|
|
@@ -1,984 +1,2419 @@
|
|
|
-<template>
|
|
|
- <div class="main">
|
|
|
- <el-row>
|
|
|
- <el-col :xs="1" :sm="1" :md="1" :lg="1" :xl="1" style="position: relative;">
|
|
|
- <div class="main-menu">
|
|
|
- <div class="img-container" :class="{ 'active': activeIndex === 0 }" @click="openDraSetting"><img
|
|
|
- src="@/assets/icons/img/setting.png" class="notification__image" width="22px" height="22px" />
|
|
|
- <span class="notification__title">功能</span>
|
|
|
- </div>
|
|
|
- <div class="img-container" :class="{ 'active': activeIndex === 1 }" @click="openPoint"><img
|
|
|
- src="@/assets/icons/img/post.png" class="notification__image" width="22px" height="22px" />
|
|
|
- <span class="notification__title">目标点</span>
|
|
|
- </div>
|
|
|
- <div class="img-container" :class="{ 'active': activeIndex === 2 }" @click="openTask"><img
|
|
|
- src="@/assets/icons/img/task.png" class="notification__image" width="22px" height="22px" />
|
|
|
- <span class="notification__title">任务</span>
|
|
|
- </div>
|
|
|
- <div class="img-container" :class="{ 'active': activeIndex === 3 }" @click="initNavigation"><img
|
|
|
- src="@/assets/icons/img/init.png" class="notification__image" width="22px" height="22px" />
|
|
|
- <span class="notification__title">初始化</span>
|
|
|
- </div>
|
|
|
- <div class="img-container" @click="restNavigation"><img src="@/assets/icons/img/restart.png"
|
|
|
- class="notification__image" width="22px" height="22px" />
|
|
|
- <span class="notification__title">重启</span>
|
|
|
- </div>
|
|
|
- <div class="img-container" @click="offNavigation"><img src="@/assets/icons/img/off.png"
|
|
|
- class="notification__image" width="22px" height="22px" />
|
|
|
- <span class="notification__title">结束</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- 功能设置 -->
|
|
|
- <div class="drawer" v-show="settingDrawer" style="width: 200px;">
|
|
|
- <div style="position: relative;">
|
|
|
- <p>点云</p>
|
|
|
- <el-radio-group v-model="settingParams.pointCloud" size="mini">
|
|
|
- <el-radio-button :label="true">开</el-radio-button>
|
|
|
- <el-radio-button :label="false">关</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- <p>底图</p>
|
|
|
- <el-radio-group v-model="settingParams.baseMap" size="mini">
|
|
|
- <el-radio-button :label="true">开</el-radio-button>
|
|
|
- <el-radio-button :label="false">关</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- <p>点ID</p>
|
|
|
- <el-radio-group v-model="settingParams.pointId" size="mini">
|
|
|
- <el-radio-button :label="true">开</el-radio-button>
|
|
|
- <el-radio-button :label="false">关</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- <p>位置跟随</p>
|
|
|
- <el-radio-group v-model="settingParams.follow" size="mini">
|
|
|
- <el-radio-button :label="true">开</el-radio-button>
|
|
|
- <el-radio-button :label="false">关</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- <p>网络邻居</p>
|
|
|
- <el-radio-group v-model="settingParams.network" size="mini">
|
|
|
- <el-radio-button :label="true">开</el-radio-button>
|
|
|
- <el-radio-button :label="false">关</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- </div>
|
|
|
- <div class="drawer-close" @click="closeDra('hand')"><i class="el-icon-close"></i></div>
|
|
|
- </div>
|
|
|
- <!-- 目标点 -->
|
|
|
- <div class="drawer" v-show="pointDrawer" style="width: 450px;">
|
|
|
- <div style="position: relative;">
|
|
|
- <div style="width: 100%;margin-bottom: 10px;">
|
|
|
- <!-- <el-tooltip class="item" effect="dark" content="将点位显示在地图上" placement="top">
|
|
|
- <el-button type="primary" icon="el-icon-search" size="mini" :disabled="multiple"></el-button>
|
|
|
- </el-tooltip> -->
|
|
|
- <el-tooltip class="item" effect="dark" content="上移" placement="top">
|
|
|
- <el-button type="primary" icon="el-icon-top" size="mini" :disabled="single" @click="moveUp"></el-button>
|
|
|
- </el-tooltip>
|
|
|
- <el-tooltip class="item" effect="dark" content="下移" placement="top">
|
|
|
- <el-button type="primary" icon="el-icon-bottom" size="mini" :disabled="single"
|
|
|
- @click="moveDown"></el-button>
|
|
|
- </el-tooltip>
|
|
|
- <el-popconfirm title="批量删除选中点位数据?" @confirm="removePoint">
|
|
|
- <el-button type="danger" icon="el-icon-delete" size="mini" :disabled="multiple" slot="reference"
|
|
|
- style="margin-left: 10px;"></el-button>
|
|
|
- </el-popconfirm>
|
|
|
- </div>
|
|
|
- <el-table ref="multipleTable" :data="pointList" tooltip-effect="dark" style="width: 100%" max-height="200px"
|
|
|
- @selection-change="handleSelectionChange" border>
|
|
|
- <el-table-column type="selection" width="45" align="center">
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="编号" width="60" show-overflow-tooltip align="center">
|
|
|
- <template slot-scope="scope">{{ scope.row.id }}</template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="x坐标" width="90" show-overflow-tooltip align="center">
|
|
|
- <template slot-scope="scope">{{ scope.row.x }}</template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="y坐标" width="90" show-overflow-tooltip align="center">
|
|
|
- <template slot-scope="scope">{{ scope.row.y }}</template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" show-overflow-tooltip align="center">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-button size="mini" type="text" icon="el-icon-edit" @click="editPoint(scope.row)"
|
|
|
- style="margin-right: 10px;">编辑</el-button>
|
|
|
- <el-popconfirm title="删除当前点位数据?" @confirm="removePoint(scope.row)">
|
|
|
- <el-button size="mini" type="text" icon="el-icon-edit" slot="reference">删除</el-button>
|
|
|
- </el-popconfirm>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- <div style="width: 100%;margin-top: 10px;">
|
|
|
- <el-button type="warning" icon="el-icon-caret-right" size="mini" :disabled="single">立即前往</el-button>
|
|
|
- <el-button type="success" icon="el-icon-folder-add" size="mini" :disabled="multiple"
|
|
|
- @click="taskGenerateDiaShow = true">生成任务</el-button>
|
|
|
- <el-button type="success" icon="el-icon-thumb" size="mini" @click="mapSelectEle('open')"
|
|
|
- v-if="!pointSelectionEnabled">地图选点模式</el-button>
|
|
|
- <el-button type="danger" icon="el-icon-thumb" size="mini" @click="mapSelectEle('close')"
|
|
|
- v-else>关闭选点模式</el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="drawer-close" @click="closeDra('hand')"><i class="el-icon-close"></i></div>
|
|
|
- </div>
|
|
|
- <!-- 任务 -->
|
|
|
- <div class="drawer" v-if="taskDrawer" style="width: 300px;">
|
|
|
- <div style="position: relative;">
|
|
|
- <el-empty description="暂无创建的任务!" v-if="taskDataList.length < 1"></el-empty>
|
|
|
- <el-collapse v-else>
|
|
|
- <el-collapse-item v-for="(item, index) in taskDataList" :name="index" :key="item.taskId">
|
|
|
- <template slot="title"><span>任务名:{{ item.taskName }}</span>
|
|
|
- <el-tag type="info" v-if="item.status == 1" size="mini" class="task-status-tag">空闲中</el-tag><el-tag
|
|
|
- type="success" v-else size="mini" class="task-status-tag">执行中</el-tag>
|
|
|
- </template>
|
|
|
- <div class="collapse-content-div">
|
|
|
- <el-button type="danger" size="mini" icon="el-icon-delete"
|
|
|
- @click="removeTaskItem(item)">删除</el-button>
|
|
|
- <el-button type="warning" size="mini" icon="el-icon-view">查看</el-button>
|
|
|
- <el-button type="success" size="mini" icon="el-icon-caret-right" :disabled="item.status == 0"
|
|
|
- @click="executeTask(item)">执行</el-button>
|
|
|
- </div>
|
|
|
- </el-collapse-item>
|
|
|
- </el-collapse>
|
|
|
- </div>
|
|
|
- <div class="drawer-close" @click="closeDra('hand')" style="right: 1px;top: 1px;"><i class="el-icon-close"></i>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- <el-col :xs="23" :sm="23" :md="23" :lg="23" :xl="23">
|
|
|
- <div class="main-content">
|
|
|
- <!-- 当前操作类型标记 -->
|
|
|
- <div class="hand-ment-mark">
|
|
|
- <el-tag type="danger" effect="dark" size="mini" v-if="nowHandMenu">{{ nowHandMenu }}</el-tag>
|
|
|
- </div>
|
|
|
- <OlMap ref="olmap" :width="olWidth + 'px'" :height="olHeight + 'px'" backgroundColor="#F5F5F5" :mapName="mapName"
|
|
|
- :pointSwitch="settingParams.pointId" :baseLayerShow="settingParams.baseMap" :robotPoseData="laserPositionData"
|
|
|
- :pointSelectionEnabled="pointSelectionEnabled" :poseInitEnable="poseInitEnable" @addNowPoint="addNowPoint"
|
|
|
- :isRobotFollow="settingParams.follow" @initNavigationResult="initNavigationResult"></OlMap>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-dialog title="目标点编辑" :visible.sync="pointEditDiaShow" width="360px">
|
|
|
- <el-row>
|
|
|
- <span class="point-edit-span">x坐标(m)</span><el-input v-model="pointEditData.x" placeholder="请输入x坐标" size="mini"
|
|
|
- class="point-edit-input"></el-input>
|
|
|
- </el-row>
|
|
|
- <span class="point-edit-span">y坐标(m)</span><el-input v-model="pointEditData.y" placeholder="请输入y坐标"
|
|
|
- size="mini"></el-input>
|
|
|
- <span class="point-edit-span">规划类型</span><el-select v-model="pointEditData.type" placeholder="请选择" size="mini">
|
|
|
- <el-option v-for="item in planOptions" :key="item.value" :label="item.label" :value="item.value">
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- <span class="point-edit-span">添加动作</span>
|
|
|
- <div v-for="(item, index) in pointEditData.actionMenuList" style="margin-bottom: 8px;" class="action-menu">
|
|
|
- <el-select v-model="item.value" placeholder="请选择" size="mini" @change="changeAction(index)">
|
|
|
- <el-option v-for="item in actionOptions" :key="item.value" :label="item.label" :value="item.value">
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- <el-tooltip class="item" effect="dark" content="输入等待时间(秒)" placement="top">
|
|
|
- <el-input v-model="item.other" placeholder="时间(秒)" size="mini" style="width: 60px;margin-left: 5px;"
|
|
|
- class="action-menu_input" v-if="'other' in item"></el-input>
|
|
|
- </el-tooltip>
|
|
|
- <el-button type="info" icon="el-icon-plus" circle size="mini" style="margin-left: 5px;padding: 5px;"
|
|
|
- @click="appendActionMenu"></el-button>
|
|
|
- <el-button type="info" icon="el-icon-minus" circle size="mini" style="margin-left: 5px;padding: 5px;"
|
|
|
- @click="removeActionMenu(index)" v-if="index > 0"></el-button>
|
|
|
- </div>
|
|
|
- <span slot="footer" class="dialog-footer">
|
|
|
- <el-button @click="pointEditDiaShow = false">取 消</el-button>
|
|
|
- <el-button type="primary" @click="submitEditPoint">确 定</el-button>
|
|
|
- </span>
|
|
|
- </el-dialog>
|
|
|
- <el-dialog title="创建任务" :visible.sync="taskGenerateDiaShow" width="400px">
|
|
|
- <el-row>
|
|
|
- <span class="point-edit-span">任务名称</span><el-input v-model="generateTaskParam.taskName" placeholder="请输入任务名"
|
|
|
- size="mini" class="point-edit-input" style="width: 50%;"></el-input>
|
|
|
- <span class="point-edit-span">执行次数</span><el-input-number v-model="generateTaskParam.count"
|
|
|
- controls-position="right" :min="1" :max="100" size="mini" style="width: 50%;"></el-input-number>
|
|
|
- <span class="point-edit-span">开始时间</span><el-time-picker v-model="generateTaskParam.time" :picker-options="{
|
|
|
- selectableRange: '00:00:00 - 23:59:59'
|
|
|
- }" placeholder="选择时间" size="mini">
|
|
|
- </el-time-picker>
|
|
|
- <span class="point-edit-span">执行日期</span>
|
|
|
- <el-checkbox-group v-model="generateTaskParam.date" style="margin-bottom: 10px;">
|
|
|
- <el-checkbox label="1">周一</el-checkbox>
|
|
|
- <el-checkbox label="2">周二</el-checkbox>
|
|
|
- <el-checkbox label="3">周三</el-checkbox>
|
|
|
- <el-checkbox label="4">周四</el-checkbox>
|
|
|
- <el-checkbox label="5">周五</el-checkbox>
|
|
|
- <el-checkbox label="6">周六</el-checkbox>
|
|
|
- <el-checkbox label="7">周日</el-checkbox>
|
|
|
- </el-checkbox-group>
|
|
|
- </el-row>
|
|
|
- <span slot="footer" class="dialog-footer">
|
|
|
- <el-button @click="closeTaskGenerate()">取 消</el-button>
|
|
|
- <el-button type="primary" @click="submitTaskGenerate">确 定</el-button>
|
|
|
- </span>
|
|
|
- </el-dialog>
|
|
|
- <div class="info-dra-class-all">
|
|
|
- <el-drawer title="实时信息" :visible.sync="infoDrawer" :direction="'rtl'" :modal="false" :size="'100%'"
|
|
|
- :wrapperClosable="false" custom-class="navigation-info-dra-class" :modal-append-to-body="false">
|
|
|
- <div class="info-dra-class_content">
|
|
|
- <div style="margin-bottom: 20px;">
|
|
|
- <div class="info-dra_title">
|
|
|
- <div
|
|
|
- style="height: 20px;width: 5px;background-color: #6565FC;margin-right: 5px;border-radius: 4px 4px 0 0;">
|
|
|
- </div><span>当前地图</span>
|
|
|
- </div>
|
|
|
- <span class="info-dra_content">{{mapName}}</span>
|
|
|
- </div>
|
|
|
- <div style="margin-bottom: 20px;">
|
|
|
- <div class="info-dra_title">
|
|
|
- <div
|
|
|
- style="height: 20px;width: 5px;background-color: #6565FC;margin-right: 5px;border-radius: 4px 4px 0 0;">
|
|
|
- </div><span>当前任务</span>
|
|
|
- </div>
|
|
|
- <span class="info-dra_content">测试任务</span>
|
|
|
- </div>
|
|
|
- <div style="margin-bottom: 20px;">
|
|
|
- <div class="info-dra_title">
|
|
|
- <div
|
|
|
- style="height: 20px;width: 5px;background-color: #6565FC;margin-right: 5px;border-radius: 4px 4px 0 0;">
|
|
|
- </div><span>实时信息</span>
|
|
|
- </div>
|
|
|
- <span class="info-dra_content info-dra_content_title">速度: <span
|
|
|
- class="info-dra_content_other">0.35m/s</span></span>
|
|
|
- <span class="info-dra_content info-dra_content_title">速度指令: <span
|
|
|
- class="info-dra_content_other">0.22m/s</span></span>
|
|
|
- <span class="info-dra_content info-dra_content_title">坐标: <span class="info-dra_content_other">(1.813,
|
|
|
- -63.931,
|
|
|
- 0.000)</span></span>
|
|
|
- <span class="info-dra_content info-dra_content_title">航向: <span
|
|
|
- class="info-dra_content_other">-79.6°</span></span>
|
|
|
- <span class="info-dra_content info-dra_content_title">累计里程: <span
|
|
|
- class="info-dra_content_other">5965352.00m</span></span>
|
|
|
- <span class="info-dra_content info-dra_content_title">配准误差: <span
|
|
|
- class="info-dra_content_other">10.000</span></span>
|
|
|
- <span class="info-dra_content info-dra_content_title">电量: <span
|
|
|
- class="info-dra_content_other">67%</span></span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-drawer>
|
|
|
- </div>
|
|
|
- <!-- 展开抽屉 -->
|
|
|
- <div class="fixed-right-center" @click="showInfoDra" v-if="!infoDrawer">
|
|
|
- <i class="el-icon-arrow-left"></i>
|
|
|
- </div>
|
|
|
- <MqttComp ref="mqtt" :topics="topics" @message-received="onMessage" />
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import OlMap from "@/components/OlMap";
|
|
|
-import MqttComp from "@/components/Mqtt/mqttComp.vue";
|
|
|
-export default {
|
|
|
- name: "Navigation",
|
|
|
- components: {
|
|
|
- OlMap,
|
|
|
- MqttComp
|
|
|
- },
|
|
|
- data() {
|
|
|
- return {
|
|
|
- topics:[
|
|
|
- { topic: this.$mqttPrefix+'/localization/action/init/reply', qos: 2, retain: false },
|
|
|
- { topic:this.$mqttPrefix + '/localization/pose'}
|
|
|
- ],
|
|
|
- activeIndex: -1, // 默认为没有激活的项
|
|
|
- settingDrawer: false,
|
|
|
- pointDrawer: false,
|
|
|
- taskDrawer: false,
|
|
|
- infoDrawer: true,
|
|
|
- pointEditDiaShow: false,
|
|
|
- taskGenerateDiaShow: false,
|
|
|
- // 是否开启地图选点
|
|
|
- pointSelectionEnabled: false,
|
|
|
- // 是否开启位置初始化功能
|
|
|
- poseInitEnable: false,
|
|
|
- // 功能设置参数
|
|
|
- settingParams: {
|
|
|
- pointCloud: false,
|
|
|
- baseMap: true,
|
|
|
- pointId: false,
|
|
|
- follow: false,
|
|
|
- network: false
|
|
|
- },
|
|
|
- // 非单个禁用
|
|
|
- single: true,
|
|
|
- // 非多个禁用
|
|
|
- multiple: true,
|
|
|
- taskDataList: [],
|
|
|
- // 目标点的编辑数据
|
|
|
- pointEditData: {
|
|
|
- id: '',
|
|
|
- x: '',
|
|
|
- y: '',
|
|
|
- type: '',
|
|
|
- // 添加动作菜单列表 (other:追加参数,例如原定等待选项的等待时间)
|
|
|
- actionMenuList: []
|
|
|
- },
|
|
|
- // 生成任务的参数
|
|
|
- generateTaskParam: {
|
|
|
- taskId: '',
|
|
|
- taskName: '',
|
|
|
- count: 1,
|
|
|
- time: '',
|
|
|
- date: [] // 1-7分别指代周一到周末
|
|
|
- },
|
|
|
- // 点位列表
|
|
|
- pointList: [],
|
|
|
- pointIds: [],
|
|
|
- // 路径类型
|
|
|
- planOptions: [
|
|
|
- { label: '自由路径', value: 0 },
|
|
|
- { label: '路网路径', value: 1 }
|
|
|
- ],
|
|
|
- // 动作类型
|
|
|
- actionOptions: [
|
|
|
- { label: '原地等待', value: 0 },
|
|
|
- { label: '开始录制', value: 1 },
|
|
|
- { label: '结束录制', value: 2 },
|
|
|
- { label: '添加建图轨迹', value: 3 },
|
|
|
- { label: '挂钩挂载', value: 4 },
|
|
|
- { label: '挂钩卸载', value: 5 }
|
|
|
- ],
|
|
|
- olWidth: 0, // 用于存储宽度的变量
|
|
|
- olHeight: 0,
|
|
|
- nowHandMenu: '',
|
|
|
- mapName: this.$route.params.mapName || '', // 地图名称
|
|
|
- // 激光定位数据
|
|
|
- laserPositionData: {
|
|
|
- x: 0,
|
|
|
- y: 0,
|
|
|
- angle: 0
|
|
|
- },
|
|
|
- };
|
|
|
- },
|
|
|
- created() {
|
|
|
-
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- // const mapId = this.$route.params.mapId;
|
|
|
- this.updateOlCss();
|
|
|
- window.addEventListener('resize', this.updateOlCss);
|
|
|
- },
|
|
|
- beforeDestroy() {
|
|
|
- window.removeEventListener('resize', this.updateOlCss);
|
|
|
- },
|
|
|
- methods: {
|
|
|
- onMessage({ topic, message }) {
|
|
|
- // console.log("收到消息:", topic, message);
|
|
|
- if (topic === this.$mqttPrefix + '/localization/action/init/reply') {
|
|
|
- this.handleInitReply(message);
|
|
|
- } else if (topic === this.$mqttPrefix + '/localization/pose') {
|
|
|
- this.handleLaserPose(message);
|
|
|
- }
|
|
|
- },
|
|
|
- handleInitReply(message) {
|
|
|
- // 处理初始化回复消息
|
|
|
- console.log("初始化回复:", message);
|
|
|
-
|
|
|
- },
|
|
|
- handleLaserPose(message) {
|
|
|
- try {
|
|
|
- const data = message.args[0];
|
|
|
- const {xyz, rpy, blh, heading} = data.pose;
|
|
|
- // 激光定位实时数据
|
|
|
- this.laserPositionData.x = xyz[0];
|
|
|
- this.laserPositionData.y = xyz[1];
|
|
|
- this.laserPositionData.angle = rpy[2];
|
|
|
- // GNSS定位实时数据
|
|
|
- /* this.gnssPositionData.longitude = blh[1]; // 经度
|
|
|
- this.gnssPositionData.latitude = blh[0]; // 纬度
|
|
|
- this.gnssPositionData.angle = heading; // 航向角
|
|
|
-
|
|
|
- this.gnssPositionData.status = data.rtk.star+ '/' + data.rtk.status; // RTK状态 */
|
|
|
-
|
|
|
- } catch (e) {
|
|
|
- console.error("解析失败:", e);
|
|
|
- }
|
|
|
- },
|
|
|
- publishMsg() {
|
|
|
-
|
|
|
- },
|
|
|
- updateOlCss() {
|
|
|
- const element = this.$el.querySelector('.main-content');
|
|
|
- this.olWidth = element.offsetWidth;
|
|
|
- this.olHeight = element.offsetHeight;
|
|
|
- },
|
|
|
- openDraSetting() {
|
|
|
- this.poseInitEnable = false;
|
|
|
- this.activeIndex = this.activeIndex == 0 ? -1 : 0;
|
|
|
- if (this.settingDrawer) {
|
|
|
- this.nowHandMenu = ''
|
|
|
- this.settingDrawer = false;
|
|
|
- return;
|
|
|
- }
|
|
|
- this.closeDra()
|
|
|
- this.nowHandMenu = '功能菜单操作'
|
|
|
- this.settingDrawer = true;
|
|
|
- },
|
|
|
- openPoint() {
|
|
|
- this.poseInitEnable = false
|
|
|
- this.activeIndex = this.activeIndex == 1 ? -1 : 1;
|
|
|
- if (this.pointDrawer) {
|
|
|
- this.pointDrawer = false;
|
|
|
- this.nowHandMenu = ''
|
|
|
- return;
|
|
|
- }
|
|
|
- this.closeDra()
|
|
|
- this.nowHandMenu = '目标点操作'
|
|
|
- this.pointDrawer = true;
|
|
|
- },
|
|
|
- openTask() {
|
|
|
- this.poseInitEnable = false
|
|
|
- this.activeIndex = this.activeIndex == 2 ? -1 : 2;
|
|
|
- if (this.taskDrawer) {
|
|
|
- this.taskDrawer = false;
|
|
|
- this.nowHandMenu = ''
|
|
|
- return;
|
|
|
- }
|
|
|
- this.closeDra()
|
|
|
- this.nowHandMenu = '任务操作'
|
|
|
- this.taskDrawer = true;
|
|
|
- },
|
|
|
- /**
|
|
|
- * 关闭左侧菜单的抽屉
|
|
|
- * type 类型(hand手动,auto自动)
|
|
|
- */
|
|
|
- closeDra(type) {
|
|
|
- this.settingDrawer = false;
|
|
|
- this.pointDrawer = false;
|
|
|
- this.taskDrawer = false;
|
|
|
- if (type == 'hand') {
|
|
|
- this.activeIndex = -1;
|
|
|
- }
|
|
|
- this.nowHandMenu = ''
|
|
|
- },
|
|
|
- handleSelectionChange(selection) {
|
|
|
- this.pointIds = selection.map(item => item.id)
|
|
|
- this.single = selection.length !== 1
|
|
|
- this.multiple = !selection.length
|
|
|
- },
|
|
|
- // 点位编辑
|
|
|
- editPoint(row) {
|
|
|
- this.pointEditDiaShow = true;
|
|
|
- this.pointEditData.id = row.id;
|
|
|
- this.pointEditData.x = row.x;
|
|
|
- this.pointEditData.y = row.y;
|
|
|
- this.pointEditData.type = row.type;
|
|
|
- this.pointEditData.actionMenuList = row.action
|
|
|
- },
|
|
|
- // 点位删除
|
|
|
- removePoint(row) {
|
|
|
- const idArr = [];
|
|
|
- const ids = row?.id || this.pointIds;
|
|
|
- if (ids) {
|
|
|
- idArr.push(...(Array.isArray(ids) ? ids : [ids]));
|
|
|
- idArr.forEach(id => {
|
|
|
- let indexToRemove = this.pointList.findIndex(item => item?.id === id);
|
|
|
- if (indexToRemove !== -1) {
|
|
|
- this.pointList.splice(indexToRemove, 1);
|
|
|
- if (this.pointList.length < 1) {
|
|
|
- // 重置地图的id计数器
|
|
|
- this.$refs.olmap.restIdNum();
|
|
|
- }
|
|
|
- this.$refs.olmap.removeIconHtmlById("pose-" + id);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- // 点位上移
|
|
|
- moveUp() {
|
|
|
- const index = this.pointList.findIndex(point => point.id == this.pointIds[0]);
|
|
|
- if (index !== -1 && index > 0) {
|
|
|
- let temp = this.pointList[index];
|
|
|
- this.$set(this.pointList, index, this.pointList[index - 1]);
|
|
|
- this.$set(this.pointList, index - 1, temp);
|
|
|
- }
|
|
|
- },
|
|
|
- // 点位下移
|
|
|
- moveDown() {
|
|
|
- const index = this.pointList.findIndex(point => point.id == this.pointIds[0]);
|
|
|
- if (index !== -1 && index < this.pointList.length - 1) {
|
|
|
- let temp = this.pointList[index];
|
|
|
- this.$set(this.pointList, index, this.pointList[index + 1]);
|
|
|
- this.$set(this.pointList, index + 1, temp);
|
|
|
- }
|
|
|
- },
|
|
|
- // 追加动作按钮
|
|
|
- appendActionMenu() {
|
|
|
- this.pointEditData.actionMenuList.push({ value: 0, other: 0 })
|
|
|
- },
|
|
|
- // 删除追加动作按钮
|
|
|
- removeActionMenu(index) {
|
|
|
- this.pointEditData.actionMenuList.splice(index, 1);
|
|
|
- },
|
|
|
- // 修改动作下拉值
|
|
|
- changeAction(index) {
|
|
|
- // 判断当前修改后是否是原定等待,如果不是则删除other属性
|
|
|
- let item = this.pointEditData.actionMenuList[index];
|
|
|
- if (item && 'other' in item) {
|
|
|
- delete item.other; // 删除 'other' 属性
|
|
|
- } else {
|
|
|
- item.other = 0;
|
|
|
- }
|
|
|
- },
|
|
|
- clearActionDia() {
|
|
|
- this.pointEditData.id = '';
|
|
|
- this.pointEditData.x = '';
|
|
|
- this.pointEditData.y = '';
|
|
|
- this.pointEditData.type = '';
|
|
|
- this.pointEditData.actionMenuList = [];
|
|
|
- },
|
|
|
- // 提交点位修改
|
|
|
- submitEditPoint() {
|
|
|
- this.pointEditDiaShow = false;
|
|
|
- // 模拟数据修改
|
|
|
- const point = this.pointList.find(item => item.id === this.pointEditData.id);
|
|
|
- if (this.pointEditData.actionMenuList)
|
|
|
- if (point) {
|
|
|
- // 找到对应元素,更新数据(真实情况下发请求修改,然后重新查询点位列表)
|
|
|
- point.x = this.pointEditData.x;
|
|
|
- point.y = this.pointEditData.y;
|
|
|
- point.type = this.pointEditData.type;
|
|
|
- point.action = this.pointEditData.actionMenuList;
|
|
|
- this.$modal.msgSuccess("当前点位数据已修改");
|
|
|
- }
|
|
|
- },
|
|
|
- // 生成任务
|
|
|
- submitTaskGenerate() {
|
|
|
- // 查询当前选择的id对应的点位对象数据
|
|
|
- const orderedPoints = this.pointIds.map(id => this.pointList.find(point => point.id === id));
|
|
|
- if (!this.generateTaskParam.taskName || !this.generateTaskParam.count || !this.generateTaskParam.time || this.generateTaskParam.date.length < 1) {
|
|
|
- this.$message({
|
|
|
- message: '请完善任务数据!',
|
|
|
- type: 'warning'
|
|
|
- });
|
|
|
- return;
|
|
|
- }
|
|
|
- this.generateTaskParam.taskId = Math.floor(Math.random() * 10001);
|
|
|
- let taskData = {
|
|
|
- taskId: this.generateTaskParam.taskId,
|
|
|
- taskName: this.generateTaskParam.taskName,
|
|
|
- count: this.generateTaskParam.count,
|
|
|
- time: this.generateTaskParam.time,
|
|
|
- date: this.generateTaskParam.date,
|
|
|
- status: 1,
|
|
|
- points: orderedPoints
|
|
|
- }
|
|
|
- // 实际使用需要提交任务后等待后端添加完毕然后查询列表,这里临时用push
|
|
|
- this.taskDataList.push(taskData)
|
|
|
- this.restGenerateParam();
|
|
|
- this.taskGenerateDiaShow = false;
|
|
|
- this.pointIds = [];
|
|
|
- this.$modal.msgSuccess("点位任务创建成功");
|
|
|
- },
|
|
|
- // 初始化导航
|
|
|
- initNavigation() {
|
|
|
- this.closeDra()
|
|
|
- if (this.poseInitEnable) {
|
|
|
- this.poseInitEnable = false;
|
|
|
- this.nowHandMenu = ''
|
|
|
- } else {
|
|
|
- this.poseInitEnable = true;
|
|
|
- this.nowHandMenu = '初始化导航'
|
|
|
- }
|
|
|
- this.pointSelectionEnabled = false;
|
|
|
- this.activeIndex = this.activeIndex == 3 ? -1 : 3;
|
|
|
- },
|
|
|
- // 重启导航
|
|
|
- restNavigation() {
|
|
|
- this.$confirm('将重启当前导航, 是否继续?', '提示', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- customClass: 'el-message-box-cust',
|
|
|
- type: 'warning'
|
|
|
- }).then(() => {
|
|
|
- this.$message({
|
|
|
- type: 'success',
|
|
|
- message: '导航已重启!'
|
|
|
- });
|
|
|
- }).catch(() => { });
|
|
|
- },
|
|
|
- // 关闭导航
|
|
|
- offNavigation() {
|
|
|
- this.$confirm('将关闭当前导航, 是否继续?', '提示', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- customClass: 'el-message-box-cust',
|
|
|
- type: 'warning'
|
|
|
- }).then(() => {
|
|
|
- this.$message({
|
|
|
- type: 'success',
|
|
|
- message: '导航已关闭!'
|
|
|
- });
|
|
|
- }).catch(() => { });
|
|
|
- },
|
|
|
- // 关闭任务生成弹窗
|
|
|
- closeTaskGenerate() {
|
|
|
- this.taskGenerateDiaShow = false;
|
|
|
- this.restGenerateParam();
|
|
|
- },
|
|
|
- // 重置任务创建弹窗数据
|
|
|
- restGenerateParam() {
|
|
|
- this.generateTaskParam = {
|
|
|
- taskId: '',
|
|
|
- taskName: '',
|
|
|
- number: 1,
|
|
|
- time: '',
|
|
|
- date: [] // 1-7分别指代周一到周末
|
|
|
- }
|
|
|
- },
|
|
|
- // 任务删除
|
|
|
- removeTaskItem(data) {
|
|
|
- this.$confirm('删除名为' + data.taskName + '的任务', '删除', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- customClass: 'el-message-box-cust',
|
|
|
- type: 'warning'
|
|
|
- }).then(() => {
|
|
|
- this.$message({
|
|
|
- type: 'success',
|
|
|
- message: '已删除!'
|
|
|
- });
|
|
|
- this.taskDataList = this.taskDataList.filter(task => task.taskId !== data.taskId);
|
|
|
- }).catch(() => { });
|
|
|
- },
|
|
|
- // 执行任务
|
|
|
- executeTask(data) {
|
|
|
- this.$confirm('开始执行任务' + data.taskName + '?', '执行', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- customClass: 'el-message-box-cust',
|
|
|
- type: 'warning'
|
|
|
- }).then(() => {
|
|
|
- this.$message({
|
|
|
- type: 'success',
|
|
|
- message: '任务已开始执行!'
|
|
|
- });
|
|
|
- this.taskDataList.forEach(task => {
|
|
|
- if (task.taskId === data.taskId) {
|
|
|
- task.status = 0;
|
|
|
- }
|
|
|
- });
|
|
|
- }).catch(() => { });
|
|
|
- },
|
|
|
- // 展开右侧实时信息
|
|
|
- showInfoDra() {
|
|
|
- this.infoDrawer = true;
|
|
|
- },
|
|
|
- test() {
|
|
|
- console.log(this.set);
|
|
|
- },
|
|
|
- // 开启地图选点
|
|
|
- mapSelectEle(type) {
|
|
|
- if (type == 'open') {
|
|
|
- this.pointSelectionEnabled = true;
|
|
|
- this.poseInitEnable = false;
|
|
|
- this.$notify({
|
|
|
- title: '地图选择',
|
|
|
- message: '已开启选择模式',
|
|
|
- type: 'success',
|
|
|
- duration: 1000
|
|
|
- });
|
|
|
- } else {
|
|
|
- this.pointSelectionEnabled = false;
|
|
|
- this.$notify.info({
|
|
|
- title: '关闭选择',
|
|
|
- message: '已关闭选择模式',
|
|
|
- duration: 1000
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- // 将当前选择的点位数据添加到点位列表中
|
|
|
- // currentCoordinate 坐标信息 currentPlace 画布位置信息
|
|
|
- addNowPoint(currentCoordinate, currentPlace) {
|
|
|
- let coordData = {
|
|
|
- id: currentCoordinate[0],
|
|
|
- x: currentCoordinate[1].toFixed(3),
|
|
|
- y: currentCoordinate[2].toFixed(3),
|
|
|
- placeX: currentPlace[0].toFixed(3),
|
|
|
- placeY: currentPlace[1].toFixed(3),
|
|
|
- type: 0,
|
|
|
- action: [{ value: 0, other: 0 }]
|
|
|
- }
|
|
|
- this.pointList.push(coordData);
|
|
|
- },
|
|
|
- /**
|
|
|
- * 位姿初始化操作绘制的回执
|
|
|
- * @param position 坐标
|
|
|
- * @param angle 角度
|
|
|
- */
|
|
|
- initNavigationResult(position, angle, nid) {
|
|
|
- const prefix = process.env.VUE_APP_PNS_MQTT_PROXY;
|
|
|
- let num = nid.split("_")[1];// 获取点位id编号
|
|
|
- this.$refs.mqtt.publish(prefix + "/localization/action/init", {
|
|
|
- "timestamp" : 123456,
|
|
|
- "args" : [
|
|
|
- {"nid" : Number(num)}
|
|
|
- ]
|
|
|
- },2,false
|
|
|
- );
|
|
|
- console.log(position);
|
|
|
- console.log(angle);
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.point-edit-span {
|
|
|
- display: block;
|
|
|
- margin: 10px 0;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-.drawer {
|
|
|
- height: 100%;
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 100%;
|
|
|
- /* box-shadow: 4px 4px 12px rgba(201, 201, 201, 0.2); */
|
|
|
- /* 右边和下边的阴影 */
|
|
|
- border-radius: 0 0 12px 0;
|
|
|
- border-left: 1px solid #F0F0F0;
|
|
|
- padding: 8px 15px;
|
|
|
- border-right: 1px solid #ececec;
|
|
|
- border-bottom: 1px solid #ececec;
|
|
|
- overflow-y: auto;
|
|
|
- background-color: #fff;
|
|
|
- z-index: 1000;
|
|
|
-}
|
|
|
-
|
|
|
-.drawer-close {
|
|
|
- position: absolute;
|
|
|
- right: 3px;
|
|
|
- top: 3px;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-
|
|
|
-.drawer-title {
|
|
|
- position: absolute;
|
|
|
- top: -23px;
|
|
|
- left: -8px;
|
|
|
- font-size: 13px;
|
|
|
- font-weight: bold;
|
|
|
- color: #838383;
|
|
|
-}
|
|
|
-
|
|
|
-.drawer p {
|
|
|
- font-size: 13px;
|
|
|
- border-left: 5px #D1D1D1 solid;
|
|
|
- padding-left: 5px;
|
|
|
- margin: 8px 0;
|
|
|
- border-radius: 3px 0 0 3px;
|
|
|
-}
|
|
|
-
|
|
|
-.img-container {
|
|
|
- text-align: center;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- /* 垂直居中子元素 */
|
|
|
- border-radius: 7px;
|
|
|
- background: linear-gradient(135deg, #00bcd4, #009688);
|
|
|
- cursor: pointer;
|
|
|
- width: 70%;
|
|
|
- aspect-ratio: 1;
|
|
|
- /* 设置宽高比为1,即高度和宽度相等 */
|
|
|
- margin-top: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.img-container:hover {
|
|
|
- transform: scale(1.02);
|
|
|
- /* 鼠标悬停时放大 */
|
|
|
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
|
|
- /* 增加阴影效果 */
|
|
|
- background-color: #00796b;
|
|
|
- /* 改变背景颜色 */
|
|
|
-}
|
|
|
-
|
|
|
-.img-container:active {
|
|
|
- transform: scale(0.98);
|
|
|
- /* 点击时缩小 */
|
|
|
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
|
- /* 点击时加深阴影 */
|
|
|
- background-color: #004d40;
|
|
|
- /* 点击时改变背景颜色 */
|
|
|
- filter: brightness(1.1);
|
|
|
- /* 点击时稍微增加亮度 */
|
|
|
-}
|
|
|
-
|
|
|
-.img-container img {
|
|
|
- opacity: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.img-container span {
|
|
|
- font-size: 12px;
|
|
|
- color: #ffffff;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-/* 激活时的样式 */
|
|
|
-.img-container.active {
|
|
|
- background: linear-gradient(135deg, #007d8d, #004b43);
|
|
|
-}
|
|
|
-
|
|
|
-.explore-unit {
|
|
|
- margin-left: 8px;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .el-dialog__body {
|
|
|
- padding: 20px 20px 0 20px;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .download-map .el-dialog__body {
|
|
|
- padding: 10px 20px 0 20px !important;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .el-table--medium .el-table__cell {
|
|
|
- padding: 6px 0;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .action-menu .action-menu_input .el-input__inner {
|
|
|
- padding: 0 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.task-status-tag {
|
|
|
- margin-left: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .drawer .el-collapse-item__header {
|
|
|
- height: 38px;
|
|
|
- line-height: 38px;
|
|
|
- color: #767676;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .drawer .el-collapse-item__content {
|
|
|
- text-align: left;
|
|
|
- padding-bottom: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .drawer .el-collapse {
|
|
|
- border: 1px solid #EBEEF5;
|
|
|
- padding: 0 8px;
|
|
|
- border-radius: 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.collapse-content-div {
|
|
|
- margin-top: 0;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .collapse-content-div .el-button--mini {
|
|
|
- padding: 4px 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.fixed-right-center {
|
|
|
- position: fixed;
|
|
|
- top: 50%;
|
|
|
- right: 0;
|
|
|
- transform: translateY(-50%);
|
|
|
- background-color: rgba(0, 0, 255, 0.6);
|
|
|
- color: white;
|
|
|
- border-radius: 6px 0 0 6px;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-
|
|
|
-.info-dra-class_content {
|
|
|
- padding: 0 20px 20px 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.info-dra_title {
|
|
|
- display: flex;
|
|
|
- /* 启用 Flexbox 布局 */
|
|
|
- justify-content: flex-start;
|
|
|
- /* 水平从左排列 */
|
|
|
- align-items: center;
|
|
|
- color: #8a8a8a;
|
|
|
- font-size: 15px;
|
|
|
- font-weight: bold;
|
|
|
- border-bottom: 2px solid #B9B9FF;
|
|
|
- margin-bottom: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.info-dra_content {
|
|
|
- color: #5A5A5A;
|
|
|
- font-size: 14px;
|
|
|
- margin-left: 8px;
|
|
|
- display: block;
|
|
|
- margin-bottom: 8px;
|
|
|
-}
|
|
|
-
|
|
|
-.info-dra_content_title {
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-.info-dra_content_other {
|
|
|
- font-weight: 400;
|
|
|
-}
|
|
|
-
|
|
|
-.hand-ment-mark {
|
|
|
- position: absolute;
|
|
|
- bottom: 12px;
|
|
|
- left: 12px;
|
|
|
- z-index: 1000;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .info-dra-class-all .el-drawer__wrapper {
|
|
|
- width: 15%;
|
|
|
- left: 85%;
|
|
|
-}
|
|
|
-
|
|
|
-@media (max-width: 1599px) {
|
|
|
- ::v-deep .info-dra-class-all .el-drawer__wrapper {
|
|
|
- width: 18%;
|
|
|
- left: 82%;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@media (max-width: 1269px) {
|
|
|
- ::v-deep .info-dra-class-all .el-drawer__wrapper {
|
|
|
- width: 21%;
|
|
|
- left: 79%;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@media (max-width: 1000px) {
|
|
|
- ::v-deep .info-dra-class-all .el-drawer__wrapper {
|
|
|
- width: 24%;
|
|
|
- left: 76%;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.notification__title {
|
|
|
- font-size: 1.2rem;
|
|
|
- /* 默认字体大小 */
|
|
|
-}
|
|
|
-
|
|
|
-.main-content {
|
|
|
- width: 100%;
|
|
|
- min-height: calc(100vh - 84px);
|
|
|
- overflow: hidden;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.main {
|
|
|
- width: 100%;
|
|
|
- min-height: calc(100vh - 84px);
|
|
|
- min-width: 1500px;
|
|
|
- overflow-x: auto;
|
|
|
-}
|
|
|
-
|
|
|
-.main-menu {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
-}
|
|
|
-</style>
|
|
|
-
|
|
|
-<style>
|
|
|
-.navigation-info-dra-class {
|
|
|
- background-color: #ffffff;
|
|
|
- box-shadow: none;
|
|
|
- border-radius: 5px 0 0 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.navigation-info-dra-class .el-drawer__header {
|
|
|
- margin-bottom: 20px;
|
|
|
-}
|
|
|
-</style>
|
|
|
+<template>
|
|
|
+ <div class="navigation-container">
|
|
|
+ <!-- 地图舞台容器 -->
|
|
|
+ <div class="map-stage" ref="mapStage">
|
|
|
+ <!-- 当前操作类型标记 -->
|
|
|
+ <div class="hand-ment-mark">
|
|
|
+ <el-tag type="danger" effect="dark" size="mini" v-if="nowHandMenu">{{ nowHandMenu }}</el-tag>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 地图组件 - 完全保持原样 -->
|
|
|
+ <OlMap ref="olmap" :width="olWidth + 'px'" :height="olHeight + 'px'" backgroundColor="#F5F5F5" :mapName="mapName"
|
|
|
+ :pointSwitch="settingParams.pointId" :baseLayerShow="settingParams.baseMap" :robotPoseData="laserPositionData"
|
|
|
+ :pointSelectionEnabled="selectPointMode" :poseInitEnable="initPoseMode" @addNowPoint="addNowPoint"
|
|
|
+ :isRobotFollow="settingParams.follow" @initNavigationResult="initNavigationResult"
|
|
|
+ :showDefaultControls="false"></OlMap>
|
|
|
+
|
|
|
+ <!-- 左侧工具条浮层 -->
|
|
|
+ <MapToolbar
|
|
|
+ class="nav-toolbar"
|
|
|
+ preset="nav"
|
|
|
+ :selectedKey="selectedKey"
|
|
|
+ :hasRobotPosition="true"
|
|
|
+ :isConnected="true"
|
|
|
+ :isBusy="false"
|
|
|
+ :isFullscreen="isFullscreen"
|
|
|
+ @zoom-in="onZoomIn"
|
|
|
+ @zoom-out="onZoomOut"
|
|
|
+ @center-robot="onCenterRobot"
|
|
|
+ @toggle-fullscreen="onToggleFullscreen"
|
|
|
+ @confirm-init="handleConfirmInit"
|
|
|
+ @confirm-reboot="handleConfirmReboot"
|
|
|
+ @confirm-stop="handleConfirmStop"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 右侧信息面板浮层 -->
|
|
|
+ <RightPanel
|
|
|
+ mode="nav"
|
|
|
+ panelType="nav"
|
|
|
+ :overlay="true"
|
|
|
+ :visible.sync="rightVisible"
|
|
|
+ :realtime-info="realtimeInfo"
|
|
|
+ :waypoint-list="waypoints"
|
|
|
+ :task-list="tasks"
|
|
|
+ :setting-params="settingParams"
|
|
|
+ @wp-select="onWpSelect"
|
|
|
+ @wp-send="onWpSend"
|
|
|
+ @wp-create="onWpCreate"
|
|
|
+ @wp-edit="onWpEdit"
|
|
|
+ @wp-remove="onWpRemove"
|
|
|
+ @wp-move-up="onWpMoveUp"
|
|
|
+ @wp-move-down="onWpMoveDown"
|
|
|
+ @wp-batch-remove="onWpBatchRemove"
|
|
|
+ @wp-goto="onWpGoto"
|
|
|
+ @wp-goto-single="onWpGotoSingle"
|
|
|
+ @wp-create-task="onWpCreateTask"
|
|
|
+ @wp-selection-change="onWpSelectionChange"
|
|
|
+ @map-select-mode-change="onMapSelectModeChange"
|
|
|
+ @task-view="onTaskView"
|
|
|
+ @task-start="onTaskStart"
|
|
|
+ @task-pause="onTaskPause"
|
|
|
+ @task-stop="onTaskStop"
|
|
|
+ @task-remove="onTaskRemove"
|
|
|
+ @setting-change="onSettingChange"
|
|
|
+ />
|
|
|
+
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 目标点编辑对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ title="目标点编辑"
|
|
|
+ :visible.sync="pointEditDiaShow"
|
|
|
+ width="480px"
|
|
|
+ @close="clearActionDia"
|
|
|
+ class="waypoint-edit-dialog"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ center
|
|
|
+ >
|
|
|
+ <div class="dialog-content">
|
|
|
+ <el-form :model="pointEditData" label-width="90px" size="small" class="waypoint-form">
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-location-outline"></i>
|
|
|
+ 位置信息
|
|
|
+ </h4>
|
|
|
+ <!-- 分两行布局,给输入框更多空间 -->
|
|
|
+ <el-form-item label="X坐标(m)">
|
|
|
+ <el-input v-model="pointEditData.x" placeholder="请输入X坐标值" class="coordinate-input">
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="Y坐标(m)">
|
|
|
+ <el-input v-model="pointEditData.y" placeholder="请输入Y坐标值" class="coordinate-input">
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-guide"></i>
|
|
|
+ 路径配置
|
|
|
+ </h4>
|
|
|
+ <el-form-item label="规划类型">
|
|
|
+ <el-select v-model="pointEditData.type" placeholder="请选择路径类型" style="width: 100%">
|
|
|
+ <el-option v-for="item in planOptions" :key="item.value" :label="item.label" :value="item.value">
|
|
|
+ <span style="float: left">{{ item.label }}</span>
|
|
|
+ <span style="float: right; color: #8492a6; font-size: 13px">{{ item.value === 0 ? '自由规划' : '路网约束' }}</span>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-setting"></i>
|
|
|
+ 动作配置
|
|
|
+ <el-button type="text" size="mini" @click="appendActionMenu" class="add-action-btn">
|
|
|
+ <i class="el-icon-plus"></i><span>添加动作</span>
|
|
|
+ </el-button>
|
|
|
+ </h4>
|
|
|
+ <div class="action-list">
|
|
|
+ <div v-for="(item, index) in pointEditData.actionMenuList" :key="index" class="action-item">
|
|
|
+ <div class="action-header">
|
|
|
+ <span class="action-index">{{ index + 1 }}</span>
|
|
|
+ <el-select v-model="item.value" placeholder="请选择动作" size="small" @change="changeAction(index)" style="flex: 1;">
|
|
|
+ <el-option v-for="option in actionOptions" :key="option.value" :label="option.label" :value="option.value"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <el-button v-if="index > 0" type="text" size="mini" @click="removeActionMenu(index)" class="remove-btn">
|
|
|
+ <i class="el-icon-close"></i>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div v-if="'other' in item" class="action-params">
|
|
|
+ <el-input v-model="item.other" placeholder="等待时间" size="small" style="width: 120px;">
|
|
|
+ <template slot="append">秒</template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="pointEditDiaShow = false" size="medium">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="submitEditPoint" size="medium">
|
|
|
+ <i class="el-icon-check"></i> 保存修改
|
|
|
+ </el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+ <!-- 创建任务对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ title="创建任务"
|
|
|
+ :visible.sync="taskGenerateDiaShow"
|
|
|
+ width="480px"
|
|
|
+ @close="closeTaskGenerate"
|
|
|
+ class="task-create-dialog"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ center
|
|
|
+ >
|
|
|
+ <div class="dialog-content">
|
|
|
+ <el-form :model="generateTaskParam" label-width="90px" size="small" class="task-form">
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-s-order"></i>
|
|
|
+ 任务信息
|
|
|
+ </h4>
|
|
|
+ <el-form-item label="任务名称">
|
|
|
+ <el-input v-model="generateTaskParam.taskName" placeholder="请输入任务名称" class="task-input">
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="执行次数">
|
|
|
+ <el-input-number
|
|
|
+ v-model="generateTaskParam.count"
|
|
|
+ controls-position="right"
|
|
|
+ :min="1"
|
|
|
+ :max="100"
|
|
|
+ class="task-input-number"
|
|
|
+ style="width: 100%"
|
|
|
+ ></el-input-number>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
+ 执行计划
|
|
|
+ </h4>
|
|
|
+ <el-form-item label="开始时间">
|
|
|
+ <el-time-picker
|
|
|
+ v-model="generateTaskParam.time"
|
|
|
+ :picker-options="{
|
|
|
+ selectableRange: '00:00:00 - 23:59:59'
|
|
|
+ }"
|
|
|
+ placeholder="选择时间"
|
|
|
+ class="task-time-picker"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ </el-time-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="执行日期">
|
|
|
+ <el-checkbox-group v-model="generateTaskParam.date" class="task-date-group">
|
|
|
+ <el-checkbox label="1">周一</el-checkbox>
|
|
|
+ <el-checkbox label="2">周二</el-checkbox>
|
|
|
+ <el-checkbox label="3">周三</el-checkbox>
|
|
|
+ <el-checkbox label="4">周四</el-checkbox>
|
|
|
+ <el-checkbox label="5">周五</el-checkbox>
|
|
|
+ <el-checkbox label="6">周六</el-checkbox>
|
|
|
+ <el-checkbox label="7">周日</el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="closeTaskGenerate()" size="medium">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="submitTaskGenerate" size="medium">
|
|
|
+ <i class="el-icon-check"></i> 创建任务
|
|
|
+ </el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 任务查看对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ title="任务详情"
|
|
|
+ :visible.sync="taskViewDiaShow"
|
|
|
+ width="480px"
|
|
|
+ class="task-view-dialog"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ center
|
|
|
+ >
|
|
|
+ <div class="dialog-content">
|
|
|
+ <el-form :model="taskViewData" label-width="90px" size="small" class="task-view-form">
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-s-order"></i>
|
|
|
+ 任务信息
|
|
|
+ </h4>
|
|
|
+ <el-form-item label="任务名称">
|
|
|
+ <span class="form-text">{{ taskViewData.taskName || '--' }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="执行次数">
|
|
|
+ <span class="form-text">{{ taskViewData.count || '--' }}次</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="任务状态">
|
|
|
+ <span class="form-text status-text" :class="getTaskStatusClass(taskViewData.status)">
|
|
|
+ {{ getTaskStatusText(taskViewData.status) }}
|
|
|
+ </span>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="form-section">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
+ 执行计划
|
|
|
+ </h4>
|
|
|
+ <el-form-item label="开始时间">
|
|
|
+ <span class="form-text">{{ formatTime(taskViewData.time) }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="执行日期">
|
|
|
+ <span class="form-text">{{ formatDate(taskViewData.date) }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="taskViewDiaShow = false" size="medium">关 闭</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import OlMap from "@/components/OlMap";
|
|
|
+import { MapToolbar, RightPanel } from "./components/shared";
|
|
|
+import { FullscreenOperations, RobotPositionUtils } from "@/utils/map-operations";
|
|
|
+
|
|
|
+import MqttComp from "@/components/Mqtt/mqttComp.vue";
|
|
|
+export default {
|
|
|
+ name: "NavigationPage",
|
|
|
+ components: {
|
|
|
+ OlMap,
|
|
|
+ MapToolbar,
|
|
|
+ RightPanel,
|
|
|
+ MqttComp
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ topics:[
|
|
|
+ { topic: this.$mqttPrefix+'/localization/action/init/reply', qos: 2, retain: false },
|
|
|
+ { topic:this.$mqttPrefix + '/localization/pose'}
|
|
|
+ ],
|
|
|
+ nowHandMenu: '',
|
|
|
+ mapName: this.$route.params.mapName || '', // 地图名称
|
|
|
+ // 激光定位数据
|
|
|
+ laserPositionData: {
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ angle: 0
|
|
|
+ },
|
|
|
+ activeIndex: -1, // 默认为没有激活的项
|
|
|
+ settingDrawer: false,
|
|
|
+ pointDrawer: false,
|
|
|
+ taskDrawer: false,
|
|
|
+ pointEditDiaShow: false,
|
|
|
+ taskGenerateDiaShow: false,
|
|
|
+ taskViewDiaShow: false,
|
|
|
+ // 是否开启地图选点
|
|
|
+ pointSelectionEnabled: false,
|
|
|
+ // 是否开启位置初始化功能
|
|
|
+ poseInitEnable: false,
|
|
|
+ // 功能设置参数
|
|
|
+ settingParams: {
|
|
|
+ pointCloud: false,
|
|
|
+ baseMap: true,
|
|
|
+ pointId: false,
|
|
|
+ follow: false,
|
|
|
+ network: false
|
|
|
+ },
|
|
|
+ // 非单个禁用
|
|
|
+ single: true,
|
|
|
+ // 非多个禁用
|
|
|
+ multiple: true,
|
|
|
+ taskDataList: [],
|
|
|
+ // 目标点的编辑数据
|
|
|
+ pointEditData: {
|
|
|
+ id: '',
|
|
|
+ x: '',
|
|
|
+ y: '',
|
|
|
+ type: '',
|
|
|
+ // 添加动作菜单列表 (other:追加参数,例如原定等待选项的等待时间)
|
|
|
+ actionMenuList: []
|
|
|
+ },
|
|
|
+ // 生成任务的参数
|
|
|
+ generateTaskParam: {
|
|
|
+ taskId: '',
|
|
|
+ taskName: '',
|
|
|
+ count: 1,
|
|
|
+ time: '',
|
|
|
+ date: [] // 1-7分别指代周一到周末
|
|
|
+ },
|
|
|
+
|
|
|
+ // 任务查看数据
|
|
|
+ taskViewData: {},
|
|
|
+ // 点位列表
|
|
|
+ pointList: [],
|
|
|
+ pointIds: [],
|
|
|
+ // 路径类型
|
|
|
+ planOptions: [
|
|
|
+ { label: '自由路径', value: 0 },
|
|
|
+ { label: '路网路径', value: 1 }
|
|
|
+ ],
|
|
|
+ // 动作类型
|
|
|
+ actionOptions: [
|
|
|
+ { label: '原地等待', value: 0 },
|
|
|
+ { label: '开始录制', value: 1 },
|
|
|
+ { label: '结束录制', value: 2 },
|
|
|
+ { label: '添加建图轨迹', value: 3 },
|
|
|
+ { label: '挂钩挂载', value: 4 },
|
|
|
+ { label: '挂钩卸载', value: 5 }
|
|
|
+ ],
|
|
|
+ olWidth: 0, // 用于存储宽度的变量
|
|
|
+ olHeight: 0,
|
|
|
+ nowHandMenu: '',
|
|
|
+
|
|
|
+ // 新UI相关状态
|
|
|
+ panelVisible: true, // 右侧面板是否可见(旧的,保留兼容)
|
|
|
+ rightVisible: true, // 导航页右侧面板是否可见
|
|
|
+ activeTab: 'info', // 当前激活的tab
|
|
|
+ lastTab: 'info', // 上次激活的tab
|
|
|
+ selectPointMode: false, // 选点模式
|
|
|
+ initPoseMode: false, // 初始化位姿模式
|
|
|
+ // Mock任务数据
|
|
|
+ mockTasks: [
|
|
|
+ { id: 1, name: '巡检任务A', nodes: 5, status: 'idle' },
|
|
|
+ { id: 2, name: '运输任务B', nodes: 3, status: 'running' },
|
|
|
+ { id: 3, name: '清扫任务C', nodes: 8, status: 'paused' }
|
|
|
+ ],
|
|
|
+ // 实时信息数据
|
|
|
+ realtimeInfo: {
|
|
|
+ currentMap: 'shanghai',
|
|
|
+ currentTask: '测试任务',
|
|
|
+ speed: '0.35m/s',
|
|
|
+ speedCommand: '0.22m/s',
|
|
|
+ coordinates: '(1.813, -63.931, 0.000)',
|
|
|
+ heading: '-79.6°',
|
|
|
+ totalDistance: '5965352.00m',
|
|
|
+ registrationError: '10.000',
|
|
|
+ batteryLevel: '67%'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 机器人位姿数据(用于OlMap组件)
|
|
|
+ robotPoseData: {
|
|
|
+ x: 1.813,
|
|
|
+ y: -63.931,
|
|
|
+ angle: 0.000
|
|
|
+ },
|
|
|
+ // 连接和状态
|
|
|
+ isConnected: true,
|
|
|
+ isBusy: false,
|
|
|
+ isFullscreen: false,
|
|
|
+
|
|
|
+ // 全屏监听清理函数
|
|
|
+ fullscreenCleanup: null,
|
|
|
+
|
|
|
+ // 目标点相关状态
|
|
|
+ selectedWaypointIds: [], // 选中的目标点ID列表
|
|
|
+ waypointSingle: true, // 是否单个选中
|
|
|
+ waypointMultiple: true, // 是否多个选中
|
|
|
+ pointEditDiaShow: false, // 目标点编辑对话框显示状态
|
|
|
+
|
|
|
+ // 目标点的编辑数据
|
|
|
+ pointEditData: {
|
|
|
+ id: '',
|
|
|
+ x: '',
|
|
|
+ y: '',
|
|
|
+ type: '',
|
|
|
+ // 添加动作菜单列表 (other:追加参数,例如原定等待选项的等待时间)
|
|
|
+ actionMenuList: []
|
|
|
+ },
|
|
|
+
|
|
|
+ // 路径类型选项
|
|
|
+ planOptions: [
|
|
|
+ { label: '自由路径', value: 0 },
|
|
|
+ { label: '路网路径', value: 1 }
|
|
|
+ ],
|
|
|
+
|
|
|
+ // 动作类型选项
|
|
|
+ actionOptions: [
|
|
|
+ { label: '原地等待', value: 0 },
|
|
|
+ { label: '开始录制', value: 1 },
|
|
|
+ { label: '结束录制', value: 2 },
|
|
|
+ { label: '添加建图轨迹', value: 3 },
|
|
|
+ { label: '挂钩挂载', value: 4 },
|
|
|
+ { label: '挂钩卸载', value: 5 }
|
|
|
+ ],
|
|
|
+
|
|
|
+ // === 导航页专用数据 ===
|
|
|
+ waypoints: [],
|
|
|
+ tasks: [
|
|
|
+ {
|
|
|
+ taskId: 1,
|
|
|
+ taskName: '巡检任务Alpha',
|
|
|
+ status: 'idle',
|
|
|
+ count: 3,
|
|
|
+ time: new Date('2024-01-01 09:00:00'),
|
|
|
+ date: ['1', '3', '5'],
|
|
|
+ points: [
|
|
|
+ { id: 101, name: '目标点1', x: '1.234', y: '2.345' },
|
|
|
+ { id: 102, name: '目标点2', x: '3.456', y: '4.567' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ taskId: 2,
|
|
|
+ taskName: '运输任务Beta',
|
|
|
+ status: 'running',
|
|
|
+ count: 1,
|
|
|
+ time: new Date('2024-01-01 14:30:00'),
|
|
|
+ date: ['2', '4'],
|
|
|
+ points: [
|
|
|
+ { id: 103, name: '目标点3', x: '5.678', y: '6.789' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ taskId: 3,
|
|
|
+ taskName: '清扫任务Gamma',
|
|
|
+ status: 'paused',
|
|
|
+ count: 5,
|
|
|
+ time: new Date('2024-01-01 16:00:00'),
|
|
|
+ date: ['1', '2', '3', '4', '5'],
|
|
|
+ points: [
|
|
|
+ { id: 104, name: '目标点4', x: '7.890', y: '8.901' },
|
|
|
+ { id: 105, name: '目标点5', x: '9.012', y: '0.123' },
|
|
|
+ { id: 106, name: '目标点6', x: '1.345', y: '2.456' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ // 当前选中的工具key
|
|
|
+ selectedKey() {
|
|
|
+ if (this.initPoseMode) return 'init-pose';
|
|
|
+ return '';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 机器人位置
|
|
|
+ robotPosition() {
|
|
|
+ // 直接使用robotPoseData,与标定页面保持一致
|
|
|
+ if (this.robotPoseData && (this.robotPoseData.x !== 0 || this.robotPoseData.y !== 0)) {
|
|
|
+ return [this.robotPoseData.x, this.robotPoseData.y];
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 是否有有效的机器人位置
|
|
|
+ hasValidRobotPosition() {
|
|
|
+ return !!this.robotPosition;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 地图是否就绪
|
|
|
+ isMapReady() {
|
|
|
+ return !!this.getMapInstance();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ // const mapId = this.$route.params.mapId;
|
|
|
+ this.updateOlCss();
|
|
|
+ window.addEventListener('resize', this.updateOlCss);
|
|
|
+
|
|
|
+ // 地图初始化(无需额外操作,直接使用getMapInstance方法获取地图实例)
|
|
|
+
|
|
|
+ // 设置全屏状态监听
|
|
|
+ this.fullscreenCleanup = FullscreenOperations.addFullscreenListener((isFullscreen) => {
|
|
|
+ this.isFullscreen = isFullscreen;
|
|
|
+ // 全屏状态改变后,重新计算地图尺寸
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.updateOlCss();
|
|
|
+ // 触发地图重新计算尺寸
|
|
|
+ const map = this.getMapInstance();
|
|
|
+ if (map) {
|
|
|
+ map.updateSize();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ window.removeEventListener('resize', this.updateOlCss);
|
|
|
+
|
|
|
+ // 清理全屏监听
|
|
|
+ if (this.fullscreenCleanup) {
|
|
|
+ this.fullscreenCleanup();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ onMessage({ topic, message }) {
|
|
|
+ // console.log("收到消息:", topic, message);
|
|
|
+ if (topic === this.$mqttPrefix + '/localization/action/init/reply') {
|
|
|
+ this.handleInitReply(message);
|
|
|
+ } else if (topic === this.$mqttPrefix + '/localization/pose') {
|
|
|
+ this.handleLaserPose(message);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleInitReply(message) {
|
|
|
+ // 处理初始化回复消息
|
|
|
+ console.log("初始化回复:", message);
|
|
|
+
|
|
|
+ },
|
|
|
+ handleLaserPose(message) {
|
|
|
+ try {
|
|
|
+ const data = message.args[0];
|
|
|
+ const {xyz, rpy, blh, heading} = data.pose;
|
|
|
+ // 激光定位实时数据
|
|
|
+ this.laserPositionData.x = xyz[0];
|
|
|
+ this.laserPositionData.y = xyz[1];
|
|
|
+ this.laserPositionData.angle = rpy[2];
|
|
|
+ // GNSS定位实时数据
|
|
|
+ /* this.gnssPositionData.longitude = blh[1]; // 经度
|
|
|
+ this.gnssPositionData.latitude = blh[0]; // 纬度
|
|
|
+ this.gnssPositionData.angle = heading; // 航向角
|
|
|
+
|
|
|
+ this.gnssPositionData.status = data.rtk.star+ '/' + data.rtk.status; // RTK状态 */
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ console.error("解析失败:", e);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ publishMsg() {
|
|
|
+
|
|
|
+ },
|
|
|
+ updateOlCss() {
|
|
|
+ const element = this.$el.querySelector('.map-stage');
|
|
|
+ this.olWidth = element.offsetWidth;
|
|
|
+ this.olHeight = element.offsetHeight;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 地图API适配器
|
|
|
+ getMapInstance() {
|
|
|
+ return this.$refs.olmap && this.$refs.olmap.map ? this.$refs.olmap.map : null;
|
|
|
+ },
|
|
|
+
|
|
|
+ openDraSetting() {
|
|
|
+ this.poseInitEnable = false;
|
|
|
+ this.activeIndex = this.activeIndex == 0 ? -1 : 0;
|
|
|
+ if (this.settingDrawer) {
|
|
|
+ this.nowHandMenu = ''
|
|
|
+ this.settingDrawer = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.closeDra()
|
|
|
+ this.nowHandMenu = '功能菜单操作'
|
|
|
+ this.settingDrawer = true;
|
|
|
+ },
|
|
|
+ openPoint() {
|
|
|
+ this.poseInitEnable = false
|
|
|
+ this.activeIndex = this.activeIndex == 1 ? -1 : 1;
|
|
|
+ if (this.pointDrawer) {
|
|
|
+ this.pointDrawer = false;
|
|
|
+ this.nowHandMenu = ''
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.closeDra()
|
|
|
+ this.nowHandMenu = '目标点操作'
|
|
|
+ this.pointDrawer = true;
|
|
|
+ },
|
|
|
+ openTask() {
|
|
|
+ this.poseInitEnable = false
|
|
|
+ this.activeIndex = this.activeIndex == 2 ? -1 : 2;
|
|
|
+ if (this.taskDrawer) {
|
|
|
+ this.taskDrawer = false;
|
|
|
+ this.nowHandMenu = ''
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.closeDra()
|
|
|
+ this.nowHandMenu = '任务操作'
|
|
|
+ this.taskDrawer = true;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 关闭左侧菜单的抽屉
|
|
|
+ * type 类型(hand手动,auto自动)
|
|
|
+ */
|
|
|
+ closeDra(type) {
|
|
|
+ this.settingDrawer = false;
|
|
|
+ this.pointDrawer = false;
|
|
|
+ this.taskDrawer = false;
|
|
|
+ if (type == 'hand') {
|
|
|
+ this.activeIndex = -1;
|
|
|
+ }
|
|
|
+ this.nowHandMenu = ''
|
|
|
+ },
|
|
|
+ handleSelectionChange(selection) {
|
|
|
+ this.pointIds = selection.map(item => item.id)
|
|
|
+ this.single = selection.length !== 1
|
|
|
+ this.multiple = !selection.length
|
|
|
+ },
|
|
|
+ // 点位编辑
|
|
|
+ editPoint(row) {
|
|
|
+ this.pointEditDiaShow = true;
|
|
|
+ this.pointEditData.id = row.id;
|
|
|
+ this.pointEditData.x = row.x;
|
|
|
+ this.pointEditData.y = row.y;
|
|
|
+ this.pointEditData.type = row.type;
|
|
|
+ this.pointEditData.actionMenuList = row.action
|
|
|
+ },
|
|
|
+ // 点位删除
|
|
|
+ removePoint(row) {
|
|
|
+ const idArr = [];
|
|
|
+ const ids = row?.id || this.pointIds;
|
|
|
+ if (ids) {
|
|
|
+ idArr.push(...(Array.isArray(ids) ? ids : [ids]));
|
|
|
+ idArr.forEach(id => {
|
|
|
+ let indexToRemove = this.pointList.findIndex(item => item?.id === id);
|
|
|
+ if (indexToRemove !== -1) {
|
|
|
+ this.pointList.splice(indexToRemove, 1);
|
|
|
+ if (this.pointList.length < 1) {
|
|
|
+ // 重置地图的id计数器
|
|
|
+ this.$refs.olmap.restIdNum();
|
|
|
+ }
|
|
|
+ this.$refs.olmap.removeIconHtmlById("pose-" + id);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 点位上移
|
|
|
+ moveUp() {
|
|
|
+ const index = this.pointList.findIndex(point => point.id == this.pointIds[0]);
|
|
|
+ if (index !== -1 && index > 0) {
|
|
|
+ let temp = this.pointList[index];
|
|
|
+ this.$set(this.pointList, index, this.pointList[index - 1]);
|
|
|
+ this.$set(this.pointList, index - 1, temp);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 点位下移
|
|
|
+ moveDown() {
|
|
|
+ const index = this.pointList.findIndex(point => point.id == this.pointIds[0]);
|
|
|
+ if (index !== -1 && index < this.pointList.length - 1) {
|
|
|
+ let temp = this.pointList[index];
|
|
|
+ this.$set(this.pointList, index, this.pointList[index + 1]);
|
|
|
+ this.$set(this.pointList, index + 1, temp);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 追加动作按钮
|
|
|
+ appendActionMenu() {
|
|
|
+ this.pointEditData.actionMenuList.push({ value: 0, other: 0 })
|
|
|
+ },
|
|
|
+ // 删除追加动作按钮
|
|
|
+ removeActionMenu(index) {
|
|
|
+ this.pointEditData.actionMenuList.splice(index, 1);
|
|
|
+ },
|
|
|
+ // 修改动作下拉值
|
|
|
+ changeAction(index) {
|
|
|
+ // 判断当前修改后是否是原定等待,如果不是则删除other属性
|
|
|
+ let item = this.pointEditData.actionMenuList[index];
|
|
|
+ if (item && 'other' in item) {
|
|
|
+ delete item.other; // 删除 'other' 属性
|
|
|
+ } else {
|
|
|
+ item.other = 0;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ clearActionDia() {
|
|
|
+ this.pointEditData.id = '';
|
|
|
+ this.pointEditData.x = '';
|
|
|
+ this.pointEditData.y = '';
|
|
|
+ this.pointEditData.type = '';
|
|
|
+ this.pointEditData.actionMenuList = [];
|
|
|
+ },
|
|
|
+ // 提交点位修改
|
|
|
+ submitEditPoint() {
|
|
|
+ this.pointEditDiaShow = false;
|
|
|
+ // 模拟数据修改
|
|
|
+ const point = this.pointList.find(item => item.id === this.pointEditData.id);
|
|
|
+ if (this.pointEditData.actionMenuList)
|
|
|
+ if (point) {
|
|
|
+ // 找到对应元素,更新数据(真实情况下发请求修改,然后重新查询点位列表)
|
|
|
+ point.x = this.pointEditData.x;
|
|
|
+ point.y = this.pointEditData.y;
|
|
|
+ point.type = this.pointEditData.type;
|
|
|
+ point.action = this.pointEditData.actionMenuList;
|
|
|
+ this.$modal.msgSuccess("当前点位数据已修改");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 生成任务
|
|
|
+ submitTaskGenerate() {
|
|
|
+ // 查询当前选择的id对应的点位对象数据
|
|
|
+ const orderedPoints = this.selectedWaypointIds.map(id => this.waypoints.find(point => point.id === id));
|
|
|
+ if (!this.generateTaskParam.taskName || !this.generateTaskParam.count || !this.generateTaskParam.time || this.generateTaskParam.date.length < 1) {
|
|
|
+ this.$message({
|
|
|
+ message: '请完善任务数据!',
|
|
|
+ type: 'warning'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.generateTaskParam.taskId = Math.floor(Math.random() * 10001);
|
|
|
+ let taskData = {
|
|
|
+ taskId: this.generateTaskParam.taskId,
|
|
|
+ taskName: this.generateTaskParam.taskName,
|
|
|
+ count: this.generateTaskParam.count,
|
|
|
+ time: this.generateTaskParam.time,
|
|
|
+ date: this.generateTaskParam.date,
|
|
|
+ status: 'idle',
|
|
|
+ points: orderedPoints
|
|
|
+ }
|
|
|
+ // 实际使用需要提交任务后等待后端添加完毕然后查询列表,这里临时用push
|
|
|
+ this.tasks.push(taskData);
|
|
|
+ this.restGenerateParam();
|
|
|
+ this.taskGenerateDiaShow = false;
|
|
|
+ this.selectedWaypointIds = [];
|
|
|
+ this.$message.success("点位任务创建成功");
|
|
|
+ },
|
|
|
+ // 初始化导航
|
|
|
+ initNavigation() {
|
|
|
+ this.closeDra()
|
|
|
+ if (this.poseInitEnable) {
|
|
|
+ this.poseInitEnable = false;
|
|
|
+ this.nowHandMenu = ''
|
|
|
+ } else {
|
|
|
+ this.poseInitEnable = true;
|
|
|
+ this.nowHandMenu = '初始化导航'
|
|
|
+ }
|
|
|
+ this.pointSelectionEnabled = false;
|
|
|
+ this.activeIndex = this.activeIndex == 3 ? -1 : 3;
|
|
|
+ },
|
|
|
+ // 重启导航
|
|
|
+ restNavigation() {
|
|
|
+ this.$confirm('将重启当前导航, 是否继续?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ customClass: 'el-message-box-cust',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ this.$message({
|
|
|
+ type: 'success',
|
|
|
+ message: '导航已重启!'
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 关闭导航
|
|
|
+ offNavigation() {
|
|
|
+ this.$confirm('将关闭当前导航, 是否继续?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ customClass: 'el-message-box-cust',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ this.$message({
|
|
|
+ type: 'success',
|
|
|
+ message: '导航已关闭!'
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 关闭任务生成弹窗
|
|
|
+ closeTaskGenerate() {
|
|
|
+ this.taskGenerateDiaShow = false;
|
|
|
+ this.restGenerateParam();
|
|
|
+ },
|
|
|
+ // 重置任务创建弹窗数据
|
|
|
+ restGenerateParam() {
|
|
|
+ this.generateTaskParam = {
|
|
|
+ taskId: '',
|
|
|
+ taskName: '',
|
|
|
+ number: 1,
|
|
|
+ time: '',
|
|
|
+ date: [] // 1-7分别指代周一到周末
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 任务删除
|
|
|
+ removeTaskItem(data) {
|
|
|
+ this.$confirm('删除名为' + data.taskName + '的任务', '删除', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ customClass: 'el-message-box-cust',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ this.$message({
|
|
|
+ type: 'success',
|
|
|
+ message: '已删除!'
|
|
|
+ });
|
|
|
+ this.taskDataList = this.taskDataList.filter(task => task.taskId !== data.taskId);
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 执行任务
|
|
|
+ executeTask(data) {
|
|
|
+ this.$confirm('开始执行任务' + data.taskName + '?', '执行', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ customClass: 'el-message-box-cust',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ this.$message({
|
|
|
+ type: 'success',
|
|
|
+ message: '任务已开始执行!'
|
|
|
+ });
|
|
|
+ this.taskDataList.forEach(task => {
|
|
|
+ if (task.taskId === data.taskId) {
|
|
|
+ task.status = 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ test() {
|
|
|
+ console.log(this.set);
|
|
|
+ },
|
|
|
+ // 开启地图选点
|
|
|
+ mapSelectEle(type) {
|
|
|
+ if (type == 'open') {
|
|
|
+ this.pointSelectionEnabled = true;
|
|
|
+ this.poseInitEnable = false;
|
|
|
+ this.$notify({
|
|
|
+ title: '地图选择',
|
|
|
+ message: '已开启选择模式',
|
|
|
+ type: 'success',
|
|
|
+ duration: 1000
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.pointSelectionEnabled = false;
|
|
|
+ this.$notify.info({
|
|
|
+ title: '关闭选择',
|
|
|
+ message: '已关闭选择模式',
|
|
|
+ duration: 1000
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 将当前选择的点位数据添加到点位列表中
|
|
|
+ // currentCoordinate 坐标信息 currentPlace 画布位置信息
|
|
|
+ addNowPoint(currentCoordinate, currentPlace) {
|
|
|
+ let coordData = {
|
|
|
+ id: currentCoordinate[0],
|
|
|
+ name: `目标点${currentCoordinate[0]}`,
|
|
|
+ x: currentCoordinate[1].toFixed(3),
|
|
|
+ y: currentCoordinate[2].toFixed(3),
|
|
|
+ placeX: currentPlace[0].toFixed(3),
|
|
|
+ placeY: currentPlace[1].toFixed(3),
|
|
|
+ type: 0,
|
|
|
+ action: [{ value: 0, other: 0 }]
|
|
|
+ }
|
|
|
+ this.waypoints.push(coordData);
|
|
|
+ this.$message.success(`已添加目标点: (${coordData.x}, ${coordData.y})`);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 位姿初始化操作绘制的回执
|
|
|
+ * @param position 坐标
|
|
|
+ * @param angle 角度
|
|
|
+ */
|
|
|
+ initNavigationResult(position, angle, nid) {
|
|
|
+ const prefix = process.env.VUE_APP_PNS_MQTT_PROXY;
|
|
|
+ let num = nid.split("_")[1];// 获取点位id编号
|
|
|
+ this.$refs.mqtt.publish(prefix + "/localization/action/init", {
|
|
|
+ "timestamp" : 123456,
|
|
|
+ "args" : [
|
|
|
+ {"nid" : Number(num)}
|
|
|
+ ]
|
|
|
+ },2,false
|
|
|
+ );
|
|
|
+ console.log(position);
|
|
|
+ console.log(angle);
|
|
|
+ },
|
|
|
+
|
|
|
+ // === 新UI相关方法 ===
|
|
|
+
|
|
|
+
|
|
|
+ // Tab切换事件
|
|
|
+ onTabChange(tabKey) {
|
|
|
+ this.activeTab = tabKey;
|
|
|
+ this.lastTab = tabKey;
|
|
|
+
|
|
|
+ // 根据Tab自动调整模式
|
|
|
+ if (tabKey === 'points' && this.selectPointMode) {
|
|
|
+ // 保持选点模式
|
|
|
+ } else if (tabKey !== 'points') {
|
|
|
+ // 切换到其他Tab时退出选点模式
|
|
|
+ this.selectPointMode = false;
|
|
|
+ this.nowHandMenu = '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 切换选点模式
|
|
|
+ toggleSelectPointMode() {
|
|
|
+ this.selectPointMode = !this.selectPointMode;
|
|
|
+ this.initPoseMode = false;
|
|
|
+ this.nowHandMenu = this.selectPointMode ? '选点模式' : '';
|
|
|
+
|
|
|
+ if (this.selectPointMode) {
|
|
|
+ this.$message.success('已进入选点模式');
|
|
|
+ } else {
|
|
|
+ this.$message.info('已退出选点模式');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 清空所有点位
|
|
|
+ clearAllPoints() {
|
|
|
+ this.$confirm('确定要清空所有点位吗?', '确认清空', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ this.pointList = [];
|
|
|
+ if (this.$refs.olmap && this.$refs.olmap.restIdNum) {
|
|
|
+ this.$refs.olmap.restIdNum();
|
|
|
+ }
|
|
|
+ this.$message.success('已清空所有点位');
|
|
|
+ }).catch(() => {});
|
|
|
+ },
|
|
|
+
|
|
|
+ // 地图缩放
|
|
|
+ handleZoomIn() {
|
|
|
+ try {
|
|
|
+ const map = this.getMapInstance();
|
|
|
+ if (map) {
|
|
|
+ // OpenLayers API
|
|
|
+ const view = map.getView();
|
|
|
+ const currentZoom = view.getZoom();
|
|
|
+ view.animate({
|
|
|
+ zoom: currentZoom + 1,
|
|
|
+ duration: 250
|
|
|
+ });
|
|
|
+ } else if (this.$refs.olmap && this.$refs.olmap.zoomIn) {
|
|
|
+ // 备用方法
|
|
|
+ this.$refs.olmap.zoomIn();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('地图放大失败:', error);
|
|
|
+ this.$message.warning('地图放大失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleZoomOut() {
|
|
|
+ try {
|
|
|
+ const map = this.getMapInstance();
|
|
|
+ if (map) {
|
|
|
+ // OpenLayers API
|
|
|
+ const view = map.getView();
|
|
|
+ const currentZoom = view.getZoom();
|
|
|
+ view.animate({
|
|
|
+ zoom: Math.max(currentZoom - 1, 1),
|
|
|
+ duration: 250
|
|
|
+ });
|
|
|
+ } else if (this.$refs.olmap && this.$refs.olmap.zoomOut) {
|
|
|
+ // 备用方法
|
|
|
+ this.$refs.olmap.zoomOut();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('地图缩小失败:', error);
|
|
|
+ this.$message.warning('地图缩小失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 居中到机器人
|
|
|
+ handleCenterToRobot() {
|
|
|
+ try {
|
|
|
+ const map = this.getMapInstance();
|
|
|
+ if (map) {
|
|
|
+ // OpenLayers API
|
|
|
+ const view = map.getView();
|
|
|
+
|
|
|
+ // 与标定页面保持一致的实现
|
|
|
+ let centerPoint = [this.robotPoseData.x, this.robotPoseData.y];
|
|
|
+ if (this.robotPoseData.x === 0 && this.robotPoseData.y === 0) {
|
|
|
+ // 使用当前地图中心点作为默认位置
|
|
|
+ centerPoint = view.getCenter();
|
|
|
+ console.log('使用地图中心点作为居中位置:', centerPoint);
|
|
|
+ } else {
|
|
|
+ console.log('使用机器人位置作为居中位置:', centerPoint);
|
|
|
+ }
|
|
|
+
|
|
|
+ view.animate({
|
|
|
+ center: centerPoint,
|
|
|
+ zoom: Math.max(view.getZoom(), 15),
|
|
|
+ duration: 500
|
|
|
+ });
|
|
|
+ this.$message.success('已居中到机器人位置');
|
|
|
+ } else if (this.$refs.olmap && this.$refs.olmap.centerToRobot) {
|
|
|
+ // 备用方法
|
|
|
+ this.$refs.olmap.centerToRobot();
|
|
|
+ this.$message.success('已居中到机器人位置');
|
|
|
+ } else {
|
|
|
+ console.warn('无法获取地图实例');
|
|
|
+ this.$message.warning('地图未就绪,无法居中');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('定位机器人失败:', error);
|
|
|
+ console.log('调试信息:', {
|
|
|
+ robotPoseData: this.robotPoseData,
|
|
|
+ robotPosition: this.robotPosition,
|
|
|
+ mapReady: !!this.getMapInstance()
|
|
|
+ });
|
|
|
+ this.$message.warning('定位机器人失败: ' + error.message);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 切换全屏
|
|
|
+ handleToggleFullscreen() {
|
|
|
+ const mapContainer = this.$el.querySelector('.map-stage');
|
|
|
+ if (!mapContainer) {
|
|
|
+ this.$message.error('无法找到地图容器');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (FullscreenOperations.toggleFullscreen(mapContainer)) {
|
|
|
+ // 全屏切换成功,状态会通过监听器自动更新
|
|
|
+ } else {
|
|
|
+ this.$message.error('浏览器不支持全屏功能');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 确认初始化
|
|
|
+ handleConfirmInit() {
|
|
|
+ this.initPoseMode = true;
|
|
|
+ this.selectPointMode = false;
|
|
|
+ this.nowHandMenu = '初始化导航';
|
|
|
+ this.$message.success('已进入位姿初始化模式');
|
|
|
+ },
|
|
|
+
|
|
|
+ // 确认重启
|
|
|
+ handleConfirmReboot() {
|
|
|
+ this.isBusy = true;
|
|
|
+ this.$message.success('重启指令已发送');
|
|
|
+ // 模拟重启过程
|
|
|
+ setTimeout(() => {
|
|
|
+ this.isBusy = false;
|
|
|
+ this.$message.info('系统重启完成');
|
|
|
+ }, 3000);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 确认停止
|
|
|
+ handleConfirmStop() {
|
|
|
+ this.selectPointMode = false;
|
|
|
+ this.initPoseMode = false;
|
|
|
+ this.nowHandMenu = '';
|
|
|
+ this.$message.warning('已执行急停操作');
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // 任务相关方法
|
|
|
+ showCreateTaskDialog() {
|
|
|
+ this.$message.info('创建任务功能待接入');
|
|
|
+ },
|
|
|
+
|
|
|
+ startTask(task) {
|
|
|
+ task.status = 'running';
|
|
|
+ this.$message.success(`任务 "${task.name}" 已开始执行`);
|
|
|
+ },
|
|
|
+
|
|
|
+ pauseTask(task) {
|
|
|
+ task.status = 'paused';
|
|
|
+ this.$message.warning(`任务 "${task.name}" 已暂停`);
|
|
|
+ },
|
|
|
+
|
|
|
+ resumeTask(task) {
|
|
|
+ task.status = 'running';
|
|
|
+ this.$message.success(`任务 "${task.name}" 已继续执行`);
|
|
|
+ },
|
|
|
+
|
|
|
+ cancelTask(task) {
|
|
|
+ this.$confirm(`确定要取消任务 "${task.name}" 吗?`, '确认取消', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ task.status = 'idle';
|
|
|
+ this.$message.info(`任务 "${task.name}" 已取消`);
|
|
|
+ }).catch(() => {});
|
|
|
+ },
|
|
|
+
|
|
|
+ // 切换初始化模式
|
|
|
+ toggleInitPoseMode() {
|
|
|
+ this.initPoseMode = !this.initPoseMode;
|
|
|
+ this.selectPointMode = false;
|
|
|
+ this.nowHandMenu = this.initPoseMode ? '初始化导航' : '';
|
|
|
+
|
|
|
+ if (this.initPoseMode) {
|
|
|
+ this.$message.success('已进入位姿初始化模式');
|
|
|
+ } else {
|
|
|
+ this.$message.info('已退出位姿初始化模式');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // RightPanel 事件处理方法
|
|
|
+ onRestart() {
|
|
|
+ this.handleConfirmReboot();
|
|
|
+ },
|
|
|
+
|
|
|
+ onStop() {
|
|
|
+ this.handleConfirmStop();
|
|
|
+ },
|
|
|
+
|
|
|
+ onInit() {
|
|
|
+ this.handleConfirmInit();
|
|
|
+ },
|
|
|
+
|
|
|
+ // MapToolbar 方法别名
|
|
|
+ onZoomIn() {
|
|
|
+ this.handleZoomIn();
|
|
|
+ },
|
|
|
+
|
|
|
+ onZoomOut() {
|
|
|
+ this.handleZoomOut();
|
|
|
+ },
|
|
|
+
|
|
|
+ onCenterRobot() {
|
|
|
+ this.handleCenterToRobot();
|
|
|
+ },
|
|
|
+
|
|
|
+ onToggleFullscreen() {
|
|
|
+ this.handleToggleFullscreen();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 辅助方法
|
|
|
+ getTaskStatusText(status) {
|
|
|
+ const statusMap = {
|
|
|
+ 'idle': '空闲',
|
|
|
+ 'running': '执行中',
|
|
|
+ 'paused': '暂停'
|
|
|
+ };
|
|
|
+ return statusMap[status] || '未知';
|
|
|
+ },
|
|
|
+
|
|
|
+ // === 导航页RightPanel事件处理 ===
|
|
|
+
|
|
|
+ // 目标点事件
|
|
|
+ onWpSelect(waypoint) {
|
|
|
+ console.log('选择目标点:', waypoint);
|
|
|
+ this.$message.success(`已选择目标点: ${waypoint.name}`);
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpSend(waypoint) {
|
|
|
+ console.log('发送目标点:', waypoint);
|
|
|
+ this.$message.success(`已发送目标点: ${waypoint.name}`);
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpCreate() {
|
|
|
+ console.log('创建目标点');
|
|
|
+ this.$message.info('创建目标点功能待实现');
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpEdit(waypoint) {
|
|
|
+ console.log('编辑目标点:', waypoint);
|
|
|
+ this.pointEditDiaShow = true;
|
|
|
+ this.pointEditData.id = waypoint.id;
|
|
|
+ this.pointEditData.x = waypoint.x;
|
|
|
+ this.pointEditData.y = waypoint.y;
|
|
|
+ this.pointEditData.type = waypoint.type;
|
|
|
+ this.pointEditData.actionMenuList = waypoint.action ? [...waypoint.action] : [{ value: 0, other: 0 }];
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpRemove(waypoint) {
|
|
|
+ console.log('删除目标点:', waypoint);
|
|
|
+ // 从waypoints数组中删除目标点
|
|
|
+ this.waypoints = this.waypoints.filter(wp => wp.id !== waypoint.id);
|
|
|
+ // 如果删除的是选中的目标点,也要从选中列表中移除
|
|
|
+ this.selectedWaypointIds = this.selectedWaypointIds.filter(id => id !== waypoint.id);
|
|
|
+ this.$message.success(`已删除目标点: ${waypoint.name || '目标点'}`);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // 新增的目标点操作方法
|
|
|
+ onWpMoveUp() {
|
|
|
+ if (this.selectedWaypointIds.length !== 1) return;
|
|
|
+
|
|
|
+ const selectedId = this.selectedWaypointIds[0];
|
|
|
+ const index = this.waypoints.findIndex(wp => wp.id === selectedId);
|
|
|
+
|
|
|
+ if (index > 0) {
|
|
|
+ // 交换位置
|
|
|
+ const temp = this.waypoints[index];
|
|
|
+ this.$set(this.waypoints, index, this.waypoints[index - 1]);
|
|
|
+ this.$set(this.waypoints, index - 1, temp);
|
|
|
+ this.$message.success('目标点已上移');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpMoveDown() {
|
|
|
+ if (this.selectedWaypointIds.length !== 1) return;
|
|
|
+
|
|
|
+ const selectedId = this.selectedWaypointIds[0];
|
|
|
+ const index = this.waypoints.findIndex(wp => wp.id === selectedId);
|
|
|
+
|
|
|
+ if (index < this.waypoints.length - 1) {
|
|
|
+ // 交换位置
|
|
|
+ const temp = this.waypoints[index];
|
|
|
+ this.$set(this.waypoints, index, this.waypoints[index + 1]);
|
|
|
+ this.$set(this.waypoints, index + 1, temp);
|
|
|
+ this.$message.success('目标点已下移');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpBatchRemove() {
|
|
|
+ if (this.selectedWaypointIds.length === 0) return;
|
|
|
+
|
|
|
+ this.$confirm(`确定要删除选中的 ${this.selectedWaypointIds.length} 个目标点吗?`, '批量删除', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ // 删除选中的目标点
|
|
|
+ this.waypoints = this.waypoints.filter(wp => !this.selectedWaypointIds.includes(wp.id));
|
|
|
+ this.selectedWaypointIds = [];
|
|
|
+ this.$message.success('目标点删除成功');
|
|
|
+ }).catch(() => {});
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpGoto() {
|
|
|
+ if (this.selectedWaypointIds.length !== 1) return;
|
|
|
+
|
|
|
+ const selectedWaypoint = this.waypoints.find(wp => wp.id === this.selectedWaypointIds[0]);
|
|
|
+ if (selectedWaypoint) {
|
|
|
+ this.$message.success(`正在前往目标点: ${selectedWaypoint.name || '目标点' + selectedWaypoint.id} (${selectedWaypoint.x}, ${selectedWaypoint.y})`);
|
|
|
+ // 这里可以添加实际的导航逻辑
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpGotoSingle(waypoint) {
|
|
|
+ this.$message.success(`正在前往目标点: ${waypoint.name || '目标点' + waypoint.id} (${waypoint.x}, ${waypoint.y})`);
|
|
|
+ // 这里可以添加实际的导航逻辑
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpCreateTask() {
|
|
|
+ if (this.selectedWaypointIds.length === 0) return;
|
|
|
+
|
|
|
+ const selectedWaypoints = this.waypoints.filter(wp => this.selectedWaypointIds.includes(wp.id));
|
|
|
+ console.log(`准备使用 ${selectedWaypoints.length} 个目标点创建任务`, selectedWaypoints);
|
|
|
+
|
|
|
+ // 打开任务创建对话框
|
|
|
+ this.taskGenerateDiaShow = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ onWpSelectionChange(selection) {
|
|
|
+ this.selectedWaypointIds = selection.map(wp => wp.id);
|
|
|
+ this.waypointSingle = selection.length !== 1;
|
|
|
+ this.waypointMultiple = selection.length === 0;
|
|
|
+ console.log('目标点选择变更:', this.selectedWaypointIds);
|
|
|
+ },
|
|
|
+
|
|
|
+ onMapSelectModeChange(isActive) {
|
|
|
+ this.selectPointMode = isActive;
|
|
|
+ this.initPoseMode = false; // 确保互斥
|
|
|
+
|
|
|
+ if (isActive) {
|
|
|
+ this.$message.success('已开启地图选点模式,点击地图添加目标点');
|
|
|
+ } else {
|
|
|
+ this.$message.info('已关闭地图选点模式');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // === 目标点编辑相关方法 ===
|
|
|
+
|
|
|
+ // 追加动作按钮
|
|
|
+ appendActionMenu() {
|
|
|
+ this.pointEditData.actionMenuList.push({ value: 0, other: 0 });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 删除追加动作按钮
|
|
|
+ removeActionMenu(index) {
|
|
|
+ this.pointEditData.actionMenuList.splice(index, 1);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 修改动作下拉值
|
|
|
+ changeAction(index) {
|
|
|
+ // 判断当前修改后是否是原定等待,如果不是则删除other属性
|
|
|
+ let item = this.pointEditData.actionMenuList[index];
|
|
|
+ if (item.value === 0) {
|
|
|
+ // 原地等待需要等待时间参数
|
|
|
+ if (!('other' in item)) {
|
|
|
+ item.other = 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 其他动作不需要等待时间参数
|
|
|
+ if ('other' in item) {
|
|
|
+ delete item.other;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 提交点位修改
|
|
|
+ submitEditPoint() {
|
|
|
+ this.pointEditDiaShow = false;
|
|
|
+ // 查找要修改的目标点
|
|
|
+ const waypoint = this.waypoints.find(item => item.id === this.pointEditData.id);
|
|
|
+ if (waypoint) {
|
|
|
+ // 更新数据
|
|
|
+ waypoint.x = this.pointEditData.x;
|
|
|
+ waypoint.y = this.pointEditData.y;
|
|
|
+ waypoint.type = this.pointEditData.type;
|
|
|
+ waypoint.action = [...this.pointEditData.actionMenuList];
|
|
|
+ this.$message.success("目标点数据已修改");
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 清空编辑对话框数据
|
|
|
+ clearActionDia() {
|
|
|
+ this.pointEditData.id = '';
|
|
|
+ this.pointEditData.x = '';
|
|
|
+ this.pointEditData.y = '';
|
|
|
+ this.pointEditData.type = '';
|
|
|
+ this.pointEditData.actionMenuList = [];
|
|
|
+ },
|
|
|
+
|
|
|
+ // 任务事件
|
|
|
+ onTaskView(task) {
|
|
|
+ console.log('查看任务详情:', task);
|
|
|
+ this.taskViewData = { ...task };
|
|
|
+ this.taskViewDiaShow = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ onTaskStart(task) {
|
|
|
+ console.log('开始任务:', task);
|
|
|
+ this.$message.success(`任务 ${task.taskName} 已开始`);
|
|
|
+ },
|
|
|
+
|
|
|
+ onTaskPause(task) {
|
|
|
+ console.log('暂停任务:', task);
|
|
|
+ this.$message.warning(`任务 ${task.taskName} 已暂停`);
|
|
|
+ },
|
|
|
+
|
|
|
+ onTaskStop(task) {
|
|
|
+ console.log('停止任务:', task);
|
|
|
+ this.$message.error(`任务 ${task.taskName} 已停止`);
|
|
|
+ },
|
|
|
+
|
|
|
+ onTaskRemove(task) {
|
|
|
+ console.log('删除任务:', task);
|
|
|
+ this.$message.warning(`任务 ${task.taskName} 已删除`);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化方法
|
|
|
+ getTaskStatusText(status) {
|
|
|
+ const statusMap = {
|
|
|
+ 0: '运行中',
|
|
|
+ 1: '空闲',
|
|
|
+ 'idle': '空闲',
|
|
|
+ 'running': '运行中',
|
|
|
+ 'paused': '暂停',
|
|
|
+ 'completed': '已完成',
|
|
|
+ 'error': '失败'
|
|
|
+ }
|
|
|
+ return statusMap[status] || '未知'
|
|
|
+ },
|
|
|
+
|
|
|
+ getTaskStatusClass(status) {
|
|
|
+ const statusClassMap = {
|
|
|
+ 0: 'status-running',
|
|
|
+ 1: 'status-idle',
|
|
|
+ 'idle': 'status-idle',
|
|
|
+ 'running': 'status-running',
|
|
|
+ 'paused': 'status-paused',
|
|
|
+ 'completed': 'status-completed',
|
|
|
+ 'error': 'status-error'
|
|
|
+ }
|
|
|
+ return statusClassMap[status] || 'status-unknown'
|
|
|
+ },
|
|
|
+
|
|
|
+ formatTime(time) {
|
|
|
+ if (!time) return '--'
|
|
|
+ if (typeof time === 'string') return time
|
|
|
+ if (time instanceof Date) {
|
|
|
+ return time.toLocaleTimeString('zh-CN', {
|
|
|
+ hour12: false,
|
|
|
+ hour: '2-digit',
|
|
|
+ minute: '2-digit'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return '--'
|
|
|
+ },
|
|
|
+
|
|
|
+ formatDate(dateArray) {
|
|
|
+ if (!dateArray || !Array.isArray(dateArray) || dateArray.length === 0) return '--'
|
|
|
+
|
|
|
+ const dayNames = {
|
|
|
+ '1': '周一',
|
|
|
+ '2': '周二',
|
|
|
+ '3': '周三',
|
|
|
+ '4': '周四',
|
|
|
+ '5': '周五',
|
|
|
+ '6': '周六',
|
|
|
+ '7': '周日'
|
|
|
+ }
|
|
|
+
|
|
|
+ return dateArray.map(day => dayNames[day] || day).join(', ')
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // 功能设置变更处理
|
|
|
+ onSettingChange(setting) {
|
|
|
+ console.log('功能设置变更:', setting);
|
|
|
+ this.settingParams[setting.key] = setting.value;
|
|
|
+
|
|
|
+ // 根据设置项类型执行相应操作
|
|
|
+ switch(setting.key) {
|
|
|
+ case 'pointCloud':
|
|
|
+ this.$message.info(`点云显示已${setting.value ? '开启' : '关闭'}`);
|
|
|
+ // 这里可以调用地图组件的点云显示/隐藏方法
|
|
|
+ break;
|
|
|
+ case 'baseMap':
|
|
|
+ this.$message.info(`底图显示已${setting.value ? '开启' : '关闭'}`);
|
|
|
+ // 这里可以调用地图组件的底图显示/隐藏方法
|
|
|
+ break;
|
|
|
+ case 'pointId':
|
|
|
+ this.$message.info(`点ID显示已${setting.value ? '开启' : '关闭'}`);
|
|
|
+ // 这里可以调用地图组件的点ID显示/隐藏方法
|
|
|
+ break;
|
|
|
+ case 'follow':
|
|
|
+ this.$message.info(`位置跟随已${setting.value ? '开启' : '关闭'}`);
|
|
|
+ // 这里可以调用地图组件的跟随模式开启/关闭方法
|
|
|
+ break;
|
|
|
+ case 'network':
|
|
|
+ this.$message.info(`网络邻居显示已${setting.value ? '开启' : '关闭'}`);
|
|
|
+ // 这里可以调用相关的网络邻居显示/隐藏方法
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ watch: {
|
|
|
+ // 监听面板可见性变化,触发地图刷新
|
|
|
+ panelVisible() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.updateOlCss();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 监听选点模式变化
|
|
|
+ selectPointMode(newVal) {
|
|
|
+ if (newVal) {
|
|
|
+ this.initPoseMode = false; // 确保互斥
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 监听初始化模式变化
|
|
|
+ initPoseMode(newVal) {
|
|
|
+ if (newVal) {
|
|
|
+ this.selectPointMode = false; // 确保互斥
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 监听实时信息变化,同步机器人位姿数据
|
|
|
+ 'realtimeInfo.coordinates': {
|
|
|
+ handler(newCoordinates) {
|
|
|
+ if (newCoordinates) {
|
|
|
+ const position = RobotPositionUtils.parseCoordinates(newCoordinates);
|
|
|
+ if (position) {
|
|
|
+ this.robotPoseData.x = position.x;
|
|
|
+ this.robotPoseData.y = position.y;
|
|
|
+ // 角度从heading字段解析,这里先保持不变
|
|
|
+ // this.robotPoseData.angle = parseFloat(this.realtimeInfo.heading.replace('°', '')) || 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.point-edit-span {
|
|
|
+ display: block;
|
|
|
+ margin: 10px 0;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.drawer {
|
|
|
+ height: 100%;
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 100%;
|
|
|
+ /* box-shadow: 4px 4px 12px rgba(201, 201, 201, 0.2); */
|
|
|
+ /* 右边和下边的阴影 */
|
|
|
+ border-radius: 0 0 12px 0;
|
|
|
+ border-left: 1px solid #F0F0F0;
|
|
|
+ padding: 8px 15px;
|
|
|
+ border-right: 1px solid #ececec;
|
|
|
+ border-bottom: 1px solid #ececec;
|
|
|
+ overflow-y: auto;
|
|
|
+ background-color: #fff;
|
|
|
+ z-index: 1000;
|
|
|
+}
|
|
|
+
|
|
|
+.drawer-close {
|
|
|
+ position: absolute;
|
|
|
+ right: 3px;
|
|
|
+ top: 3px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.drawer-title {
|
|
|
+ position: absolute;
|
|
|
+ top: -23px;
|
|
|
+ left: -8px;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #838383;
|
|
|
+}
|
|
|
+
|
|
|
+.drawer p {
|
|
|
+ font-size: 13px;
|
|
|
+ border-left: 5px #D1D1D1 solid;
|
|
|
+ padding-left: 5px;
|
|
|
+ margin: 8px 0;
|
|
|
+ border-radius: 3px 0 0 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.img-container {
|
|
|
+ text-align: center;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ /* 垂直居中子元素 */
|
|
|
+ border-radius: 7px;
|
|
|
+ background: linear-gradient(135deg, #00bcd4, #009688);
|
|
|
+ cursor: pointer;
|
|
|
+ width: 70%;
|
|
|
+ aspect-ratio: 1;
|
|
|
+ /* 设置宽高比为1,即高度和宽度相等 */
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.img-container:hover {
|
|
|
+ transform: scale(1.02);
|
|
|
+ /* 鼠标悬停时放大 */
|
|
|
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
|
|
+ /* 增加阴影效果 */
|
|
|
+ background-color: #00796b;
|
|
|
+ /* 改变背景颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+.img-container:active {
|
|
|
+ transform: scale(0.98);
|
|
|
+ /* 点击时缩小 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
|
+ /* 点击时加深阴影 */
|
|
|
+ background-color: #004d40;
|
|
|
+ /* 点击时改变背景颜色 */
|
|
|
+ filter: brightness(1.1);
|
|
|
+ /* 点击时稍微增加亮度 */
|
|
|
+}
|
|
|
+
|
|
|
+.img-container img {
|
|
|
+ opacity: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.img-container span {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #ffffff;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+/* 激活时的样式 */
|
|
|
+.img-container.active {
|
|
|
+ background: linear-gradient(135deg, #007d8d, #004b43);
|
|
|
+}
|
|
|
+
|
|
|
+.explore-unit {
|
|
|
+ margin-left: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .el-dialog__body {
|
|
|
+ padding: 20px 20px 0 20px;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .download-map .el-dialog__body {
|
|
|
+ padding: 10px 20px 0 20px !important;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .el-table--medium .el-table__cell {
|
|
|
+ padding: 6px 0;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .action-menu .action-menu_input .el-input__inner {
|
|
|
+ padding: 0 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.task-status-tag {
|
|
|
+ margin-left: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .drawer .el-collapse-item__header {
|
|
|
+ height: 38px;
|
|
|
+ line-height: 38px;
|
|
|
+ color: #767676;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .drawer .el-collapse-item__content {
|
|
|
+ text-align: left;
|
|
|
+ padding-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .drawer .el-collapse {
|
|
|
+ border: 1px solid #EBEEF5;
|
|
|
+ padding: 0 8px;
|
|
|
+ border-radius: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.collapse-content-div {
|
|
|
+ margin-top: 0;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .collapse-content-div .el-button--mini {
|
|
|
+ padding: 4px 10px;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+.hand-ment-mark {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 12px;
|
|
|
+ left: 12px;
|
|
|
+ z-index: 1000;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+.notification__title {
|
|
|
+ font-size: 1.2rem;
|
|
|
+ /* 默认字体大小 */
|
|
|
+}
|
|
|
+
|
|
|
+.navigation-container {
|
|
|
+ width: 100%;
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ background: var(--color-bg-secondary);
|
|
|
+
|
|
|
+ .map-stage {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: calc(100vh - 84px);
|
|
|
+ min-height: 600px;
|
|
|
+ overflow: hidden;
|
|
|
+ background: var(--color-bg-secondary);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 新UI浮层样式 - 重新设计为浮动面板 */
|
|
|
+.nav-toolbar {
|
|
|
+ position: absolute;
|
|
|
+ left: 16px;
|
|
|
+ top: 96px;
|
|
|
+ z-index: 50;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.main-menu {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 目标点编辑对话框样式 */
|
|
|
+::v-deep .waypoint-edit-dialog {
|
|
|
+ /* 修复问题1:紫色标题栏圆角对齐 */
|
|
|
+ .el-dialog {
|
|
|
+ border-radius: 12px !important;
|
|
|
+ overflow: hidden !important; /* 确保子元素不会超出圆角 */
|
|
|
+ margin-top: 0 !important; /* 移除默认的上边距 */
|
|
|
+ margin-bottom: 0 !important; /* 移除默认的下边距 */
|
|
|
+
|
|
|
+ /* 确保对话框垂直居中 */
|
|
|
+ position: fixed !important;
|
|
|
+ top: 50% !important;
|
|
|
+ left: 50% !important;
|
|
|
+ transform: translate(-50%, -50%) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__header {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: white;
|
|
|
+ padding: 20px 24px 16px;
|
|
|
+ margin: 0;
|
|
|
+ border-radius: 12px 12px 0 0 !important; /* 只有上方圆角 */
|
|
|
+
|
|
|
+ .el-dialog__title {
|
|
|
+ color: white;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__close {
|
|
|
+ color: white;
|
|
|
+ font-size: 18px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #f0f0f0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 24px;
|
|
|
+ background: #f8fafc;
|
|
|
+ margin: 0 !important; /* 确保没有额外边距 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__footer {
|
|
|
+ padding: 16px 24px 24px;
|
|
|
+ background: #f8fafc;
|
|
|
+ border-top: 1px solid #e2e8f0;
|
|
|
+ border-radius: 0 0 12px 12px !important; /* 只有下方圆角 */
|
|
|
+ margin: 0 !important; /* 确保没有额外边距 */
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-content {
|
|
|
+ .form-section {
|
|
|
+ background: white;
|
|
|
+ border-radius: 10px; /* 稍微增加圆角,与整体设计更协调 */
|
|
|
+ padding: 24px; /* 增加内边距,让内容更宽松 */
|
|
|
+ margin-bottom: 20px; /* 增加卡片间距 */
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); /* 稍微增强阴影 */
|
|
|
+ border: 1px solid #f1f5f9; /* 添加淡边框 */
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin: 0 0 20px 0; /* 增加标题与内容的间距 */
|
|
|
+ font-size: 15px; /* 稍微增加字体大小 */
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2d3748;
|
|
|
+ border-bottom: 2px solid #e2e8f0;
|
|
|
+ padding-bottom: 10px; /* 增加下内边距 */
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 10px; /* 增加图标与文字的间距 */
|
|
|
+ color: #667eea;
|
|
|
+ font-size: 18px; /* 稍微增加图标大小 */
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 添加动作按钮样式优化 */
|
|
|
+ .add-action-btn {
|
|
|
+ margin-left: 12px !important;
|
|
|
+ display: inline-flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ padding: 4px 8px !important;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 4px !important; /* 减少图标与文字的间距 */
|
|
|
+ font-size: 12px !important;
|
|
|
+ color: #667eea !important;
|
|
|
+ display: inline-block !important;
|
|
|
+ vertical-align: middle !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ span {
|
|
|
+ font-size: 12px !important;
|
|
|
+ color: #667eea !important;
|
|
|
+ line-height: 1 !important;
|
|
|
+ vertical-align: middle !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ i, span {
|
|
|
+ color: #409eff !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .waypoint-form {
|
|
|
+ .el-form-item {
|
|
|
+ margin-bottom: 18px; /* 适中的表单项间距 */
|
|
|
+ display: flex !important; /* 使用flex布局 */
|
|
|
+ align-items: center !important; /* 标签和输入框水平对齐 */
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 确保输入框容器占用剩余空间 */
|
|
|
+ .el-form-item__content {
|
|
|
+ flex: 1 !important;
|
|
|
+ margin-left: 0 !important; /* 移除默认左边距 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 坐标输入框特殊样式 */
|
|
|
+ .coordinate-input {
|
|
|
+ margin-bottom: 4px; /* 坐标输入框之间的间距稍小 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-form-item__label {
|
|
|
+ font-weight: 600 !important; /* 增加字体粗细,更突出 */
|
|
|
+ color: #2d3748 !important; /* 更深的颜色,更清晰 */
|
|
|
+ padding-right: 12px !important; /* 适当的间距 */
|
|
|
+ min-width: 100px !important; /* 稍微增加宽度,适应新的标签 */
|
|
|
+ font-size: 14px !important; /* 统一字体大小 */
|
|
|
+ line-height: 44px !important; /* 与输入框高度保持一致,实现垂直居中 */
|
|
|
+ height: 44px !important; /* 设置标签高度与输入框一致 */
|
|
|
+ display: flex !important; /* 使用flex布局 */
|
|
|
+ align-items: center !important; /* 垂直居中对齐 */
|
|
|
+ margin-bottom: 0 !important; /* 移除默认下边距 */
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 优化坐标输入框样式 - 分行布局 */
|
|
|
+ .coordinate-input {
|
|
|
+ .el-input__inner {
|
|
|
+ border-radius: 8px !important; /* 稍微增加圆角 */
|
|
|
+ border: 1px solid #e2e8f0 !important;
|
|
|
+ height: 44px !important; /* 增加输入框高度,更宽松 */
|
|
|
+ font-size: 15px !important; /* 增加字体大小,更易读 */
|
|
|
+ padding: 0 16px !important; /* 增加左右内边距 */
|
|
|
+ background: #ffffff !important;
|
|
|
+ transition: all 0.2s ease !important;
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea !important;
|
|
|
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
|
|
|
+ background: #fafbff !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #cbd5e0 !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 通用输入框样式 */
|
|
|
+ .el-input {
|
|
|
+ .el-input__inner {
|
|
|
+ border-radius: 6px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ height: 40px !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ padding: 0 12px !important;
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea;
|
|
|
+ box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-select {
|
|
|
+ .el-input__inner {
|
|
|
+ border-radius: 6px;
|
|
|
+ height: 40px !important; /* 与输入框保持一致的高度 */
|
|
|
+ font-size: 14px !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-list {
|
|
|
+ .action-item {
|
|
|
+ background: #f7fafc;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 12px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #cbd5e0;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .action-index {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ background: #667eea;
|
|
|
+ color: white;
|
|
|
+ border-radius: 50%;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .remove-btn {
|
|
|
+ color: #e53e3e;
|
|
|
+ padding: 4px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #fed7d7;
|
|
|
+ color: #c53030;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-params {
|
|
|
+ padding-left: 36px;
|
|
|
+ margin-top: 12px; /* 增加与上方的间距 */
|
|
|
+ display: flex !important; /* 使用flex布局 */
|
|
|
+ align-items: center !important; /* 垂直居中对齐 */
|
|
|
+ flex-wrap: nowrap !important; /* 防止换行 */
|
|
|
+
|
|
|
+ /* 修复问题3:优化等待时间输入框样式 */
|
|
|
+ .el-input {
|
|
|
+ display: inline-flex !important; /* 改为inline-flex,防止换行 */
|
|
|
+ width: 160px !important; /* 稍微增加宽度,给"秒"单位更多空间 */
|
|
|
+ align-items: center !important; /* 确保垂直居中 */
|
|
|
+
|
|
|
+ .el-input__inner {
|
|
|
+ background: white;
|
|
|
+ height: 36px !important; /* 适中的高度 */
|
|
|
+ font-size: 14px !important;
|
|
|
+ border-radius: 6px !important;
|
|
|
+ border: 1px solid #e2e8f0 !important;
|
|
|
+ padding: 0 12px !important;
|
|
|
+ flex: 1 !important; /* 输入框占用主要空间 */
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea;
|
|
|
+ box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 修复问题3:优化"秒"单位的append样式 */
|
|
|
+ .el-input-group__append {
|
|
|
+ background: #f8fafc !important;
|
|
|
+ border-color: #e2e8f0 !important;
|
|
|
+ color: #4a5568 !important;
|
|
|
+ font-weight: 500 !important;
|
|
|
+ padding: 0 12px !important; /* 调整内边距 */
|
|
|
+ border-radius: 0 6px 6px 0 !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ min-width: 40px !important; /* 增加最小宽度 */
|
|
|
+ height: 36px !important; /* 明确设置高度 */
|
|
|
+ display: flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ justify-content: center !important;
|
|
|
+ border-left: none !important; /* 移除左边框,与输入框无缝连接 */
|
|
|
+ white-space: nowrap !important; /* 防止文字换行 */
|
|
|
+ flex-shrink: 0 !important; /* 防止收缩 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .dialog-footer {
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ padding: 10px 20px;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+
|
|
|
+ &.el-button--primary {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ opacity: 0.9;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 创建任务对话框样式 */
|
|
|
+::v-deep .task-create-dialog {
|
|
|
+ /* 修复问题1:紫色标题栏圆角对齐 */
|
|
|
+ .el-dialog {
|
|
|
+ border-radius: 12px !important;
|
|
|
+ overflow: hidden !important; /* 确保子元素不会超出圆角 */
|
|
|
+ margin-top: 0 !important; /* 移除默认的上边距 */
|
|
|
+ margin-bottom: 0 !important; /* 移除默认的下边距 */
|
|
|
+
|
|
|
+ /* 确保对话框垂直居中 */
|
|
|
+ position: fixed !important;
|
|
|
+ top: 50% !important;
|
|
|
+ left: 50% !important;
|
|
|
+ transform: translate(-50%, -50%) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__header {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: white;
|
|
|
+ padding: 20px 24px 16px;
|
|
|
+ margin: 0;
|
|
|
+ border-radius: 12px 12px 0 0 !important; /* 只有上方圆角 */
|
|
|
+
|
|
|
+ .el-dialog__title {
|
|
|
+ color: white;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__close {
|
|
|
+ color: white;
|
|
|
+ font-size: 18px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #f0f0f0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 24px;
|
|
|
+ background: #fafbfc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__footer {
|
|
|
+ background: white;
|
|
|
+ padding: 16px 24px;
|
|
|
+ border-radius: 0 0 12px 12px !important; /* 只有下方圆角 */
|
|
|
+ border-top: 1px solid #e2e8f0;
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ padding: 10px 20px;
|
|
|
+ font-weight: 500;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &:not(.el-button--primary) {
|
|
|
+ color: #64748b;
|
|
|
+ border-color: #cbd5e1;
|
|
|
+ background: white;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #475569;
|
|
|
+ border-color: #94a3b8;
|
|
|
+ background: #f8fafc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.el-button--primary {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.task-form {
|
|
|
+ .el-form-item {
|
|
|
+ margin-bottom: 18px; /* 适中的表单项间距 */
|
|
|
+ display: flex !important; /* 使用flex布局 */
|
|
|
+ align-items: center !important; /* 标签和输入框水平对齐 */
|
|
|
+
|
|
|
+ .el-form-item__label {
|
|
|
+ color: #374151;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 44px !important; /* 与输入框高度保持一致,实现垂直居中 */
|
|
|
+ height: 44px !important; /* 设置标签高度与输入框一致 */
|
|
|
+ display: flex !important; /* 使用flex布局 */
|
|
|
+ align-items: center !important; /* 垂直居中对齐 */
|
|
|
+ margin-bottom: 0 !important; /* 移除默认下边距 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-form-item__content {
|
|
|
+ flex: 1 !important;
|
|
|
+ margin-left: 0 !important; /* 移除默认左边距 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.task-input {
|
|
|
+ .el-input__inner {
|
|
|
+ background: white;
|
|
|
+ height: 44px !important; /* 统一输入框高度 */
|
|
|
+ font-size: 14px !important;
|
|
|
+ border-radius: 8px !important; /* 现代化圆角 */
|
|
|
+ border: 1px solid #e2e8f0 !important;
|
|
|
+ padding: 0 16px !important; /* 增加内边距 */
|
|
|
+ transition: all 0.2s ease !important;
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea !important;
|
|
|
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
|
|
|
+ background: white !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::placeholder {
|
|
|
+ color: #9ca3af !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.task-input-number {
|
|
|
+ width: 100% !important;
|
|
|
+ display: block !important; /* 确保占满整行 */
|
|
|
+
|
|
|
+ .el-input-number__decrease,
|
|
|
+ .el-input-number__increase {
|
|
|
+ background: #f8fafc !important;
|
|
|
+ border-color: #e2e8f0 !important;
|
|
|
+ color: #667eea !important;
|
|
|
+ width: 32px !important; /* 固定按钮宽度 */
|
|
|
+ height: 22px !important; /* 调整按钮高度,让上下按钮都能显示 */
|
|
|
+ line-height: 20px !important; /* 调整行高 */
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #667eea !important;
|
|
|
+ color: white !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input-number__increase {
|
|
|
+ border-radius: 0 8px 0 0 !important; /* 上按钮圆角 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input-number__decrease {
|
|
|
+ border-radius: 0 0 8px 0 !important; /* 下按钮圆角 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input {
|
|
|
+ width: 100% !important; /* 确保input容器占满宽度 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input__inner {
|
|
|
+ background: white !important;
|
|
|
+ height: 44px !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ border-radius: 8px !important;
|
|
|
+ border: 1px solid #e2e8f0 !important;
|
|
|
+ padding: 0 68px 0 16px !important; /* 右侧留出按钮空间 */
|
|
|
+ text-align: left !important;
|
|
|
+ width: 100% !important; /* 确保输入框占满宽度 */
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea !important;
|
|
|
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.task-time-picker {
|
|
|
+ width: 100% !important;
|
|
|
+ display: block !important; /* 确保占满整行 */
|
|
|
+
|
|
|
+ .el-input {
|
|
|
+ width: 100% !important; /* 确保input容器占满宽度 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input__inner {
|
|
|
+ background: white !important;
|
|
|
+ height: 44px !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ border-radius: 8px !important;
|
|
|
+ border: 1px solid #e2e8f0 !important;
|
|
|
+ padding: 0 80px 0 16px !important; /* 大幅增加右侧内边距,从60px到80px */
|
|
|
+ width: 100% !important; /* 确保输入框占满宽度 */
|
|
|
+ box-sizing: border-box !important; /* 确保padding计算正确 */
|
|
|
+ text-overflow: ellipsis !important; /* 文字溢出处理 */
|
|
|
+ overflow: hidden !important;
|
|
|
+ white-space: nowrap !important;
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea !important;
|
|
|
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::placeholder {
|
|
|
+ color: #9ca3af !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input__suffix {
|
|
|
+ right: 24px !important; /* 进一步增加右侧距离 */
|
|
|
+ width: 40px !important; /* 增加图标区域宽度 */
|
|
|
+ text-align: center !important;
|
|
|
+ display: flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ justify-content: center !important;
|
|
|
+ height: 44px !important; /* 确保高度与输入框一致 */
|
|
|
+
|
|
|
+ .el-input__icon {
|
|
|
+ color: #9ca3af !important;
|
|
|
+ font-size: 16px !important;
|
|
|
+ margin: 0 !important; /* 移除任何默认边距 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.task-date-group {
|
|
|
+ display: flex !important;
|
|
|
+ flex-wrap: wrap !important;
|
|
|
+ gap: 8px 16px !important;
|
|
|
+
|
|
|
+ .el-checkbox {
|
|
|
+ margin-right: 0 !important;
|
|
|
+ margin-bottom: 8px !important;
|
|
|
+
|
|
|
+ .el-checkbox__label {
|
|
|
+ color: #374151 !important;
|
|
|
+ font-weight: 500 !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ padding-left: 8px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-checkbox__input.is-checked {
|
|
|
+ .el-checkbox__inner {
|
|
|
+ background-color: #667eea !important;
|
|
|
+ border-color: #667eea !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-checkbox__inner {
|
|
|
+ border-color: #d1d5db !important;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #667eea !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 任务查看对话框样式 */
|
|
|
+::v-deep .task-view-dialog {
|
|
|
+ .el-dialog {
|
|
|
+ border-radius: 12px !important;
|
|
|
+ overflow: hidden !important;
|
|
|
+ margin-top: 0 !important;
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+
|
|
|
+ position: fixed !important;
|
|
|
+ top: 50% !important;
|
|
|
+ left: 50% !important;
|
|
|
+ transform: translate(-50%, -50%) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__header {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: white;
|
|
|
+ padding: 20px 24px 16px;
|
|
|
+ margin: 0;
|
|
|
+ border-radius: 12px 12px 0 0 !important;
|
|
|
+
|
|
|
+ .el-dialog__title {
|
|
|
+ color: white;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__close {
|
|
|
+ color: white;
|
|
|
+ font-size: 18px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #f0f0f0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 24px;
|
|
|
+ background: #fafbfc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__footer {
|
|
|
+ background: white;
|
|
|
+ padding: 16px 24px;
|
|
|
+ border-radius: 0 0 12px 12px !important;
|
|
|
+ border-top: 1px solid #e2e8f0;
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ padding: 10px 20px;
|
|
|
+ font-weight: 500;
|
|
|
+ border-radius: 6px;
|
|
|
+ color: #64748b;
|
|
|
+ border-color: #cbd5e1;
|
|
|
+ background: white;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #475569;
|
|
|
+ border-color: #94a3b8;
|
|
|
+ background: #f8fafc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.task-view-form {
|
|
|
+ .el-form-item {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ display: flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ min-height: 32px;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-form-item__label {
|
|
|
+ color: #374151;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1 !important;
|
|
|
+ display: flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+ height: auto !important;
|
|
|
+ padding: 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-form-item__content {
|
|
|
+ flex: 1 !important;
|
|
|
+ margin-left: 0 !important;
|
|
|
+ display: flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-text {
|
|
|
+ color: #606266;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ &.status-text {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 6px 12px;
|
|
|
+ border-radius: 16px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ text-align: center;
|
|
|
+ min-width: 60px;
|
|
|
+ line-height: 1;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ margin: 0;
|
|
|
+
|
|
|
+ &.status-idle {
|
|
|
+ background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
|
|
+ color: #0369a1;
|
|
|
+ border-color: #bae6fd;
|
|
|
+ box-shadow: 0 1px 3px rgba(3, 105, 161, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-running {
|
|
|
+ background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
|
|
+ color: #15803d;
|
|
|
+ border-color: #bbf7d0;
|
|
|
+ box-shadow: 0 1px 3px rgba(21, 128, 61, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-paused {
|
|
|
+ background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
|
|
|
+ color: #d97706;
|
|
|
+ border-color: #fed7aa;
|
|
|
+ box-shadow: 0 1px 3px rgba(217, 119, 6, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-completed {
|
|
|
+ background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
|
|
+ color: #15803d;
|
|
|
+ border-color: #bbf7d0;
|
|
|
+ box-shadow: 0 1px 3px rgba(21, 128, 61, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-error {
|
|
|
+ background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
|
|
|
+ color: #dc2626;
|
|
|
+ border-color: #fecaca;
|
|
|
+ box-shadow: 0 1px 3px rgba(220, 38, 38, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+</style>
|