Files
apt15e/src/pages/modules/building.vue
2025-09-01 14:48:58 +08:00

485 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="page_container" :class="{'enClass': $i18n.locale === 'en-us'}">
<div id="v-step-1" class="map_container">
<gl-map ref="glMap"/>
</div>
<el-row type="flex" justify="space-between">
<el-col :span="10"><button id="v-step-2" class="button_control" :disabled="disabled" @click="addPoint"><p>{{$t('MarkPoint')}}</p></button></el-col>
<el-col :span="14">
<el-row type="flex" justify="end">
<button class="button_control" @click="$router.push('/index/home')"><p>{{$t('AbandonMapbuild')}}</p></button>
<button id="v-step-3" class="button_control" style="margin-left: 10px" :disabled="disabled" @click="stopMappingConfirm"><p>{{$t('FinishMapbuild')}}</p></button>
</el-row>
</el-col>
</el-row>
<el-dialog
:title="$t('SetUpStation')"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
width="55%">
<el-form :model="dataForm" ref="dataForm" :rules="dataRule" :label-width="$i18n.locale === 'en-us' ? '' : '1.1rem'" size="mini">
<el-form-item :label="$t('StationName')" prop="stationName">
<el-input v-model="dataForm.stationName" id="stationName"></el-input>
</el-form-item>
</el-form>
<el-row type="flex" justify="space-around" style="margin-top: .3rem">
<el-col :span="7"><button class="button_control button_control_disabled" @click="dialogVisible = false"><p>{{$t('Cancel')}}</p></button></el-col>
<el-col :span="7"><button class="button_control" @click="stationConfirm"><p>{{$t('Save')}}</p></button></el-col>
</el-row>
</el-dialog>
<transition name="custom-message" >
<div v-if="message" class="message" :style="{'top': isTop ? '.87rem' : '.57rem', 'color': error ? '#F56C6C' : '#67C23A'}">
<i :class="error ? 'el-icon-error' : 'el-icon-success'"></i>
<p>{{ message }}</p>
</div>
</transition>
<div v-if="showProgress" class="progress-mask">
<!-- 进度条内容区 -->
<div class="progress-container">
<div class="progress_tip">{{warnTip ? $t('Mapdeployed') : $t('Mapgenerated')}}</div>
<el-progress
v-show="!warnTip"
:percentage="percentage"
:stroke-width="10"
style="width: 300px;"
></el-progress>
</div>
</div>
</div>
</template>
<script>
import GlMap from './gl-map-4.vue'
import { driver } from 'driver.js'
import 'driver.js/dist/driver.css'
// import { startMapping } from '../../config/mork.js'
import { startMapping, stopMapping, getMappingStatus, setStation, oneClickDeployment, abandonMapping } from '../../config/getData.js'
import { mapGetters } from 'vuex'
export default {
name: 'ModuleBuilding',
components: {
GlMap
},
beforeRouteLeave (to, from, next) {
if (this.needsConfirmation) {
this.$confirm(this.$t('Doabandonmapattempt'), this.$t('Prompt'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(async () => {
try {
// 显示加载中状态
this.loading = this.$loading({
lock: true,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.6)'
})
// 调用放弃建图接口
const res = await abandonMapping()
this.loading.close()
if (res && res.code === 200) {
// 接口成功,允许离开页面
this.needsConfirmation = false // 标记无需再确认
next()
} else {
// 接口返回失败,提示错误并阻止离开
this.$message.error(res.message)
next(false)
}
} catch (error) {
// 发生异常,提示错误并阻止离开
this.loading.close()
next(false)
}
}).catch(() => {
next(false) // 阻止离开
})
} else {
next()
}
},
data () {
const validateStationName = (rule, value, callback) => {
if (value && value.includes(',')) {
callback(new Error(this.$t('stationnotcommas')));
} else {
callback();
}
}
return {
driver: null,
driverActive: true,
needsConfirmation: true,
mapName: '',
dialogVisible: false,
dataForm: {
stationCode: '',
stationName: ''
},
dataRule: {
stationName: [
{ validator: validateStationName, trigger: 'blur' }
]
},
submitSuccess: false,
keyPoints: [],
disabled: false,
message: '', // 用于显示消息
error: false,
showProgress: false,
warnTip: false,
percentage: 0,
intervalId: null // 用于存储定时器ID
}
},
computed: {
...mapGetters(['isTop'])
},
beforeDestroy () {
document.removeEventListener('keydown', this.handleKeydown);
if (this.intervalId) {
clearTimeout(this.intervalId)
}
},
created () {
this._startMapping()
},
mounted() {
// 初始化并启动引导
this.$nextTick(() => {
this.initGuide()
})
document.addEventListener('keydown', this.handleKeydown);
},
methods: {
/* eslint-disable */
handleKeydown(event) {
// 仅在对话框可见时响应Enter键
if (this.dialogVisible && event.key === 'Enter') {
event.preventDefault(); // 阻止默认行为
this.stationConfirm();
}
},
initGuide() {
const config = {
// allowClose: false,
overlayOpacity: 0.7,
popoverClass: 'driverjs-theme',
stagePadding: 0,
nextBtnText: this.$t('next'),
prevBtnText: this.$t('previous'),
doneBtnText: this.$t('close'),
onDestroyed: () => {
this.driverActive = false
},
onPopoverRender: (popover, {config, state}) => {
if (state.activeIndex === 0) {
const popoverElement = document.querySelector('.driver-popover')
if (popoverElement) {
setTimeout(()=>{
popoverElement.style.top = '25%'
popoverElement.style.bottom = 'auto'
popoverElement.style.left = 'calc(2% + 0.2rem)'
popoverElement.style.right = 'auto'
}, 20)
}
}
}
}
const steps = [
{
element: '#v-step-1',
popover: {
description: this.$t('driverTxt1'),
side: 'left',
align: 'center'
}
},
{
element: '#v-step-2',
popover: {
description: this.$t('driverTxt2'),
side: 'top',
align: 'start'
}
},
{
element: '#v-step-3',
popover: {
description: this.$t('driverTxt3'),
side: 'top',
align: 'end'
}
}
];
this.driver = driver({
...config,
steps
})
this.driver.drive()
},
// 开始建图
async _startMapping () {
try {
this.loading = this.$loading({
lock: true,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.6)'
})
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0') // 月份从0开始补0
const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
this.mapName = `apt_map_${year}${month}${day}${hours}${minutes}${seconds}`
let res = await startMapping(this.mapName)
if (res && res.code === 200) {
this.message = this.$t('carbuildingmap')
this.error = false
this.$nextTick(() => {
this.$refs.glMap.init()
})
}
this.loading.close()
} catch (e) {
this.message = this.$t('errorbuildingredone')
this.error = true
this.loading.close()
}
},
// 打点
addPoint () {
if (this.driverActive) return
this.dialogVisible = true
this.dataForm.stationCode = 'B' + (this.keyPoints.length + 1)
const na = this.$i18n.locale === 'en-us' ? 'Work point ' : '工作点'
this.dataForm.stationName = `${na}${this.keyPoints.length + 1}`
},
// 打点->保存
stationConfirm () {
this.$refs.dataForm.validate((valid) => {
if (valid) {
this.submitSuccess = true
this._setStation()
setTimeout(() => {
this.submitSuccess = false
}, 3000)
} else {
return false
}
})
},
async _setStation () {
this.disabled = true
try {
let res = await setStation(this.dataForm.stationName, this.dataForm.stationCode)
if (res) {
if (res.code === 200) {
this.$message({
type: 'success',
message: res.message
})
this.keyPoints.push(this.dataForm.stationCode)
} else {
this.$message.error(res.message)
}
}
this.dialogVisible = false
this.disabled = false
} catch (e) {
this.$message.error(e)
this.dialogVisible = false
this.disabled = false
}
},
stopMappingConfirm () {
if (this.driverActive) return
this.$confirm(this.$t('sureendbuilding'), this.$t('Prompt'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this._stopMapping()
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('endbuildingcancel')
})
})
},
// 结束建图
async _stopMapping () {
this.disabled = true
try {
this.loading = this.$loading({
lock: true,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.6)'
})
let res = await stopMapping()
if (res && res.code === 200) {
this.message = ''
this.error = false
this.$nextTick(() => {
this.$refs.glMap.closeWebSocket()
})
this._getMappingStatus()
}
this.disabled = false
this.loading.close()
} catch (e) {
this.message = this.$t('errorendbuilding')
this.error = true
this.disabled = false
this.loading.close()
}
},
// 进度条
async _getMappingStatus () {
try {
let res = await getMappingStatus()
this.showProgress = true
this.percentage = Number(res.mapping_percent) || 0
if (res.mapping_return === '0') {
if (this.intervalId) clearTimeout(this.intervalId)
this.intervalId = setTimeout(() => this._getMappingStatus(), 1000)
}
if (res.mapping_return === '1') {
if (this.intervalId) clearTimeout(this.intervalId)
this.warnTip = true
this.percentage = 0
this._oneClickDeployment()
}
if (res.mapping_return === '2') {
if (this.intervalId) clearTimeout(this.intervalId)
this.showProgress = false
this.percentage = 0
this.keyPoints = []
this.$confirm(this.$t('Newfailedagain'), this.$t('Prompt'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this._startMapping()
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('Mapbuildingcancel')
})
})
}
} catch (e) {
this.message = ''
this.error = false
this.keyPoints = []
this.$confirm(this.$t('Newfailedagain'), this.$t('Prompt'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this._startMapping()
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('Mapbuildingcancel')
})
})
}
},
async _oneClickDeployment () {
try {
let res = await oneClickDeployment(this.mapName)
if (res && res.code === 200) {
this.$message({
type: 'success',
message: res.message
})
this.needsConfirmation = false
this.$router.push('/index/home')
} else {
this.$message.error(res.message)
}
this.showProgress = false
this.warnTip = false
this.message = ''
this.error = false
this.disabled = false
this.loading.close()
} catch (e) {
this.showProgress = false
this.warnTip = false
this.$message.error(e)
this.message = ''
this.error = false
this.disabled = false
this.loading.close()
}
}
}
}
</script>
<style lang="stylus" scoped>
.map_container
position relative
width 100%
height calc(100% - .5rem)
margin-bottom .14rem
.message
min-width 380px
border 1px solid #e1f3d8
border-radius 4px
position fixed
left 50%
transform translateX(-50%)
background-color #f0f9eb
transition opacity .3s,transform .4s
overflow hidden
padding 8px 15px
z-index 2003
display flex
align-items center
font-size .16rem
list-height 1
p
margin-left 10px
.custom-message-enter, .custom-message-leave-to
opacity 0
transform translate(-50%,-100%)
.progress-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6); /* 半透明黑色遮罩 */
z-index: 9999; /* 确保在最上层 */
display: flex;
justify-content: center;
align-items: center;
}
/* 进度条容器 */
.progress-container {
background-color: #fff;
padding: 30px 40px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.progress_tip {
font-size: .2rem
line-height: .34rem
color: #000
font-weight: 700
}
.enClass
.button_control
p
font-size .18rem
line-height .16rem
</style>