| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743 |
- <template>
- <div class="navigation-container">
- <div class="map-stage" ref="mapStage">
- <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="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"
- :emergency-stop-enabled="emergencyStopEnabled"
- :navigation-stack-status="navigationStackStatus"
- @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"
- @emergency-stop-release="executeEmergencyStopRelease"
- />
-
- </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>
- <MqttComp ref="mqtt" :topics="topics" @message-received="onMessage" />
- </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'},
- { topic: this.$mqttPrefix + '/sensor/battery'},
- // 路径规划相关主题
- { topic: this.$mqttPrefix + '/planning/service/plan/response', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/planning/trajectory/2d/compact', qos: 2, retain: true },
- { topic: this.$mqttPrefix + '/planning/action/replan/reply', qos: 2, retain: false },
- // 任务下发相关主题
- { topic: this.$mqttPrefix + '/task/target/action/goto/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/task/target/event/arrive', qos: 2, retain: true },
- // 任务管理相关主题
- { topic: this.$mqttPrefix + '/task/agent/action/exec/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/task/agent/event/complete', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/task/realtime/info', qos: 0, retain: false },
- // 任务控制相关主题
- { topic: this.$mqttPrefix + '/task/procedure/action/pause/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/task/procedure/action/resume/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/task/procedure/action/cancel/reply', qos: 2, retain: false },
- // 导航控制相关主题
- { topic: this.$mqttPrefix + '/navigation/stack/action/start/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/navigation/stack/action/stop/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/navigation/stack/action/restart/reply', qos: 2, retain: false },
- // 急停控制相关主题
- { topic: this.$mqttPrefix + '/control/vehicle/action/stop/reply', qos: 2, retain: false },
- { topic: this.$mqttPrefix + '/control/vehicle/property/stop', qos: 2, retain: true }
- ],
- 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: this.$route.params.mapName || 'Unknown',
- currentTask: '',
- speed: '',
- speedCommand: '',
- coordinates: '',
- heading: '',
- totalDistance: '',
- registrationError: '',
- batteryLevel: ''
- },
-
- // 机器人位姿数据(用于OlMap组件)
- robotPoseData: {
- x: 1.813,
- y: -63.931,
- angle: 0.000
- },
- // 连接和状态
- isConnected: true,
- isBusy: false,
- isFullscreen: false,
-
- // 导航和急停状态
- navigationStackStatus: 'unknown', // unknown, started, stopped
- emergencyStopEnabled: 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: [],
- // 任务下发状态跟踪
- currentNavigationTask: null, // 当前导航任务
- isNavigating: false, // 是否正在导航
- navigationStatus: 'idle', // idle, planning, navigating, arrived, failed
- lastGotoRequest: null, // 最后一次goto请求,用于匹配响应
-
- // 任务管理系统
- currentExecutingTask: null, // 当前执行的任务
- taskQueue: [], // 任务队列
- isTaskExecuting: false, // 是否正在执行任务
- taskExecutionStatus: 'idle', // idle, executing, paused, completed, failed, cancelled
- currentTaskWaypointIndex: 0, // 当前任务执行到的目标点索引
- taskRealtimeInfo: {
- odom: { total: 0, remain: 0 },
- time: { total: 0, remain: 0, duration: 0 },
- driveMode: 'auto',
- autoReady: true
- }, // 任务实时信息
- lastTaskExecRequest: null, // 最后一次任务执行请求
- 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();
- },
-
- // 导航状态文本
- navigationStatusText() {
- switch(this.navigationStatus) {
- case 'idle': return '空闲';
- case 'planning': return '规划中';
- case 'navigating': return '导航中';
- case 'arrived': return '已到达';
- case 'failed': return '失败';
- default: return '未知';
- }
- },
-
- // 是否可以发送新的导航任务
- canStartNavigation() {
- return !this.isNavigating && this.navigationStatus !== 'planning';
- },
-
- // 当前导航目标描述
- currentNavigationTarget() {
- if (!this.currentNavigationTask) return null;
- const waypoint = this.currentNavigationTask.waypoint;
- return `${waypoint.name || '目标点' + waypoint.id} (${waypoint.x}, ${waypoint.y})`;
- },
-
- // 任务执行状态文本
- taskExecutionStatusText() {
- switch(this.taskExecutionStatus) {
- case 'idle': return '空闲';
- case 'executing': return '执行中';
- case 'paused': return '暂停';
- case 'completed': return '已完成';
- case 'failed': return '失败';
- case 'cancelled': return '已取消';
- default: return '未知';
- }
- },
-
- // 当前执行任务描述
- currentTaskDescription() {
- if (!this.currentExecutingTask) return null;
- const task = this.currentExecutingTask;
- const progress = `${this.currentTaskWaypointIndex}/${task.points?.length || 0}`;
- return `${task.taskName} (${progress})`;
- },
-
- // 是否可以开始新任务
- canStartNewTask() {
- return !this.isTaskExecuting && !this.isNavigating;
- },
-
- // 任务实时进度信息
- taskProgressInfo() {
- if (!this.currentExecutingTask || !this.taskRealtimeInfo) return null;
-
- return {
- taskName: this.currentExecutingTask.taskName,
- waypointProgress: `${this.currentTaskWaypointIndex}/${this.currentExecutingTask.points?.length || 0}`,
- totalDistance: `${(this.taskRealtimeInfo.odom.total / 1000).toFixed(2)}km`,
- remainDistance: `${(this.taskRealtimeInfo.odom.remain / 1000).toFixed(2)}km`,
- totalTime: this.formatSeconds(this.taskRealtimeInfo.time.total),
- remainTime: this.formatSeconds(this.taskRealtimeInfo.time.remain),
- duration: this.formatSeconds(this.taskRealtimeInfo.time.duration),
- driveMode: this.taskRealtimeInfo.driveMode === 'auto' ? '自动' : '手动',
- autoReady: this.taskRealtimeInfo.autoReady
- };
- }
- },
- 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: {
- goto() {
- // if (this.pointIds.length !== 1) {
- // this.$message.warning('请选择一个点位进行前往操作');
- // return;
- // }
- // const point = this.pointList.find(p => p.id === this.pointIds[0]);
- // if (point) {
- // console.log('前往点位:', point);
- // this.$refs.mqtt.publish(this.$mqttPrefix + "/task/target/action/goto", {
- // "timestamp" : 123456,
- // "args" : [
- // {
- // "roadmap" : this.mapName,
- // "coord" : [[Number(point.x), Number(point.y)]]
- // }
- // ]
- // },2,false);
- // this.$refs.olmap.drawPoint()
- // }
- // this.$refs.olmap.drawTrackLine(-15.491 , -57.526,10.209 , -123.223)
- },
- 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);
- } else if (topic === this.$mqttPrefix + '/sensor/battery') {
- // 处理电池消息
- const data = message.args[0];
- this.realtimeInfo.batteryLevel = data.capacity + '%' || '0%';
- } else if (topic === this.$mqttPrefix + '/planning/service/plan/response') {
- // 处理路径规划响应
- this.handlePlanResponse(message);
- } else if (topic === this.$mqttPrefix + '/planning/trajectory/2d/compact') {
- // 处理当前行驶轨迹
- this.handleTrajectoryData(message);
- } else if (topic === this.$mqttPrefix + '/planning/action/replan/reply') {
- // 处理重规划响应
- this.handleReplanReply(message);
- } else if (topic === this.$mqttPrefix + '/task/target/action/goto/reply') {
- // 处理前往目标点响应
- this.handleGotoReply(message);
- } else if (topic === this.$mqttPrefix + '/task/target/event/arrive') {
- // 处理到达目标点事件
- this.handleArriveEvent(message);
- } else if (topic === this.$mqttPrefix + '/task/agent/action/exec/reply') {
- // 处理任务执行响应
- this.handleTaskExecReply(message);
- } else if (topic === this.$mqttPrefix + '/task/agent/event/complete') {
- // 处理任务完成事件
- this.handleTaskCompleteEvent(message);
- } else if (topic === this.$mqttPrefix + '/task/realtime/info') {
- // 处理任务实时信息
- this.handleTaskRealtimeInfo(message);
- } else if (topic === this.$mqttPrefix + '/task/procedure/action/pause/reply') {
- // 处理暂停任务响应
- this.handleTaskPauseReply(message);
- } else if (topic === this.$mqttPrefix + '/task/procedure/action/resume/reply') {
- // 处理继续任务响应
- this.handleTaskResumeReply(message);
- } else if (topic === this.$mqttPrefix + '/task/procedure/action/cancel/reply') {
- // 处理取消任务响应
- this.handleTaskCancelReply(message);
- } else if (topic === this.$mqttPrefix + '/navigation/stack/action/start/reply') {
- // 处理导航启动响应
- this.handleNavigationStartReply(message);
- } else if (topic === this.$mqttPrefix + '/navigation/stack/action/stop/reply') {
- // 处理导航停止响应
- this.handleNavigationStopReply(message);
- } else if (topic === this.$mqttPrefix + '/navigation/stack/action/restart/reply') {
- // 处理导航重启响应
- this.handleNavigationRestartReply(message);
- } else if (topic === this.$mqttPrefix + '/control/vehicle/action/stop/reply') {
- // 处理急停控制响应
- this.handleEmergencyStopReply(message);
- } else if (topic === this.$mqttPrefix + '/control/vehicle/property/stop') {
- // 处理急停状态变化
- this.handleEmergencyStopStatus(message);
- }
- },
- handleInitReply(message) {
- // 处理初始化回复消息
- // console.log("初始化回复:", message);
-
- },
- handleLaserPose(message) {
- try {
- const data = message.args[0];
- const {xyz, rpy, blh, heading} = data.pose;
-
- // 保存旧的位置数据,用于比较
- const oldX = this.laserPositionData.x;
- const oldY = this.laserPositionData.y;
-
- // 激光定位实时数据
- this.laserPositionData.x = xyz[0];
- this.laserPositionData.y = xyz[1];
- this.laserPositionData.angle = rpy[2];
- // 实时数据
- this.realtimeInfo.coordinates = `(${xyz[0]}, ${xyz[1]}, ${xyz[2]})`;
- this.realtimeInfo.heading = rpy[2] + '°'; // 航向角
- this.realtimeInfo.speed = data.vel.heading+ 'm/s'; // 速度
- this.realtimeInfo.speedCommand = data.vel.enu + 'm/s'; // 速度指令
- this.realtimeInfo.totalDistance = data.odometer + 'm'; // 总里程
- this.realtimeInfo.registrationError = data.score;
-
- // 检查位置是否有变化 - 降低阈值以实现更频繁的轨迹更新
- const positionChanged = Math.abs(xyz[0] - oldX) > 0.005 || Math.abs(xyz[1] - oldY) > 0.005;
-
- // 如果位置发生变化且存在轨迹,更新轨迹进度
- if (positionChanged && this.$refs.olmap && this.$refs.olmap.currentTrajectory) {
- const currentTrajectory = this.$refs.olmap.currentTrajectory;
-
- // 检查是否到达目标点
- const reachedTarget = this.checkIfReachedTarget(currentTrajectory);
-
- // 如果还没到达目标点,实时更新轨迹绘制
- if (!reachedTarget) {
- // 不再基于进度索引,而是直接基于机器人位置重新绘制轨迹
- console.log(`机器人位置更新,重新绘制实时轨迹: (${xyz[0].toFixed(3)}, ${xyz[1].toFixed(3)})`);
- if (this.$refs.olmap.updateRealtimeTrajectory) {
- this.$refs.olmap.updateRealtimeTrajectory([xyz[0], xyz[1]]);
- }
- }
- }
-
- // 更新实时轨迹(机器人实际行走路径)
- // if (this.$refs.olmap && this.$refs.olmap.drawRealTimeTrajectory && (xyz[0] !== 0 || xyz[1] !== 0) && this.settingParams.showRealTimeTrajectory) {
- // this.$refs.olmap.drawRealTimeTrajectory([xyz[0], xyz[1]], this.trajectoryStyles.realTime);
- // }
-
- /* 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);
- }
- },
-
- // 处理路径规划响应
- handlePlanResponse(message) {
- console.log("路径规划响应:", message);
- if (message.status === 'ok' && message.args && message.args.length > 0) {
- const planData = message.args[0];
- console.log("规划路径数据:", planData);
- // 这里可以处理规划的路径点,但主要的轨迹绘制会通过trajectory消息来处理
- this.$message.success('路径规划成功!');
- } else {
- this.$message.error('路径规划失败');
- }
- },
-
- // 处理轨迹数据
- handleTrajectoryData(message) {
- console.log("接收到轨迹数据:", message);
- try {
- if (message.args && message.args.length > 0) {
- const trajectoryData = message.args[0];
- const { trj, idx, total, div } = trajectoryData;
-
- // 转换轨迹数据:真实坐标 = 坐标值/div
- const realTrajectory = trj.map(point => [
- point[0] / div,
- point[1] / div
- ]);
-
- // 计算当前机器人在轨迹中的进度
- const currentProgress = this.calculateCurrentProgress(realTrajectory);
-
- // 调用地图组件绘制轨迹
- if (this.$refs.olmap && this.$refs.olmap.drawTrajectory) {
- this.$refs.olmap.drawTrajectory(realTrajectory, {
- totalPoints: total,
- indices: idx,
- currentProgress: currentProgress,
- robotPosition: [this.laserPositionData.x, this.laserPositionData.y]
- });
- }
-
- console.log("轨迹绘制完成,总点数:", total, "轨迹点数:", realTrajectory.length);
- }
- } catch (error) {
- console.error("处理轨迹数据失败:", error);
- }
- },
-
- // 处理重规划响应
- handleReplanReply(message) {
- console.log("重规划响应:", message);
- if (message.status === 'ok') {
- this.$message.success('重规划成功');
- } else {
- this.$message.error('重规划失败');
- }
- },
-
- // 处理前往目标点响应
- handleGotoReply(message) {
- console.log("前往目标点响应:", message);
- try {
- if (message.status === 'ok') {
- // 检查是否匹配当前的goto请求
- if (this.lastGotoRequest && message.pub_timestamp === this.lastGotoRequest.timestamp) {
- this.navigationStatus = 'navigating';
- this.isNavigating = true;
- this.$message.success('机器人已接收前往指令,开始导航');
- }
- } else {
- this.navigationStatus = 'failed';
- this.isNavigating = false;
- this.$message.error('前往目标点请求失败');
- }
- } catch (error) {
- console.error("处理goto响应失败:", error);
- }
- },
-
- // 处理到达目标点事件
- handleArriveEvent(message) {
- console.log("到达目标点事件:", message);
- try {
- if (message.args && message.args.length > 0) {
- const arriveData = message.args[0];
- const { status, coord, nid, error } = arriveData;
-
- if (status === 'ok') {
- // 成功到达目标点
- this.navigationStatus = 'arrived';
- this.isNavigating = false;
-
- // 显示成功消息
- const targetInfo = coord && coord.length > 0 ?
- `(${coord[0][0]}, ${coord[0][1]})` :
- (nid && nid.length > 0 ? `点位${nid[0]}` : '目标点');
-
- // 检查是否是任务中的目标点到达
- if (this.currentExecutingTask && this.currentNavigationTask?.taskContext) {
- // 处理任务中的目标点到达
- this.handleTaskWaypointArrival();
- } else {
- // 单独目标点到达
- this.$message.success(`已成功到达目标点 ${targetInfo}!`);
-
- // 清除轨迹(2秒后)
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- setTimeout(() => {
- this.$refs.olmap.clearTrajectory();
- console.log('轨迹已自动清除');
- }, 2000);
- }
- }
-
- // 重置导航任务状态
- this.currentNavigationTask = null;
- this.lastGotoRequest = null;
-
- } else if (status === 'fail') {
- // 到达失败
- this.navigationStatus = 'failed';
- this.isNavigating = false;
-
- // 根据错误代码显示具体错误信息
- let errorMessage = '前往目标点失败';
- if (error) {
- switch (error) {
- case 21:
- errorMessage = '前往目标点失败:规划失败';
- break;
- case 22:
- errorMessage = '前往目标点失败:偏离车道线';
- break;
- case 23:
- errorMessage = '前往目标点失败:任务提前终止';
- break;
- case 24:
- errorMessage = '前往目标点失败:定位异常';
- break;
- default:
- errorMessage = `前往目标点失败:未知错误(${error})`;
- }
- }
-
- this.$message.error(errorMessage);
- console.error("导航失败,错误代码:", error);
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
-
- // 重置导航任务状态
- this.currentNavigationTask = null;
- this.lastGotoRequest = null;
- }
- }
- } catch (error) {
- console.error("处理到达事件失败:", error);
- }
- },
-
- // 处理任务执行响应
- handleTaskExecReply(message) {
- console.log("任务执行响应:", message);
- try {
- if (message.status === 'ok') {
- // 检查是否匹配当前的任务执行请求
- if (this.lastTaskExecRequest && message.pub_timestamp === this.lastTaskExecRequest.timestamp) {
- this.taskExecutionStatus = 'executing';
- this.isTaskExecuting = true;
- this.$message.success(`任务 "${this.currentExecutingTask.taskName}" 已开始执行`);
-
- // 开始执行任务的第一个目标点
- this.executeNextWaypoint();
- }
- } else {
- this.taskExecutionStatus = 'failed';
- this.isTaskExecuting = false;
- this.$message.error('任务执行失败');
- }
- } catch (error) {
- console.error("处理任务执行响应失败:", error);
- }
- },
-
- // 处理任务完成事件
- handleTaskCompleteEvent(message) {
- console.log("任务完成事件:", message);
- try {
- if (message.args && message.args.length > 0) {
- const completeData = message.args[0];
- const { name, status } = completeData;
-
- if (status === 'ok') {
- // 任务成功完成
- this.taskExecutionStatus = 'completed';
- this.isTaskExecuting = false;
- this.$message.success(`任务 "${name}" 已成功完成!`);
-
- // 更新任务列表中的状态
- const task = this.tasks.find(t => t.taskName === name);
- if (task) {
- task.status = 'completed';
- }
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- setTimeout(() => {
- this.$refs.olmap.clearTrajectory();
- console.log('任务完成,轨迹已清除');
- }, 3000);
- }
-
- } else if (status === 'fail') {
- // 任务失败
- this.taskExecutionStatus = 'failed';
- this.isTaskExecuting = false;
- this.$message.error(`任务 "${name}" 执行失败`);
-
- // 更新任务列表中的状态
- const task = this.tasks.find(t => t.taskName === name);
- if (task) {
- task.status = 'failed';
- }
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
- }
-
- // 重置任务执行状态
- this.currentExecutingTask = null;
- this.currentTaskWaypointIndex = 0;
- this.lastTaskExecRequest = null;
- }
- } catch (error) {
- console.error("处理任务完成事件失败:", error);
- }
- },
-
- // 处理任务实时信息
- handleTaskRealtimeInfo(message) {
- // console.log("任务实时信息:", message);
- try {
- if (message.args && message.args.length > 0) {
- const realtimeData = message.args[0];
- const { odom, time, drive_mode, auto_ready } = realtimeData;
-
- // 更新任务实时信息
- this.taskRealtimeInfo = {
- odom: {
- total: odom?.tottal || 0,
- remain: odom?.remain || 0
- },
- time: {
- total: time?.total || 0,
- remain: time?.remain || 0,
- duration: time?.duration || 0
- },
- driveMode: drive_mode || 'auto',
- autoReady: auto_ready !== undefined ? auto_ready : true
- };
-
- // 如果有正在执行的任务,更新实时信息到右侧面板
- if (this.currentExecutingTask) {
- this.realtimeInfo.currentTask = this.currentExecutingTask.taskName;
- this.realtimeInfo.totalDistance = `${(odom?.tottal / 1000).toFixed(2)}km` || '';
- // 可以在这里更新其他实时信息显示
- }
- }
- } catch (error) {
- console.error("处理任务实时信息失败:", error);
- }
- },
-
- // 处理暂停任务响应
- handleTaskPauseReply(message) {
- console.log("暂停任务响应:", message);
- if (message.status === 'ok') {
- this.taskExecutionStatus = 'paused';
- this.$message.success('任务已暂停');
- } else {
- this.$message.error('任务暂停失败');
- }
- },
-
- // 处理继续任务响应
- handleTaskResumeReply(message) {
- console.log("继续任务响应:", message);
- if (message.status === 'ok') {
- this.taskExecutionStatus = 'executing';
- this.$message.success('任务已继续执行');
- } else {
- this.$message.error('任务继续失败');
- }
- },
-
- // 处理取消任务响应
- handleTaskCancelReply(message) {
- console.log("取消任务响应:", message);
- if (message.status === 'ok') {
- this.taskExecutionStatus = 'cancelled';
- this.isTaskExecuting = false;
- this.$message.success('任务已取消');
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
-
- // 重置任务执行状态
- this.currentExecutingTask = null;
- this.currentTaskWaypointIndex = 0;
- this.lastTaskExecRequest = null;
- } else {
- this.$message.error('任务取消失败');
- }
- },
-
- // === 导航控制相关MQTT处理方法 ===
-
- // 处理导航启动响应
- handleNavigationStartReply(message) {
- console.log("导航启动响应:", message);
- try {
- if (message.status === 'ok') {
- this.navigationStackStatus = 'started';
- this.$message.success('导航系统已启动');
- } else {
- this.$message.error('导航系统启动失败');
- console.error("导航启动失败:", message);
- }
- } catch (error) {
- console.error("处理导航启动响应失败:", error);
- }
- },
-
- // 处理导航停止响应
- handleNavigationStopReply(message) {
- console.log("导航停止响应:", message);
- try {
- if (message.status === 'ok') {
- this.navigationStackStatus = 'stopped';
- // 停止导航时,清除当前的导航任务和轨迹
- this.isNavigating = false;
- this.navigationStatus = 'idle';
- this.currentNavigationTask = null;
- this.lastGotoRequest = null;
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
-
- this.$message.success('导航系统已停止');
- } else {
- this.$message.error('导航系统停止失败');
- console.error("导航停止失败:", message);
- }
- } catch (error) {
- console.error("处理导航停止响应失败:", error);
- }
- },
-
- // 处理导航重启响应
- handleNavigationRestartReply(message) {
- console.log("导航重启响应:", message);
- try {
- if (message.status === 'ok') {
- this.navigationStackStatus = 'started';
- // 重启导航时,重置所有导航相关状态
- this.isNavigating = false;
- this.navigationStatus = 'idle';
- this.currentNavigationTask = null;
- this.lastGotoRequest = null;
-
- // 重置任务执行状态
- this.isTaskExecuting = false;
- this.taskExecutionStatus = 'idle';
- this.currentExecutingTask = null;
- this.currentTaskWaypointIndex = 0;
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
-
- this.$message.success('导航系统已重启');
- this.isBusy = false;
- } else {
- this.$message.error('导航系统重启失败');
- this.isBusy = false;
- console.error("导航重启失败:", message);
- }
- } catch (error) {
- console.error("处理导航重启响应失败:", error);
- this.isBusy = false;
- }
- },
-
- // 处理急停控制响应
- handleEmergencyStopReply(message) {
- console.log("急停控制响应:", message);
- try {
- if (message.status === 'ok') {
- this.$message.success('急停指令已执行');
- } else {
- this.$message.error('急停指令执行失败');
- console.error("急停控制失败:", message);
- }
- } catch (error) {
- console.error("处理急停控制响应失败:", error);
- }
- },
-
- // 处理急停状态变化
- handleEmergencyStopStatus(message) {
- // console.log("急停状态:", message);
- try {
- if (message.args && message.args.length > 0) {
- const stopEnabled = message.args[0];
- const oldStatus = this.emergencyStopEnabled;
- this.emergencyStopEnabled = stopEnabled;
-
- // 只在状态真正变化时显示消息,避免频繁提示
- if (oldStatus !== stopEnabled) {
- if (stopEnabled) {
- this.$message.warning('车辆已启用急停状态');
- // 急停启用时,清除所有导航任务
- this.isNavigating = false;
- this.navigationStatus = 'idle';
- this.currentNavigationTask = null;
- this.isTaskExecuting = false;
- this.taskExecutionStatus = 'idle';
- } else {
- this.$message.info('车辆急停状态已解除');
- }
- }
- }
- } catch (error) {
- console.error("处理急停状态失败:", error);
- }
- },
-
- // 计算当前机器人在轨迹中的进度
- calculateCurrentProgress(trajectory) {
- if (!trajectory || trajectory.length === 0) return 0;
-
- const robotPos = [this.robotPoseData.x, this.robotPoseData.y];
- let progressDistance = 0; // 沿轨迹的累计距离
- let totalDistance = 0; // 轨迹总长度
-
- // 计算轨迹总长度
- for (let i = 0; i < trajectory.length - 1; i++) {
- const segmentLength = Math.sqrt(
- Math.pow(trajectory[i + 1][0] - trajectory[i][0], 2) +
- Math.pow(trajectory[i + 1][1] - trajectory[i][1], 2)
- );
- totalDistance += segmentLength;
- }
-
- if (totalDistance === 0) return 0;
-
- let bestProjectionDistance = 0;
- let minDistanceToTrajectory = Infinity;
-
- // 遍历轨迹的每个线段,找到机器人在轨迹上的投影点
- let accumulatedDistance = 0;
- for (let i = 0; i < trajectory.length - 1; i++) {
- const segmentStart = trajectory[i];
- const segmentEnd = trajectory[i + 1];
- const segmentLength = Math.sqrt(
- Math.pow(segmentEnd[0] - segmentStart[0], 2) +
- Math.pow(segmentEnd[1] - segmentStart[1], 2)
- );
-
- // 计算机器人到当前线段的投影点
- const projection = this.projectPointToLineSegment(robotPos, segmentStart, segmentEnd);
- const distanceToSegment = projection.distance;
-
- // 如果这是到轨迹最近的投影点
- if (distanceToSegment < minDistanceToTrajectory) {
- minDistanceToTrajectory = distanceToSegment;
- // 计算投影点在轨迹上的累计距离
- const projectionDistanceOnSegment = Math.sqrt(
- Math.pow(projection.point[0] - segmentStart[0], 2) +
- Math.pow(projection.point[1] - segmentStart[1], 2)
- );
- bestProjectionDistance = accumulatedDistance + projectionDistanceOnSegment;
- }
-
- accumulatedDistance += segmentLength;
- }
-
- // 转换为轨迹点索引(基于距离比例)
- const progressRatio = bestProjectionDistance / totalDistance;
- const progressIndex = Math.floor(progressRatio * (trajectory.length - 1));
-
- // 只有当机器人距离轨迹很近时(5米内),才更新进度
- if (minDistanceToTrajectory < 5.0) {
- console.log(`机器人轨迹进度: ${progressIndex}/${trajectory.length-1}, 距轨迹距离: ${minDistanceToTrajectory.toFixed(2)}m`);
- return Math.min(progressIndex, trajectory.length - 1);
- }
-
- // 如果离轨迹太远,保持之前的进度
- return this.$refs.olmap?.trajectoryProgress || 0;
- },
-
- // 计算点到线段的投影
- projectPointToLineSegment(point, lineStart, lineEnd) {
- const [px, py] = point;
- const [x1, y1] = lineStart;
- const [x2, y2] = lineEnd;
-
- const A = px - x1;
- const B = py - y1;
- const C = x2 - x1;
- const D = y2 - y1;
-
- const dot = A * C + B * D;
- const lenSq = C * C + D * D;
-
- if (lenSq === 0) {
- // 线段退化为点
- return {
- point: [x1, y1],
- distance: Math.sqrt(A * A + B * B)
- };
- }
-
- let param = dot / lenSq;
-
- // 限制参数在[0,1]范围内,确保投影点在线段上
- param = Math.max(0, Math.min(1, param));
-
- const projectionX = x1 + param * C;
- const projectionY = y1 + param * D;
-
- const distance = Math.sqrt(
- Math.pow(px - projectionX, 2) + Math.pow(py - projectionY, 2)
- );
-
- return {
- point: [projectionX, projectionY],
- distance: distance
- };
- },
-
- // 检测是否到达目标点(备用检测,主要依赖MQTT到达事件)
- checkIfReachedTarget(trajectory) {
- if (!trajectory || trajectory.length === 0) return false;
-
- // 如果已经通过MQTT确认到达或失败,不需要本地检测
- if (this.navigationStatus === 'arrived' || this.navigationStatus === 'failed') {
- return true;
- }
-
- // 备用的本地距离检测(仅用于防止MQTT消息丢失的情况)
- const robotPos = [this.laserPositionData.x, this.laserPositionData.y];
- const targetPos = trajectory[trajectory.length - 1]; // 最后一个点是目标点
-
- const distanceToTarget = Math.sqrt(
- Math.pow(targetPos[0] - robotPos[0], 2) + Math.pow(targetPos[1] - robotPos[1], 2)
- );
-
- // 如果距离目标点很近且正在导航,打印调试信息但不执行到达逻辑
- // (到达逻辑由MQTT事件处理)
- if (distanceToTarget < 1.0 && this.isNavigating) {
- console.log(`本地检测: 机器人接近目标点,距离: ${distanceToTarget.toFixed(2)}m,等待MQTT到达确认`);
- }
-
- return false; // 不执行本地到达逻辑
- },
-
- 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);
- this.$refs.olmap.removeCalibrationById(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() {
- // 使用保存的选中目标点数据
- const orderedPoints = this.generateTaskParam.selectedWaypoints || [];
- if (!this.generateTaskParam.taskName || orderedPoints.length === 0) {
- this.$message({
- message: '请完善任务数据(任务名称和目标点不能为空)!',
- type: 'warning'
- });
- return;
- }
-
- // 生成任务ID和时间戳
- this.generateTaskParam.taskId = Math.floor(Math.random() * 10001);
- const timestamp = new Date().getTime();
-
- let taskData = {
- taskId: this.generateTaskParam.taskId,
- taskName: this.generateTaskParam.taskName,
- count: this.generateTaskParam.count || 1,
- time: this.generateTaskParam.time,
- date: this.generateTaskParam.date || [],
- status: 'idle',
- points: [...orderedPoints], // 深拷贝目标点数据
- createdAt: timestamp,
- executionStatus: 'idle', // idle, executing, paused, completed, failed, cancelled
- currentWaypointIndex: 0,
- totalWaypoints: orderedPoints.length
- }
-
- // 添加到任务列表
- this.tasks.push(taskData);
-
- // 重置表单和状态
- this.restGenerateParam();
- this.taskGenerateDiaShow = false;
- this.selectedWaypointIds = [];
-
- this.$message.success(`多点位任务创建成功!任务包含 ${orderedPoints.length} 个目标点`);
- console.log("创建的任务:", taskData);
- },
- // 初始化导航
- 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: '',
- count: 1,
- time: '',
- date: [], // 1-7分别指代周一到周末
- selectedWaypoints: [] // 清空选中的目标点
- }
- },
- // 任务删除
- 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) {
- let num = nid.split("_")[1];// 获取点位id编号
- this.$refs.mqtt.publish(this.$mqttPrefix + "/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;
- const timestamp = new Date().getTime();
-
- // 构建导航重启请求,使用当前地图
- const restartRequest = {
- "timestamp": timestamp,
- "args": [this.mapName] // 重启后使用当前地图
- };
-
- // 发送导航重启MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/navigation/stack/action/restart", restartRequest, 2, false);
-
- this.$message.success('导航重启指令已发送');
- console.log("发送导航重启请求:", restartRequest);
-
- // 如果15秒内没有收到响应,自动取消忙碌状态
- setTimeout(() => {
- if (this.isBusy) {
- this.isBusy = false;
- this.$message.warning('导航重启响应超时,请检查系统状态');
- }
- }, 15000);
- },
-
- // 确认停止
- handleConfirmStop() {
- this.selectPointMode = false;
- this.initPoseMode = false;
- this.nowHandMenu = '';
-
- // 弹出选择对话框,让用户选择是停止导航还是急停
- this.$confirm('请选择要执行的操作:', '操作选择', {
- confirmButtonText: '急停',
- cancelButtonText: '停止导航',
- distinguishCancelAndClose: true,
- type: 'warning',
- customClass: 'stop-action-dialog'
- }).then(() => {
- // 用户选择急停
- this.executeEmergencyStop();
- }).catch((action) => {
- if (action === 'cancel') {
- // 用户选择停止导航
- this.executeNavigationStop();
- }
- // 如果是关闭对话框(action === 'close'),则不执行任何操作
- });
- },
-
- // 执行急停操作
- executeEmergencyStop() {
- const timestamp = new Date().getTime();
-
- // 构建急停请求
- const emergencyStopRequest = {
- "timestamp": timestamp,
- "args": [true] // true表示启用急停
- };
-
- // 发送急停MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/control/vehicle/action/stop", emergencyStopRequest, 1, false);
-
- this.$message.warning('急停指令已发送');
- console.log("发送急停请求:", emergencyStopRequest);
- },
-
- // 执行导航停止操作
- executeNavigationStop() {
- const timestamp = new Date().getTime();
-
- // 构建导航停止请求
- const stopRequest = {
- "timestamp": timestamp,
- "args": null
- };
-
- // 发送导航停止MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/navigation/stack/action/stop", stopRequest, 2, false);
-
- this.$message.info('导航停止指令已发送');
- console.log("发送导航停止请求:", stopRequest);
- },
-
- // 解除急停(可以在UI中添加按钮调用)
- executeEmergencyStopRelease() {
- const timestamp = new Date().getTime();
-
- // 构建解除急停请求
- const releaseRequest = {
- "timestamp": timestamp,
- "args": [false] // false表示解除急停
- };
-
- // 发送解除急停MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/control/vehicle/action/stop", releaseRequest, 1, false);
-
- this.$message.success('解除急停指令已发送');
- console.log("发送解除急停请求:", releaseRequest);
- },
-
-
- // 任务相关方法
- 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);
- // if (this.selectedWaypointIds.length < 1) {
- // // 重置地图的id计数器
- // this.$refs.olmap.restIdNum();
- // }
- this.$refs.olmap.removeIconHtmlById("calibration-"+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));
- // 获取所有即将被删除的 waypoints
- const waypointsToRemove = this.waypoints.filter(
- wp => this.selectedWaypointIds.includes(wp.id)
- );
- // 从地图上移除图标
- waypointsToRemove.forEach(wp => {
- this.$refs.olmap.removeIconHtmlById("calibration-" + wp.id);
- });
- // 从数组中移除这些 waypoints
- this.waypoints = this.waypoints.filter(
- wp => !this.selectedWaypointIds.includes(wp.id)
- );
- this.selectedWaypointIds = [];
- this.selectedWaypoints = [];
- this.$message.success('目标点删除成功');
- }).catch(() => {});
- },
- onWpGoto() {
- if (this.selectedWaypointIds.length !== 1) return;
-
- // 检查是否正在导航
- if (this.isNavigating) {
- this.$message.warning('机器人正在导航中,请等待任务完成后再发送新任务');
- return;
- }
-
- const selectedWaypoint = this.waypoints.find(wp => wp.id === this.selectedWaypointIds[0]);
- console.log('前往目标点:', selectedWaypoint);
-
- if (selectedWaypoint) {
- const timestamp = new Date().getTime();
-
- // 构建goto请求
- const gotoRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "roadmap": this.mapName,
- "nid": [], // 使用空数组,优先使用coord
- "coord": [
- [Number(selectedWaypoint.x), Number(selectedWaypoint.y)]
- ]
- }
- ]
- };
-
- // 保存当前任务状态
- this.currentNavigationTask = {
- waypoint: selectedWaypoint,
- timestamp: timestamp,
- status: 'planning'
- };
- this.lastGotoRequest = gotoRequest;
- this.navigationStatus = 'planning';
-
- // 发送路径规划请求(用于轨迹显示)
- const planRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "roadmap": this.mapName,
- "nid": [],
- "coord": [
- [Number(selectedWaypoint.x), Number(selectedWaypoint.y)]
- ]
- }
- ]
- };
-
- // 发送MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/planning/service/plan/request", planRequest, 2, false);
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/target/action/goto", gotoRequest, 2, false);
-
- this.$message.success(`正在发送前往指令: ${selectedWaypoint.name || '目标点' + selectedWaypoint.id} (${selectedWaypoint.x}, ${selectedWaypoint.y})`);
- console.log("发送前往目标点请求:", gotoRequest);
- console.log("发送路径规划请求:", planRequest);
- }
- },
- onWpGotoSingle(waypoint) {
- // 检查是否正在导航
- if (this.isNavigating) {
- this.$message.warning('机器人正在导航中,请等待任务完成后再发送新任务');
- return;
- }
-
- const timestamp = new Date().getTime();
-
- // 构建goto请求
- const gotoRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "roadmap": this.mapName,
- "nid": [], // 使用空数组,优先使用coord
- "coord": [
- [Number(waypoint.x), Number(waypoint.y)]
- ]
- }
- ]
- };
-
- // 保存当前任务状态
- this.currentNavigationTask = {
- waypoint: waypoint,
- timestamp: timestamp,
- status: 'planning'
- };
- this.lastGotoRequest = gotoRequest;
- this.navigationStatus = 'planning';
-
- // 发送路径规划请求(用于轨迹显示)
- const planRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "roadmap": this.mapName,
- "nid": [],
- "coord": [
- [Number(waypoint.x), Number(waypoint.y)]
- ]
- }
- ]
- };
-
- // 发送MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/planning/service/plan/request", planRequest, 2, false);
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/target/action/goto", gotoRequest, 2, false);
-
- this.$message.success(`正在发送前往指令: ${waypoint.name || '目标点' + waypoint.id} (${waypoint.x}, ${waypoint.y})`);
- console.log("发送前往目标点请求:", gotoRequest);
- console.log("发送路径规划请求:", planRequest);
- },
-
- // 取消当前导航任务
- cancelCurrentNavigation() {
- if (!this.isNavigating) {
- this.$message.info('当前没有正在进行的导航任务');
- return;
- }
-
- this.$confirm('确定要取消当前的导航任务吗?', '取消导航', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- // 重置导航状态
- this.navigationStatus = 'idle';
- this.isNavigating = false;
- this.currentNavigationTask = null;
- this.lastGotoRequest = null;
-
- // 清除轨迹
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
-
- this.$message.success('导航任务已取消');
- console.log('用户取消了导航任务');
- }).catch(() => {
- // 用户点击了取消
- });
- },
-
- // 执行下一个目标点
- executeNextWaypoint() {
- if (!this.currentExecutingTask || !this.currentExecutingTask.points) {
- console.error('没有正在执行的任务或任务没有目标点');
- return;
- }
-
- const waypoints = this.currentExecutingTask.points;
- const currentIndex = this.currentTaskWaypointIndex;
-
- if (currentIndex >= waypoints.length) {
- console.log('所有目标点已执行完成');
- return;
- }
-
- const currentWaypoint = waypoints[currentIndex];
- console.log(`执行目标点 ${currentIndex + 1}/${waypoints.length}:`, currentWaypoint);
-
- const timestamp = new Date().getTime();
-
- // 构建goto请求
- const gotoRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "roadmap": this.mapName,
- "nid": [],
- "coord": [
- [Number(currentWaypoint.x), Number(currentWaypoint.y)]
- ]
- }
- ]
- };
-
- // 发送路径规划请求(用于轨迹显示)
- const planRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "roadmap": this.mapName,
- "nid": [],
- "coord": [
- [Number(currentWaypoint.x), Number(currentWaypoint.y)]
- ]
- }
- ]
- };
-
- // 保存当前导航状态
- this.currentNavigationTask = {
- waypoint: currentWaypoint,
- timestamp: timestamp,
- taskContext: {
- taskId: this.currentExecutingTask.taskId,
- waypointIndex: currentIndex,
- totalWaypoints: waypoints.length
- }
- };
- this.lastGotoRequest = gotoRequest;
- this.navigationStatus = 'planning';
- this.isNavigating = true;
-
- // 发送MQTT消息
- this.$refs.mqtt.publish(this.$mqttPrefix + "/planning/service/plan/request", planRequest, 2, false);
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/target/action/goto", gotoRequest, 2, false);
-
- this.$message.info(`正在前往第 ${currentIndex + 1} 个目标点: ${currentWaypoint.name || '目标点' + currentWaypoint.id} (${currentWaypoint.x}, ${currentWaypoint.y})`);
- console.log("发送前往目标点请求:", gotoRequest);
- },
-
- // 处理任务中的目标点到达事件
- handleTaskWaypointArrival() {
- if (!this.currentExecutingTask) return;
-
- this.currentTaskWaypointIndex++;
- const totalWaypoints = this.currentExecutingTask.points.length;
-
- console.log(`目标点到达,进度: ${this.currentTaskWaypointIndex}/${totalWaypoints}`);
-
- if (this.currentTaskWaypointIndex >= totalWaypoints) {
- // 所有目标点都已完成,任务结束
- this.$message.success(`任务 "${this.currentExecutingTask.taskName}" 的所有目标点已完成!`);
- console.log('任务所有目标点执行完成');
- } else {
- // 继续执行下一个目标点
- this.$message.success(`第 ${this.currentTaskWaypointIndex} 个目标点已到达,继续前往下一个目标点...`);
- setTimeout(() => {
- this.executeNextWaypoint();
- }, 2000); // 2秒后继续下一个目标点
- }
- },
- onWpCreateTask() {
- if (this.selectedWaypointIds.length === 0) {
- this.$message.warning('请先选择要添加到任务中的目标点');
- return;
- }
-
- const selectedWaypoints = this.waypoints.filter(wp => this.selectedWaypointIds.includes(wp.id));
- console.log(`准备使用 ${selectedWaypoints.length} 个目标点创建任务`, selectedWaypoints);
-
- // 将选中的目标点保存到待创建任务中
- this.generateTaskParam.selectedWaypoints = 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;
-
- // 在地图上显示任务的所有目标点
- this.showTaskWaypointsOnMap(task);
- },
-
- onTaskStart(task) {
- console.log('开始任务:', task);
-
- // 检查是否有其他任务正在执行
- if (this.isTaskExecuting) {
- this.$message.warning('已有任务正在执行,请先完成或取消当前任务');
- return;
- }
-
- if (!task.points || task.points.length === 0) {
- this.$message.error('任务没有包含目标点,无法执行');
- return;
- }
-
- const timestamp = new Date().getTime();
-
- // 构建任务执行请求
- const taskExecRequest = {
- "timestamp": timestamp,
- "args": [
- {
- "name": task.taskName
- }
- ]
- };
-
- // 保存任务执行状态
- this.currentExecutingTask = task;
- this.currentTaskWaypointIndex = 0;
- this.lastTaskExecRequest = taskExecRequest;
- this.isTaskExecuting = true;
- this.taskExecutionStatus = 'executing';
-
- // 更新任务状态
- task.status = 'executing';
- task.executionStatus = 'executing';
-
- // 发送任务执行请求
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/agent/action/exec", taskExecRequest, 2, false);
-
- this.$message.success(`正在启动任务 "${task.taskName}",包含 ${task.points.length} 个目标点`);
- console.log("发送任务执行请求:", taskExecRequest);
- },
-
- onTaskPause(task) {
- console.log('暂停任务:', task);
-
- if (!this.isTaskExecuting || this.currentExecutingTask?.taskId !== task.taskId) {
- this.$message.warning('该任务当前未在执行中');
- return;
- }
-
- const timestamp = new Date().getTime();
-
- // 构建暂停任务请求
- const pauseRequest = {
- "timestamp": timestamp,
- "args": null
- };
-
- // 发送暂停任务请求
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/procedure/action/pause", pauseRequest, 1, false);
-
- console.log("发送暂停任务请求:", pauseRequest);
- },
-
- onTaskStop(task) {
- console.log('停止任务:', task);
-
- if (!this.isTaskExecuting || this.currentExecutingTask?.taskId !== task.taskId) {
- this.$message.warning('该任务当前未在执行中');
- return;
- }
-
- this.$confirm(`确定要取消任务 "${task.taskName}" 吗?`, '取消任务', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- const timestamp = new Date().getTime();
-
- // 构建取消任务请求
- const cancelRequest = {
- "timestamp": timestamp,
- "args": null
- };
-
- // 发送取消任务请求
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/procedure/action/cancel", cancelRequest, 1, false);
-
- console.log("发送取消任务请求:", cancelRequest);
- }).catch(() => {
- // 用户取消操作
- });
- },
-
- onTaskRemove(task) {
- console.log('删除任务:', task);
-
- // 检查任务是否正在执行
- if (this.isTaskExecuting && this.currentExecutingTask?.taskId === task.taskId) {
- this.$message.warning('任务正在执行中,无法删除。请先停止任务。');
- return;
- }
-
- this.$confirm(`确定要删除任务 "${task.taskName}" 吗?`, '删除任务', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- // 从任务列表中移除
- const index = this.tasks.findIndex(t => t.taskId === task.taskId);
- if (index !== -1) {
- this.tasks.splice(index, 1);
- this.$message.success(`任务 "${task.taskName}" 已删除`);
- }
- }).catch(() => {
- // 用户取消操作
- });
- },
-
- // 继续任务(用于暂停后的恢复)
- onTaskResume(task) {
- console.log('继续任务:', task);
-
- if (!this.currentExecutingTask || this.currentExecutingTask.taskId !== task.taskId) {
- this.$message.warning('该任务当前未在暂停状态');
- return;
- }
-
- if (this.taskExecutionStatus !== 'paused') {
- this.$message.warning('任务当前不在暂停状态');
- return;
- }
-
- const timestamp = new Date().getTime();
-
- // 构建继续任务请求
- const resumeRequest = {
- "timestamp": timestamp,
- "args": null
- };
-
- // 发送继续任务请求
- this.$refs.mqtt.publish(this.$mqttPrefix + "/task/procedure/action/resume", resumeRequest, 2, false);
-
- console.log("发送继续任务请求:", resumeRequest);
- },
-
- // 在地图上显示任务的目标点
- showTaskWaypointsOnMap(task) {
- if (!task.points || task.points.length === 0) {
- this.$message.info('该任务没有包含目标点');
- return;
- }
-
- // 清除现有的轨迹和标记
- if (this.$refs.olmap && this.$refs.olmap.clearTrajectory) {
- this.$refs.olmap.clearTrajectory();
- }
-
- // 在地图上显示所有目标点
- task.points.forEach((waypoint, index) => {
- if (this.$refs.olmap && this.$refs.olmap.addHtmlIcon) {
- // 使用不同的图标样式来区分任务目标点
- this.$refs.olmap.addHtmlIcon(
- `T${index + 1}`,
- parseFloat(waypoint.x),
- parseFloat(waypoint.y),
- '',
- 'task-waypoint'
- );
- }
- });
-
- this.$message.success(`已在地图上显示任务 "${task.taskName}" 的 ${task.points.length} 个目标点`);
- console.log(`显示任务目标点:`, task.points);
- },
-
- // 格式化方法
- 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(', ')
- },
-
- // 格式化秒数为时分秒
- formatSeconds(seconds) {
- if (!seconds || seconds < 0) return '00:00:00';
-
- const hours = Math.floor(seconds / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- const secs = Math.floor(seconds % 60);
-
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
- },
-
- // 功能设置变更处理
- 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; // 确保互斥
- }
- },
-
- // 监听实时信息变化,同步机器人位姿数据(已移至handleLaserPose直接处理)
- '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 lang="scss">
- .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>
|