修复bug,添加功能
This commit is contained in:
@@ -158,5 +158,9 @@ module.exports = {
|
|||||||
calibrationsuccessful: 'Calibration Successful',
|
calibrationsuccessful: 'Calibration Successful',
|
||||||
calibrationfailed: 'Calibration Failed',
|
calibrationfailed: 'Calibration Failed',
|
||||||
calibrationresultabnormal: 'Calibration Result Abnormal',
|
calibrationresultabnormal: 'Calibration Result Abnormal',
|
||||||
calibrationprogramabnormal: 'Calibration Program Abnormal'
|
calibrationprogramabnormal: 'Calibration Program Abnormal',
|
||||||
|
service: 'Service',
|
||||||
|
vehicle: ' Vehicle',
|
||||||
|
scheduling: 'Scheduling',
|
||||||
|
taskscurrentlynotcarried: 'There are tasks in progress currently, so mapping cannot be carried out.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,5 +158,9 @@ module.exports = {
|
|||||||
calibrationsuccessful: '标定成功',
|
calibrationsuccessful: '标定成功',
|
||||||
calibrationfailed: '标定失败',
|
calibrationfailed: '标定失败',
|
||||||
calibrationresultabnormal: '标定结果异常',
|
calibrationresultabnormal: '标定结果异常',
|
||||||
calibrationprogramabnormal: '标定程序异常'
|
calibrationprogramabnormal: '标定程序异常',
|
||||||
|
service: '服务',
|
||||||
|
vehicle: '车辆',
|
||||||
|
scheduling: '调度',
|
||||||
|
taskscurrentlynotcarried: '当前有任务正在进行,不能建图'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import store from './vuex/store'
|
|||||||
import './style/reset.css'
|
import './style/reset.css'
|
||||||
import { Row, Col, Button, Icon, Dialog, Form, FormItem, Input, Select, Option, Table, TableColumn, Tabs, TabPane, Popover, Loading, MessageBox, Message, Progress, Upload, Menu, MenuItem } from 'element-ui'
|
import { Row, Col, Button, Icon, Dialog, Form, FormItem, Input, Select, Option, Table, TableColumn, Tabs, TabPane, Popover, Loading, MessageBox, Message, Progress, Upload, Menu, MenuItem } from 'element-ui'
|
||||||
import 'element-ui/lib/theme-chalk/index.css'
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
|
import './style/iconfont/iconfont.css'
|
||||||
import './style/common.styl'
|
import './style/common.styl'
|
||||||
import i18n from './i18n/i18n'
|
import i18n from './i18n/i18n'
|
||||||
import './config/rem.js'
|
import './config/rem.js'
|
||||||
|
|||||||
@@ -234,7 +234,8 @@ export default {
|
|||||||
this.loading.close()
|
this.loading.close()
|
||||||
this.$confirm(this.$t('Whetherdrivebackpoint'), this.$t('Prompt'), {
|
this.$confirm(this.$t('Whetherdrivebackpoint'), this.$t('Prompt'), {
|
||||||
confirmButtonText: this.$t('yes'),
|
confirmButtonText: this.$t('yes'),
|
||||||
cancelButtonText: this.$t('no')
|
cancelButtonText: this.$t('no'),
|
||||||
|
closeOnClickModal: false
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this._sendAutoBack('0')
|
this._sendAutoBack('0')
|
||||||
this.backActive = false
|
this.backActive = false
|
||||||
@@ -567,6 +568,7 @@ export default {
|
|||||||
cancelButtonText: this.$t('Cancel')
|
cancelButtonText: this.$t('Cancel')
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.tipShow = false
|
this.tipShow = false
|
||||||
|
this.recordPath = true
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['userRole']),
|
...mapGetters(['userRole', 'taskSeq']),
|
||||||
nav () {
|
nav () {
|
||||||
return [
|
return [
|
||||||
{ title: '操作', zh_title: '操作', en_title: 'Operate', router: '/index/device', icon: 'RF1', isVisible: true },
|
{ title: '操作', zh_title: '操作', en_title: 'Operate', router: '/index/device', icon: 'RF1', isVisible: true },
|
||||||
{ title: '建图', zh_title: '建图', en_title: 'Map - building', router: '/index/building', icon: 'RF2', isVisible: this.userRole === 1 },
|
{ title: '建图', zh_title: '建图', en_title: 'Map - building', router: '/index/building', icon: 'RF2', isVisible: this.userRole === 1 },
|
||||||
{ title: '地图', zh_title: '地图', en_title: 'Map', router: '/index/map', icon: 'RF3', isVisible: true },
|
{ title: '地图', zh_title: '地图', en_title: 'Map', router: '/index/map', icon: 'RF3', isVisible: true },
|
||||||
{ title: '重定位', zh_title: '重定位', en_title: 'Relocate', router: '/index/relocation', icon: 'RF4', isVisible: true },
|
{ title: '重定位', zh_title: '重定位', en_title: 'Relocate', router: '/index/relocation', icon: 'RF4', isVisible: true },
|
||||||
{ title: '一键标定', zh_title: '一键标定', en_title: 'One - Click Calibration', router: '/index/calibration', icon: 'RF7', isVisible: true }
|
{ title: '一键标定', zh_title: '一键标定', en_title: 'One - Click Calibration', router: '/index/calibration', icon: 'RF7', isVisible: this.userRole === 1 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
visibleNav () {
|
visibleNav () {
|
||||||
@@ -36,6 +36,13 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
toPage (e) {
|
toPage (e) {
|
||||||
if (e.router === '/index/building') {
|
if (e.router === '/index/building') {
|
||||||
|
if (this.taskSeq) {
|
||||||
|
this.$message({
|
||||||
|
type: 'info',
|
||||||
|
message: '当前有任务正在进行,不能建图'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$confirm(this.$t('Donewmap'), this.$t('Prompt'), {
|
this.$confirm(this.$t('Donewmap'), this.$t('Prompt'), {
|
||||||
confirmButtonText: this.$t('Confirm'),
|
confirmButtonText: this.$t('Confirm'),
|
||||||
cancelButtonText: this.$t('Cancel')
|
cancelButtonText: this.$t('Cancel')
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="20">
|
<el-col :span="20">
|
||||||
<el-row type="flex" justify="end" align="middle">
|
<el-row type="flex" justify="end" align="middle">
|
||||||
<div class="warn_image" :class="['warn_image_0', 'warn_image_1', 'warn_image_2'][topInfo.anomalyLevel]" @click="$router.push('/index/warning')"></div>
|
<div class="warn_image" :class="['warn_image_0', 'warn_image_1', 'warn_image_2'][topInfo.anomalyLevel || 0]" @click="$router.push('/index/warning')"></div>
|
||||||
<div v-if="JSON.stringify(topInfo) !== '{}'" class="state-item">{{ topInfo.isManual ? $t('ManualMode') : $t('AutomaticMode')}}</div>
|
<div v-if="JSON.stringify(topInfo) !== '{}'" class="state-item">{{ topInfo.isManual ? $t('ManualMode') : $t('AutomaticMode')}}</div>
|
||||||
<div v-if="JSON.stringify(topInfo) !== '{}'" class="state-item">{{ topInfo.state }}</div>
|
<div v-if="JSON.stringify(topInfo) !== '{}'" class="state-item">{{ topInfo.state }}</div>
|
||||||
<div v-if="JSON.stringify(topInfo) !== '{}'" class="relative elec-qty-wrap" :class="{'elec-wraning': topInfo.batteryPower <= 40}">
|
<div v-if="JSON.stringify(topInfo) !== '{}'" class="relative elec-qty-wrap" :class="{'elec-wraning': topInfo.batteryPower <= 40}">
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<div class="absolute hud_left hud_right"></div>
|
<div class="absolute hud_left hud_right"></div>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</el-row>
|
</el-row>
|
||||||
<div v-if="taskSeq.length > 0" class="task_wraper">
|
<div v-if="taskSeq.length > 0" class="task_wraper" :key="taskSeq.join(',')">
|
||||||
<div class="task_content">
|
<div class="task_content">
|
||||||
<div class="step_item" v-for="(e, i) in taskSeq" :key="i" :class="{'step_actived': i <= currentStep - 1, 'step_active': i === currentStep}">
|
<div class="step_item" v-for="(e, i) in taskSeq" :key="i" :class="{'step_actived': i <= currentStep - 1, 'step_active': i === currentStep}">
|
||||||
<div v-show="i !== 0" class="step_arrow"><img src="../../images/new/arrow.png"></div>
|
<div v-show="i !== 0" class="step_arrow"><img src="../../images/new/arrow.png"></div>
|
||||||
@@ -45,20 +45,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<login-modal v-if="loginVisible" ref="loginModal"/>
|
<login-modal v-if="loginVisible" ref="loginModal"/>
|
||||||
<config-modal v-if="configVisible" ref="configModal" @refreshWebsocket="refreshWebsocket"/>
|
<config-modal v-if="configVisible" ref="configModal" @refreshWebsocket="refreshWebsocket"/>
|
||||||
<div v-if="connectionError" class="custom-connection-error" :class="{'show': connectionError, 'hide': !connectionError}">
|
<!-- websocket连接状态 -->
|
||||||
<i class="el-icon-warning"></i>
|
<Websocket-status
|
||||||
<span>{{ connectionErrorMessage }}</span>
|
v-if="isConnecting"
|
||||||
</div>
|
ref="WebsocketStatus"
|
||||||
|
:isConnecting="isConnecting"
|
||||||
|
:wsConnected="isConnected"
|
||||||
|
:vehicleConnected="vehicleConnected"
|
||||||
|
:rcsConnected="rcsConnected"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import WebsocketStatus from './websocket-status.vue'
|
||||||
import LoginModal from './login-modal.vue'
|
import LoginModal from './login-modal.vue'
|
||||||
import ConfigModal from './config-modal.vue'
|
import ConfigModal from './config-modal.vue'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
export default {
|
export default {
|
||||||
name: 'ShellIndex',
|
name: 'ShellIndex',
|
||||||
components: {
|
components: {
|
||||||
|
WebsocketStatus,
|
||||||
LoginModal,
|
LoginModal,
|
||||||
ConfigModal
|
ConfigModal
|
||||||
},
|
},
|
||||||
@@ -66,8 +73,6 @@ export default {
|
|||||||
return {
|
return {
|
||||||
websocket: null, // WebSocket实例
|
websocket: null, // WebSocket实例
|
||||||
reconnectTimer: null, // 重连计时器
|
reconnectTimer: null, // 重连计时器
|
||||||
connectionError: false,
|
|
||||||
connectionErrorMessage: '',
|
|
||||||
topInfo: {},
|
topInfo: {},
|
||||||
// topInfo: {
|
// topInfo: {
|
||||||
// batteryPower: -1.0,
|
// batteryPower: -1.0,
|
||||||
@@ -123,20 +128,36 @@ export default {
|
|||||||
taskSeq: [],
|
taskSeq: [],
|
||||||
currentStep: null,
|
currentStep: null,
|
||||||
loginVisible: false,
|
loginVisible: false,
|
||||||
configVisible: false
|
configVisible: false,
|
||||||
|
isInitializing: false,
|
||||||
|
isConnecting: false,
|
||||||
|
isConnected: false,
|
||||||
|
resizeTimer: null,
|
||||||
|
vehicleConnected: false,
|
||||||
|
rcsConnected: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['serverUrl', 'userRole']),
|
...mapGetters(['serverUrl', 'userRole']),
|
||||||
exception () {
|
exception () {
|
||||||
let str = ''
|
if (JSON.stringify(this.topInfo) === '{}') return ''
|
||||||
if (JSON.stringify(this.topInfo) !== '{}') {
|
if (!Array.isArray(this.topInfo.errorData)) return ''
|
||||||
str = this.topInfo.errorData.map((item, index) => `${index + 1}.${item[this.$langPre.computedProp('error_name')]};`).join('')
|
return this.topInfo.errorData.map((item, index) => {
|
||||||
}
|
const name = item[this.$langPre.computedProp('error_name')]
|
||||||
return str
|
return `${index + 1}.${name || ''};`
|
||||||
|
}).join('')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
watch: {
|
||||||
|
exception(newVal) {
|
||||||
|
if (newVal !== '') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.checkTextOverflow()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
// this.$store.dispatch('setAgvObj', this.topInfo)
|
// this.$store.dispatch('setAgvObj', this.topInfo)
|
||||||
// if (this.topInfo.task_seq) {
|
// if (this.topInfo.task_seq) {
|
||||||
// this.taskSeq = this.topInfo.task_seq.split(',')
|
// this.taskSeq = this.topInfo.task_seq.split(',')
|
||||||
@@ -161,33 +182,33 @@ export default {
|
|||||||
// clearInterval(generatePointsInterval);
|
// clearInterval(generatePointsInterval);
|
||||||
// });
|
// });
|
||||||
this.initWebSocket()
|
this.initWebSocket()
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.checkTextOverflow()
|
this.checkTextOverflow()
|
||||||
window.addEventListener('resize', this.checkTextOverflow)
|
window.addEventListener('resize', this.handleResize)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('resize', this.checkTextOverflow)
|
window.removeEventListener('resize', this.handleResize)
|
||||||
this.closeWebSocket() // 组件销毁时关闭连接
|
|
||||||
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
refreshWebsocket () {
|
|
||||||
this.closeWebSocket()
|
this.closeWebSocket()
|
||||||
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
||||||
this.initWebSocket()
|
if (this.$refs.scrollText) {
|
||||||
},
|
this.$refs.scrollText.style.animation = 'none'
|
||||||
showConnectionError (message) {
|
|
||||||
// 如果消息相同且已经显示,则不重复显示
|
|
||||||
if (this.connectionErrorMessage === message && this.connectionError) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
this.connectionErrorMessage = message
|
if (this.resizeTimer) clearTimeout(this.resizeTimer)
|
||||||
this.connectionError = true
|
|
||||||
},
|
},
|
||||||
hideConnectionError () {
|
methods: {
|
||||||
this.connectionError = false
|
handleResize() {
|
||||||
this.connectionErrorMessage = ''
|
// 防抖处理,避免频繁触发
|
||||||
|
if (this.resizeTimer) clearTimeout(this.resizeTimer)
|
||||||
|
this.resizeTimer = setTimeout(() => {
|
||||||
|
this.checkTextOverflow()
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
refreshWebsocket () {
|
||||||
|
if (this.reconnectTimer) {
|
||||||
|
clearTimeout(this.reconnectTimer)
|
||||||
|
this.reconnectTimer = null
|
||||||
|
}
|
||||||
|
this.closeWebSocket()
|
||||||
|
this.initWebSocket()
|
||||||
},
|
},
|
||||||
// 滚动区域
|
// 滚动区域
|
||||||
checkTextOverflow () {
|
checkTextOverflow () {
|
||||||
@@ -221,49 +242,56 @@ export default {
|
|||||||
},
|
},
|
||||||
// 初始化WebSocket连接
|
// 初始化WebSocket连接
|
||||||
initWebSocket () {
|
initWebSocket () {
|
||||||
this.loading = this.$loading({
|
if (this.isInitializing) return
|
||||||
lock: true,
|
this.isInitializing = true
|
||||||
spinner: 'el-icon-loading',
|
this.isConnecting = true
|
||||||
background: 'rgba(0, 0, 0, 0.6)'
|
|
||||||
})
|
|
||||||
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
||||||
const wsHost = this.serverUrl.replace(/^https?:\/\//, '')
|
const wsHost = this.serverUrl.replace(/^https?:\/\//, '')
|
||||||
const lang = this.$i18n.locale.slice(0, 2)
|
const lang = this.$i18n.locale.slice(0, 2)
|
||||||
const wsUrl = `ws://${wsHost}/webSocket/VehicleInfo/${this.userRole}?lang=${lang}`
|
const wsUrl = `ws://${wsHost}/webSocket/VehicleInfo/${this.userRole}?lang=${lang}`
|
||||||
this.closeWebSocket()
|
this.closeWebSocket()
|
||||||
|
|
||||||
|
try {
|
||||||
this.websocket = new WebSocket(wsUrl)
|
this.websocket = new WebSocket(wsUrl)
|
||||||
this.websocket.onopen = () => {}
|
} catch (err) {
|
||||||
|
console.error('WebSocket 创建失败:', err)
|
||||||
|
this.isInitializing = false
|
||||||
|
this.reconnectWebSocket()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.websocket.onopen = () => {
|
||||||
|
this.isConnected = true
|
||||||
|
this.reconnectTimer && clearTimeout(this.reconnectTimer)
|
||||||
|
this.isInitializing = false
|
||||||
|
}
|
||||||
|
|
||||||
this.websocket.onmessage = (event) => {
|
this.websocket.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const res = JSON.parse(event.data)
|
const res = JSON.parse(event.data)
|
||||||
if (!res.data.rcsConnected && !res.data.vehicleConnected) {
|
if (res?.data?.vehicleConnected) this.vehicleConnected = res.data.vehicleConnected
|
||||||
this.showConnectionError(this.$t('Dispatchvehiclenotconnected'))
|
if (res?.data?.rcsConnected) this.rcsConnected = res.data.rcsConnected
|
||||||
return
|
|
||||||
}
|
if (res?.data?.vehicleConnected && res?.data?.rcsConnected) {
|
||||||
if (!res.data.rcsConnected && res.data.vehicleConnected) {
|
this.isConnecting = false
|
||||||
this.showConnectionError(this.$t('Dispatchnotconnected'))
|
this.isConnected = false
|
||||||
return
|
this.vehicleConnected = false
|
||||||
}
|
this.rcsConnected = false
|
||||||
if (!res.data.vehicleConnected && res.data.rcsConnected) {
|
|
||||||
this.showConnectionError(this.$t('Vehiclenotconnected'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (res.data.rcsConnected && res.data.vehicleConnected) {
|
|
||||||
this.loading.close()
|
|
||||||
this.hideConnectionError()
|
|
||||||
this.handleWebSocketMessage(res)
|
this.handleWebSocketMessage(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hideConnectionError()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('WebSocket消息解析失败:', error)
|
console.error('WebSocket消息解析失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.websocket.onerror = (error) => {
|
this.websocket.onerror = (error) => {
|
||||||
this.showConnectionError(this.$t('WebSocketerror') + ':', error)
|
console.error('WebSocket 创建错误:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.websocket.onclose = () => {
|
this.websocket.onclose = () => {
|
||||||
this.loading.close()
|
this.isInitializing = false
|
||||||
this.topInfo = {}
|
this.topInfo = {}
|
||||||
this.taskSeq = []
|
this.taskSeq = []
|
||||||
this.currentStep = null
|
this.currentStep = null
|
||||||
@@ -271,6 +299,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleWebSocketMessage (res) {
|
handleWebSocketMessage (res) {
|
||||||
|
try {
|
||||||
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
||||||
this.topInfo = res.data
|
this.topInfo = res.data
|
||||||
if (this.topInfo.task_seq) {
|
if (this.topInfo.task_seq) {
|
||||||
@@ -280,10 +309,16 @@ export default {
|
|||||||
this.taskSeq = []
|
this.taskSeq = []
|
||||||
this.currentStep = null
|
this.currentStep = null
|
||||||
}
|
}
|
||||||
|
if (this.$store && this.$store.dispatch) {
|
||||||
this.$store.dispatch('setAgvObj', this.topInfo)
|
this.$store.dispatch('setAgvObj', this.topInfo)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('处理消息出错', err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
closeWebSocket () {
|
closeWebSocket () {
|
||||||
if (this.websocket) {
|
if (this.websocket) {
|
||||||
|
this.websocket.onclose = null
|
||||||
this.websocket.close(1000, '正常关闭')
|
this.websocket.close(1000, '正常关闭')
|
||||||
this.websocket = null
|
this.websocket = null
|
||||||
}
|
}
|
||||||
@@ -291,9 +326,8 @@ export default {
|
|||||||
reconnectWebSocket () {
|
reconnectWebSocket () {
|
||||||
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
||||||
this.reconnectTimer = setTimeout(() => {
|
this.reconnectTimer = setTimeout(() => {
|
||||||
this.showConnectionError(this.$t('AttreconnectWebSocket'))
|
|
||||||
this.initWebSocket()
|
this.initWebSocket()
|
||||||
}, 3000) // 3秒后重连
|
}, 3000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -434,15 +468,17 @@ export default {
|
|||||||
border-color #fb8f00
|
border-color #fb8f00
|
||||||
.step_arrow
|
.step_arrow
|
||||||
img
|
img
|
||||||
|
top 2px
|
||||||
left 0
|
left 0
|
||||||
animation: moveRight 3s linear infinite;
|
animation: moveRight 2s linear infinite;
|
||||||
|
will-change: transform; /* 提示浏览器优化 */
|
||||||
@keyframes moveRight {
|
@keyframes moveRight {
|
||||||
from {
|
0% { transform: translateX(0); }
|
||||||
left: 0;
|
100% { transform: translateX(0.5rem); } /* 根据实际容器宽度调整 */
|
||||||
}
|
}
|
||||||
to {
|
@keyframes scrollText {
|
||||||
left: calc(100% + 11px);
|
0% { transform: translateX(0); }
|
||||||
}
|
100% { transform: translateX(-50%); } /* 根据实际滚动方向调整 */
|
||||||
}
|
}
|
||||||
.task_wraper-t
|
.task_wraper-t
|
||||||
_font(.2rem, .4rem, #fff, ,center)
|
_font(.2rem, .4rem, #fff, ,center)
|
||||||
|
|||||||
392
src/pages/shells/websocket-status.vue
Normal file
392
src/pages/shells/websocket-status.vue
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
<template>
|
||||||
|
<div class="show_modal">
|
||||||
|
<div class="connection-content">
|
||||||
|
<!-- 水平直线:左半部分 + 右半部分 -->
|
||||||
|
<div class="line-wrapper" :class="{ 'animated': isConnecting && !wsConnected, 'full-bright': wsConnected }"></div>
|
||||||
|
|
||||||
|
<!-- 服务器图标 -->
|
||||||
|
<div class="server-icon" :class="{ 'icon-active': wsConnected }">
|
||||||
|
<i class="iconfont icon-fuwuqi"></i>
|
||||||
|
<div class="icon-label">{{$t('service')}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 服务器到分支点的水平线 -->
|
||||||
|
<div class="line-wrapper line-wrapper2" :class="{ 'full-bright': isConnecting && wsConnected }"></div>
|
||||||
|
|
||||||
|
<!-- 分支区域:车辆(上)和调度(下) -->
|
||||||
|
<div class="branches">
|
||||||
|
<!-- 车辆分支 -->
|
||||||
|
<div class="branch-item branch-vehicle">
|
||||||
|
<div
|
||||||
|
class="branch-line-vertical"
|
||||||
|
:class="{
|
||||||
|
'animate-vertical': vehicleAnimActive && vehicleAnimPhase === 'vertical',
|
||||||
|
'full-bright': vehicleConnected || vehicleVerticalFull
|
||||||
|
}"
|
||||||
|
@animationend="handleVehicleVerticalEnd"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="branch-line-horizontal"
|
||||||
|
:class="{
|
||||||
|
'animate-horizontal': vehicleAnimActive && vehicleAnimPhase === 'horizontal',
|
||||||
|
'full-bright': vehicleConnected || vehicleHorizontalFull
|
||||||
|
}"
|
||||||
|
@animationend="handleVehicleHorizontalEnd"
|
||||||
|
></div>
|
||||||
|
<div class="branch-icon" :class="{ 'icon-active': vehicleConnected }">
|
||||||
|
<i class="iconfont icon-robot"></i>
|
||||||
|
<div class="icon-label">{{$t('vehicle')}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 调度分支 -->
|
||||||
|
<div class="branch-item branch-rcs">
|
||||||
|
<div
|
||||||
|
class="branch-line-vertical_2"
|
||||||
|
:class="{
|
||||||
|
'animate-vertical': rcsAnimActive && rcsAnimPhase === 'vertical',
|
||||||
|
'full-bright': rcsConnected || rcsVerticalFull
|
||||||
|
}"
|
||||||
|
@animationend="handleRcsVerticalEnd"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="branch-line-horizontal"
|
||||||
|
:class="{
|
||||||
|
'animate-horizontal': rcsAnimActive && rcsAnimPhase === 'horizontal',
|
||||||
|
'full-bright': rcsConnected || rcsHorizontalFull
|
||||||
|
}"
|
||||||
|
@animationend="handleRcsHorizontalEnd"
|
||||||
|
></div>
|
||||||
|
<div class="branch-icon" :class="{ 'icon-active': rcsConnected }">
|
||||||
|
<i class="iconfont icon-diaodu"></i>
|
||||||
|
<div class="icon-label">{{$t('scheduling')}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'WebSocketStatus',
|
||||||
|
props: {
|
||||||
|
isConnecting: { type: Boolean, default: false },
|
||||||
|
wsConnected: { type: Boolean, default: false },
|
||||||
|
vehicleConnected: { type: Boolean, default: false },
|
||||||
|
rcsConnected: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
vehicleAnimPhase: 'vertical', // 'vertical' 或 'horizontal'
|
||||||
|
vehicleAnimActive: false,
|
||||||
|
vehicleVerticalFull: false,
|
||||||
|
vehicleHorizontalFull: false,
|
||||||
|
rcsAnimPhase: 'vertical',
|
||||||
|
rcsAnimActive: false,
|
||||||
|
rcsVerticalFull: false,
|
||||||
|
rcsHorizontalFull: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
shouldAnimateVehicle() {
|
||||||
|
return this.isConnecting && this.wsConnected && !this.vehicleConnected
|
||||||
|
},
|
||||||
|
shouldAnimateRcs() {
|
||||||
|
return this.isConnecting && this.wsConnected && !this.rcsConnected
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
shouldAnimateVehicle: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal && !this.vehicleConnected) {
|
||||||
|
// 启动动画序列,重置所有临时高亮标志
|
||||||
|
this.vehicleVerticalFull = false
|
||||||
|
this.vehicleHorizontalFull = false
|
||||||
|
this.vehicleAnimActive = true
|
||||||
|
this.vehicleAnimPhase = 'vertical'
|
||||||
|
} else {
|
||||||
|
// 停止动画,清除临时高亮
|
||||||
|
this.vehicleAnimActive = false
|
||||||
|
this.vehicleVerticalFull = false
|
||||||
|
this.vehicleHorizontalFull = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
shouldAnimateRcs: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal && !this.rcsConnected) {
|
||||||
|
this.rcsVerticalFull = false
|
||||||
|
this.rcsHorizontalFull = false
|
||||||
|
this.rcsAnimActive = true
|
||||||
|
this.rcsAnimPhase = 'vertical'
|
||||||
|
} else {
|
||||||
|
this.rcsAnimActive = false
|
||||||
|
this.rcsVerticalFull = false
|
||||||
|
this.rcsHorizontalFull = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
vehicleConnected(val) {
|
||||||
|
if (val) {
|
||||||
|
// 连接成功时停止动画并清除临时高亮
|
||||||
|
this.vehicleAnimActive = false
|
||||||
|
this.vehicleVerticalFull = false
|
||||||
|
this.vehicleHorizontalFull = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rcsConnected(val) {
|
||||||
|
if (val) {
|
||||||
|
this.rcsAnimActive = false
|
||||||
|
this.rcsVerticalFull = false
|
||||||
|
this.rcsHorizontalFull = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleVehicleVerticalEnd() {
|
||||||
|
if (!this.vehicleAnimActive) return
|
||||||
|
// 垂直动画结束:设置垂直线高亮,切换到水平动画
|
||||||
|
this.vehicleVerticalFull = true
|
||||||
|
this.vehicleAnimPhase = 'horizontal'
|
||||||
|
},
|
||||||
|
handleVehicleHorizontalEnd() {
|
||||||
|
if (!this.vehicleAnimActive) return
|
||||||
|
// 水平动画结束:设置水平线高亮
|
||||||
|
this.vehicleHorizontalFull = true
|
||||||
|
// 延迟重置(让高亮显示短暂时间后重置)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.vehicleAnimActive) return
|
||||||
|
// 重置所有高亮,重新开始垂直动画
|
||||||
|
this.vehicleVerticalFull = false
|
||||||
|
this.vehicleHorizontalFull = false
|
||||||
|
// 强制重新触发垂直动画:先清除phase再重新设置为vertical
|
||||||
|
this.vehicleAnimPhase = ''
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.vehicleAnimActive) {
|
||||||
|
this.vehicleAnimPhase = 'vertical'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 100) // 延迟100ms,使视觉上能看到水平线完成后的全亮状态
|
||||||
|
},
|
||||||
|
handleRcsVerticalEnd() {
|
||||||
|
if (!this.rcsAnimActive) return
|
||||||
|
this.rcsVerticalFull = true
|
||||||
|
this.rcsAnimPhase = 'horizontal'
|
||||||
|
},
|
||||||
|
handleRcsHorizontalEnd() {
|
||||||
|
if (!this.rcsAnimActive) return
|
||||||
|
this.rcsHorizontalFull = true
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.rcsAnimActive) return
|
||||||
|
this.rcsVerticalFull = false
|
||||||
|
this.rcsHorizontalFull = false
|
||||||
|
this.rcsAnimPhase = ''
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.rcsAnimActive) {
|
||||||
|
this.rcsAnimPhase = 'vertical'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.show_modal {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
.connection-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直线区域 */
|
||||||
|
.line-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 1.6rem;
|
||||||
|
height: 0.04rem;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
border-radius: 0.02rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 服务器图标 */
|
||||||
|
.server-icon {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
color: #5a6e8a;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
margin: 0 .2rem;
|
||||||
|
}
|
||||||
|
.server-icon i {
|
||||||
|
font-size: .8rem;
|
||||||
|
line-height: .8rem;
|
||||||
|
padding: .3rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #c0c4cc;
|
||||||
|
}
|
||||||
|
.server-icon.icon-active i {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #00d0fc;
|
||||||
|
}
|
||||||
|
.icon-label {
|
||||||
|
position: absolute;
|
||||||
|
font-size: .26rem;
|
||||||
|
bottom: -.4rem;
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分支区域 */
|
||||||
|
.branches {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .6rem;
|
||||||
|
}
|
||||||
|
.branch-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
/* 水平线段 */
|
||||||
|
.branch-line-horizontal {
|
||||||
|
position: relative;
|
||||||
|
width: 1rem;
|
||||||
|
height: 0.04rem;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
}
|
||||||
|
/* 垂直线段 */
|
||||||
|
.branch-line-vertical {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: -.3rem;
|
||||||
|
width: 0.04rem;
|
||||||
|
height: calc(50% + .3rem);
|
||||||
|
background-color: #2c3e50;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.branch-line-vertical_2 {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: -.3rem;
|
||||||
|
width: 0.04rem;
|
||||||
|
height: calc(50% + .3rem);
|
||||||
|
background-color: #2c3e50;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 车辆分支垂直动画(单次,从下往上) */
|
||||||
|
.branch-line-vertical.animate-vertical::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(0deg, #00d0fc 0%, #00d0fc 50%, #5a6e8a 50%, #5a6e8a 100%);
|
||||||
|
background-size: 100% 200%;
|
||||||
|
animation: progressMoveVertical 2s linear forwards;
|
||||||
|
}
|
||||||
|
/* 调度分支垂直动画(单次,从上往下,根据原样式保留) */
|
||||||
|
.branch-line-vertical_2.animate-vertical::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(0deg, #5a6e8a 0%, #5a6e8a 50%, #00d0fc 50%, #00d0fc 100%);
|
||||||
|
background-size: 100% 200%;
|
||||||
|
animation: progressMoveUp 2s linear forwards;
|
||||||
|
}
|
||||||
|
/* 水平动画(单次,从左到右) */
|
||||||
|
.branch-line-horizontal.animate-horizontal::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #00d0fc 0%, #00d0fc 50%, #5a6e8a 50%, #5a6e8a 100%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: progressMove 2s linear forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 关键帧动画定义 */
|
||||||
|
@keyframes progressMoveVertical {
|
||||||
|
0% { background-position: 0 0%; }
|
||||||
|
100% { background-position: 0 100%; }
|
||||||
|
}
|
||||||
|
@keyframes progressMoveUp {
|
||||||
|
0% { background-position: 0 100%; }
|
||||||
|
100% { background-position: 0 0%; }
|
||||||
|
}
|
||||||
|
@keyframes progressMove {
|
||||||
|
0% { background-position: 100% 0; }
|
||||||
|
100% { background-position: 0% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全亮状态(覆盖动画) */
|
||||||
|
.line-wrapper.full-bright,
|
||||||
|
.line-wrapper2.full-bright,
|
||||||
|
.branch-line-vertical.full-bright,
|
||||||
|
.branch-line-vertical_2.full-bright,
|
||||||
|
.branch-line-horizontal.full-bright {
|
||||||
|
background-color: #00d0fc;
|
||||||
|
}
|
||||||
|
.line-wrapper.full-bright::before,
|
||||||
|
.line-wrapper2.full-bright::before,
|
||||||
|
.branch-line-vertical.full-bright::before,
|
||||||
|
.branch-line-vertical_2.full-bright::before,
|
||||||
|
.branch-line-horizontal.full-bright::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主水平线无限循环动画(保持原有效果) */
|
||||||
|
.line-wrapper.animated::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #00d0fc 0%, #00d0fc 50%, #5a6e8a 50%, #5a6e8a 100%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: progressMove 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 车辆/调度图标 */
|
||||||
|
.branch-icon {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
color: #5a6e8a;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
margin: 0 .2rem;
|
||||||
|
}
|
||||||
|
.branch-icon i {
|
||||||
|
font-size: .8rem;
|
||||||
|
line-height: .8rem;
|
||||||
|
padding: .3rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #c0c4cc;
|
||||||
|
}
|
||||||
|
.branch-icon.icon-active i {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #00d0fc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,4 @@
|
|||||||
@import 'mixin.styl'
|
@import 'mixin.styl'
|
||||||
@font-face {
|
|
||||||
font-family: 'iconfont';
|
|
||||||
src: url('iconfont/iconfont.woff2?t=1631676179964') format('woff2'),
|
|
||||||
url('iconfont/iconfont.woff?t=1631676179964') format('woff'),
|
|
||||||
url('iconfont/iconfont.ttf?t=1631676179964') format('truetype');
|
|
||||||
}
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "YouSheBiaoTiHei";
|
font-family: "YouSheBiaoTiHei";
|
||||||
src: url('font/YouSheBiaoTiHei.ttf') format('truetype');
|
src: url('font/YouSheBiaoTiHei.ttf') format('truetype');
|
||||||
@@ -236,6 +230,8 @@
|
|||||||
.el-message-box__message p
|
.el-message-box__message p
|
||||||
font-size .2rem
|
font-size .2rem
|
||||||
line-height: .22rem
|
line-height: .22rem
|
||||||
|
.el-message-box__btns button:nth-child(2)
|
||||||
|
margin-left 30px
|
||||||
.el-progress-bar__inner,.el-progress-bar__outer
|
.el-progress-bar__inner,.el-progress-bar__outer
|
||||||
border-radius 0
|
border-radius 0
|
||||||
.el-progress__text
|
.el-progress__text
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
.icon_dropdown
|
|
||||||
&::before
|
|
||||||
content: '\e65b'
|
|
||||||
.icon_eyeopen
|
|
||||||
&::before
|
|
||||||
content: '\ee33'
|
|
||||||
.icon_eyeclose
|
|
||||||
&::before
|
|
||||||
content: '\ee34'
|
|
||||||
.icon_admin
|
|
||||||
&::before
|
|
||||||
content: '\e7a3'
|
|
||||||
.icon_close
|
|
||||||
color: #909399
|
|
||||||
&::before
|
|
||||||
content '\e659'
|
|
||||||
.icon_radio
|
|
||||||
position relative
|
|
||||||
color #ffffff
|
|
||||||
&::before
|
|
||||||
content '\e608'
|
|
||||||
31
src/style/iconfont/iconfont.css
Normal file
31
src/style/iconfont/iconfont.css
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "iconfont"; /* Project id 5093318 */
|
||||||
|
src: url('iconfont.woff2?t=1776130387372') format('woff2'),
|
||||||
|
url('iconfont.woff?t=1776130387372') format('woff'),
|
||||||
|
url('iconfont.ttf?t=1776130387372') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-family: "iconfont" !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fuwuqi:before {
|
||||||
|
content: "\e6a4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-diaodu:before {
|
||||||
|
content: "\e6ac";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-robot:before {
|
||||||
|
content: "\e661";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiqiren:before {
|
||||||
|
content: "\e613";
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -8,7 +8,8 @@ const state = {
|
|||||||
autoLoopEnable: '0',
|
autoLoopEnable: '0',
|
||||||
autoBackEnable: '',
|
autoBackEnable: '',
|
||||||
autoBackFinish: '',
|
autoBackFinish: '',
|
||||||
ready: ''
|
ready: '',
|
||||||
|
taskSeq: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
@@ -19,7 +20,8 @@ const getters = {
|
|||||||
autoLoopEnable: state => state.autoLoopEnable,
|
autoLoopEnable: state => state.autoLoopEnable,
|
||||||
autoBackEnable: state => state.autoBackEnable,
|
autoBackEnable: state => state.autoBackEnable,
|
||||||
autoBackFinish: state => state.autoBackFinish,
|
autoBackFinish: state => state.autoBackFinish,
|
||||||
ready: state => state.ready
|
ready: state => state.ready,
|
||||||
|
taskSeq: state => state.taskSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
@@ -57,6 +59,9 @@ const mutations = {
|
|||||||
if (Object.prototype.hasOwnProperty.call(data, 'ready')) {
|
if (Object.prototype.hasOwnProperty.call(data, 'ready')) {
|
||||||
state.ready = data.ready
|
state.ready = data.ready
|
||||||
}
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, 'task_seq')) {
|
||||||
|
state.taskSeq = data.task_seq
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user