index.vue 84 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615
  1. <template>
  2. <div class="app-container">
  3. <el-form
  4. :model="queryParams"
  5. ref="queryForm"
  6. size="small"
  7. :inline="true"
  8. v-show="showSearch"
  9. label-width="68px"
  10. >
  11. <el-form-item label="任务名称" prop="bizName">
  12. <el-input
  13. v-model="queryParams.bizName"
  14. placeholder="请输入任务名称"
  15. clearable
  16. @keyup.enter.native="handleQuery"
  17. />
  18. </el-form-item>
  19. <el-form-item label="状态" prop="status">
  20. <el-select
  21. v-model="queryParams.status"
  22. placeholder="请选择状态"
  23. clearable
  24. >
  25. <el-option
  26. v-for="dict in dict.type.uavps_task_status"
  27. :key="dict.value"
  28. :label="dict.label"
  29. :value="dict.value"
  30. />
  31. </el-select>
  32. </el-form-item>
  33. <el-form-item label="开始时间" prop="startTime">
  34. <el-date-picker
  35. clearable
  36. v-model="queryParams.startTime"
  37. type="date"
  38. value-format="yyyy-MM-dd"
  39. placeholder="请选择开始时间"
  40. >
  41. </el-date-picker>
  42. </el-form-item>
  43. <el-form-item label="结束时间" prop="endTime">
  44. <el-date-picker
  45. clearable
  46. v-model="queryParams.endTime"
  47. type="date"
  48. value-format="yyyy-MM-dd"
  49. placeholder="请选择结束时间"
  50. >
  51. </el-date-picker>
  52. </el-form-item>
  53. <el-form-item label="创建时间">
  54. <el-date-picker
  55. v-model="daterangeCreateTime"
  56. style="width: 240px"
  57. value-format="yyyy-MM-dd"
  58. type="daterange"
  59. range-separator="-"
  60. start-placeholder="开始日期"
  61. end-placeholder="结束日期"
  62. ></el-date-picker>
  63. </el-form-item>
  64. <el-form-item>
  65. <el-button
  66. type="primary"
  67. icon="el-icon-search"
  68. size="mini"
  69. @click="handleQuery"
  70. >搜索</el-button
  71. >
  72. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
  73. >重置</el-button
  74. >
  75. </el-form-item>
  76. </el-form>
  77. <el-row :gutter="10" class="mb8">
  78. <el-col :span="1.5">
  79. <el-button
  80. type="primary"
  81. plain
  82. icon="el-icon-plus"
  83. size="mini"
  84. @click="handleAdd"
  85. v-hasPermi="['system:task:add']"
  86. >新增</el-button
  87. >
  88. </el-col>
  89. <el-col :span="1.5">
  90. <el-button
  91. type="success"
  92. plain
  93. icon="el-icon-edit"
  94. size="mini"
  95. :disabled="single"
  96. @click="handleUpdate"
  97. v-hasPermi="['system:task:edit']"
  98. >修改</el-button
  99. >
  100. </el-col>
  101. <el-col :span="1.5">
  102. <el-button
  103. type="danger"
  104. plain
  105. icon="el-icon-delete"
  106. size="mini"
  107. :disabled="multiple"
  108. @click="handleDelete"
  109. v-hasPermi="['system:task:remove']"
  110. >删除</el-button
  111. >
  112. </el-col>
  113. <el-col :span="1.5">
  114. <el-button
  115. type="warning"
  116. plain
  117. icon="el-icon-download"
  118. size="mini"
  119. @click="handleExport"
  120. v-hasPermi="['system:task:export']"
  121. >导出</el-button
  122. >
  123. </el-col>
  124. <right-toolbar
  125. :showSearch.sync="showSearch"
  126. @queryTable="getList"
  127. ></right-toolbar>
  128. </el-row>
  129. <el-table
  130. v-loading="loading"
  131. :data="taskList"
  132. @selection-change="handleSelectionChange"
  133. >
  134. <el-table-column type="selection" width="55" align="center" />
  135. <el-table-column label="业务ID" align="center" prop="bizId" />
  136. <el-table-column label="任务名称" align="center" prop="bizName" />
  137. <el-table-column label="状态" align="center" prop="status">
  138. <template slot-scope="scope">
  139. <dict-tag
  140. :options="dict.type.uavps_task_status"
  141. :value="scope.row.status"
  142. />
  143. </template>
  144. </el-table-column>
  145. <el-table-column
  146. label="开始时间"
  147. align="center"
  148. prop="startTime"
  149. width="180"
  150. >
  151. <template slot-scope="scope">
  152. <span>{{
  153. parseTime(scope.row.startTime, "{y}-{m}-{d} {h}:{m}:{s}")
  154. }}</span>
  155. </template>
  156. </el-table-column>
  157. <el-table-column
  158. label="结束时间"
  159. align="center"
  160. prop="endTime"
  161. width="180"
  162. >
  163. <template slot-scope="scope">
  164. <span>{{
  165. parseTime(scope.row.endTime, "{y}-{m}-{d} {h}:{m}:{s}")
  166. }}</span>
  167. </template>
  168. </el-table-column>
  169. <el-table-column
  170. label="创建时间"
  171. align="center"
  172. prop="createTime"
  173. width="180"
  174. >
  175. <template slot-scope="scope">
  176. <span>{{
  177. parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{m}:{s}")
  178. }}</span>
  179. </template>
  180. </el-table-column>
  181. <el-table-column
  182. label="更新时间"
  183. align="center"
  184. prop="updateTime"
  185. width="180"
  186. >
  187. <template slot-scope="scope">
  188. <span>{{
  189. parseTime(scope.row.updateTime, "{y}-{m}-{d} {h}:{m}:{s}")
  190. }}</span>
  191. </template>
  192. </el-table-column>
  193. <el-table-column
  194. label="操作"
  195. align="center"
  196. class-name="small-padding fixed-width"
  197. >
  198. <template slot-scope="scope">
  199. <el-button
  200. v-hasPermi="['system:task:edit']"
  201. size="mini"
  202. type="text"
  203. icon="el-icon-s-operation"
  204. @click="handleRun(scope.row)"
  205. >运行</el-button
  206. >
  207. <el-button
  208. size="mini"
  209. type="text"
  210. icon="el-icon-view"
  211. @click="handlePlayback(scope.row)"
  212. v-hasPermi="['uavps:parameter:run']"
  213. :disabled="scope.row.status == '1'"
  214. >回放</el-button
  215. >
  216. <el-button
  217. size="mini"
  218. type="text"
  219. icon="el-icon-edit"
  220. @click="handleUpdate(scope.row)"
  221. v-hasPermi="['system:task:edit']"
  222. >修改</el-button
  223. >
  224. <el-button
  225. size="mini"
  226. type="text"
  227. icon="el-icon-delete"
  228. @click="handleDelete(scope.row)"
  229. v-hasPermi="['system:task:remove']"
  230. >删除</el-button
  231. >
  232. </template>
  233. </el-table-column>
  234. </el-table>
  235. <pagination
  236. v-show="total > 0"
  237. :total="total"
  238. :page.sync="queryParams.pageNum"
  239. :limit.sync="queryParams.pageSize"
  240. @pagination="getList"
  241. />
  242. <!-- 添加或修改算法任务对话框 -->
  243. <el-dialog
  244. :title="title"
  245. :visible.sync="open"
  246. width="75%"
  247. :close-on-click-modal="false"
  248. append-to-body
  249. :before-close="cancel"
  250. >
  251. <el-form
  252. ref="form"
  253. :inline="true"
  254. :model="form"
  255. :rules="rules"
  256. label-width="120px"
  257. >
  258. <el-row>
  259. <el-form-item label="任务名称" prop="bizName">
  260. <el-input v-model="form.bizName" placeholder="请输入任务名称" />
  261. </el-form-item>
  262. <el-form-item label="编队类型">
  263. <el-select
  264. v-model="form.multiTarget"
  265. @change="typeChange"
  266. placeholder="请选择"
  267. :disabled="form.multiTarget == '1' || title == '修改任务数据'"
  268. >
  269. <el-option
  270. v-for="item in formationTypeList"
  271. :key="item.value"
  272. :label="item.label"
  273. :value="item.value"
  274. >
  275. </el-option>
  276. </el-select>
  277. </el-form-item>
  278. <el-form-item label="智能算法" prop="smartAlgorithm">
  279. <el-checkbox
  280. v-model="form.smartAlgorithm"
  281. :true-label="'1'"
  282. :false-label="'0'"
  283. ></el-checkbox>
  284. </el-form-item>
  285. </el-row>
  286. <el-row>
  287. <el-form-item label="噪声类型">
  288. <el-select v-model="form.noiseType" placeholder="请选择">
  289. <el-option
  290. v-for="item in noiseTypeList"
  291. :key="item.value"
  292. :label="item.label"
  293. :value="item.value"
  294. >
  295. </el-option>
  296. </el-select>
  297. </el-form-item>
  298. <el-form-item label="噪声方差" prop="noiseVariance">
  299. <el-input
  300. v-model="form.noiseVariance"
  301. placeholder="请输入噪声方差"
  302. />
  303. </el-form-item>
  304. <el-form-item label="噪声均值" prop="noiseMean">
  305. <el-input v-model="form.noiseMean" placeholder="请输入噪声均值" />
  306. </el-form-item>
  307. </el-row>
  308. <el-divider content-position="left">平台无人机</el-divider>
  309. <el-form-item label="经度" prop="platformUav.longitude">
  310. <el-input
  311. v-model="form.platformUav.longitude"
  312. placeholder="请输入经度"
  313. />
  314. </el-form-item>
  315. <el-form-item label="纬度" prop="platformUav.latitude">
  316. <el-input
  317. v-model="form.platformUav.latitude"
  318. placeholder="请输入纬度"
  319. />
  320. </el-form-item>
  321. <el-form-item label="海拔" prop="platformUav.altitude">
  322. <el-input
  323. v-model="form.platformUav.altitude"
  324. placeholder="请输入海拔"
  325. />
  326. </el-form-item>
  327. <el-form-item label="东向速度" prop="platformUav.eastSpeed">
  328. <el-input
  329. v-model="form.platformUav.eastSpeed"
  330. placeholder="请输入东向速度"
  331. />
  332. </el-form-item>
  333. <el-form-item label="北向速度" prop="platformUav.northSpeed">
  334. <el-input
  335. v-model="form.platformUav.northSpeed"
  336. placeholder="请输入北向速度"
  337. />
  338. </el-form-item>
  339. <el-form-item label="天向速度" prop="platformUav.skySpeed">
  340. <el-input
  341. v-model="form.platformUav.skySpeed"
  342. placeholder="请输入天向速度"
  343. />
  344. </el-form-item>
  345. <div v-if="form.multiTarget == '1'">
  346. <el-divider content-position="left">固定编队无人机</el-divider>
  347. <el-form-item
  348. label="飞机数量"
  349. prop="fixedMultiTargetFormation.targetTotal"
  350. >
  351. <el-input-number
  352. v-model="form.fixedMultiTargetFormation.targetTotal"
  353. :min="1"
  354. :max="50"
  355. label="请输入飞机数量"
  356. ></el-input-number>
  357. </el-form-item>
  358. <el-form-item label="经度" prop="fixedMultiTargetFormation.longitude">
  359. <el-input
  360. v-model="form.fixedMultiTargetFormation.longitude"
  361. placeholder="请输入经度"
  362. />
  363. </el-form-item>
  364. <el-form-item label="纬度" prop="fixedMultiTargetFormation.latitude">
  365. <el-input
  366. v-model="form.fixedMultiTargetFormation.latitude"
  367. placeholder="请输入纬度"
  368. />
  369. </el-form-item>
  370. <el-form-item label="海拔" prop="fixedMultiTargetFormation.altitude">
  371. <el-input
  372. v-model="form.fixedMultiTargetFormation.altitude"
  373. placeholder="请输入海拔"
  374. />
  375. </el-form-item>
  376. <el-form-item
  377. label="东向速度"
  378. prop="fixedMultiTargetFormation.eastSpeed"
  379. >
  380. <el-input
  381. v-model="form.fixedMultiTargetFormation.eastSpeed"
  382. placeholder="请输入东向速度"
  383. />
  384. </el-form-item>
  385. <el-form-item
  386. label="北向速度"
  387. prop="fixedMultiTargetFormation.northSpeed"
  388. >
  389. <el-input
  390. v-model="form.fixedMultiTargetFormation.northSpeed"
  391. placeholder="请输入北向速度"
  392. />
  393. </el-form-item>
  394. <el-form-item
  395. label="天向速度"
  396. prop="fixedMultiTargetFormation.skySpeed"
  397. >
  398. <el-input
  399. v-model="form.fixedMultiTargetFormation.skySpeed"
  400. placeholder="请输入天向速度"
  401. />
  402. </el-form-item>
  403. </div>
  404. <div v-show="form.multiTarget == '2'" style="width: 100%">
  405. <el-divider content-position="left">自定义编队无人机</el-divider>
  406. <div style="width: 100%">
  407. <el-form
  408. ref="canvasInfoFormRef"
  409. :inline="true"
  410. :model="canvasInfo"
  411. :rules="infoRules"
  412. class="demo-form-inline"
  413. :disabled="submitInfoFlag"
  414. v-show="this.title == '修改任务数据' ? false : true"
  415. >
  416. <el-form-item label="中心点-经度" prop="centerLongitude">
  417. <el-input
  418. v-model="canvasInfo.centerLongitude"
  419. placeholder="中心点-经度"
  420. ></el-input>
  421. </el-form-item>
  422. <el-form-item label="中心点-纬度" prop="centerLatitude">
  423. <el-input
  424. v-model="canvasInfo.centerLatitude"
  425. placeholder="中心点-纬度"
  426. ></el-input>
  427. </el-form-item>
  428. <el-form-item label="边长距离" prop="lengthKm">
  429. <el-input v-model="canvasInfo.lengthKm" placeholder="边长距离">
  430. <template slot="append">km</template>
  431. </el-input>
  432. </el-form-item>
  433. <el-form-item>
  434. <el-button type="primary" @click="canvasInfoSubmit"
  435. >确 定</el-button
  436. >
  437. </el-form-item>
  438. </el-form>
  439. </div>
  440. <div
  441. style="
  442. width: 100%;
  443. height: 800px;
  444. display: flex;
  445. justify-content: space-evenly;
  446. "
  447. >
  448. <div
  449. ref="pixiCanvas"
  450. class="left"
  451. style="width: 800px; position: relative; height: 800px"
  452. >
  453. <div style="position: absolute; top: 5px; left: 5px">
  454. <span>经度:{{ nowlongitude }}</span
  455. ><br />
  456. <span>纬度:{{ nowlatitude }}</span>
  457. </div>
  458. </div>
  459. <div class="right" style="width: 25%; height: 800px">
  460. <el-divider content-position="left">编队信息</el-divider>
  461. <div style="overflow-y: scroll; width: 100%; height: 750px">
  462. <li
  463. v-for="(item, index) in formationInfoList"
  464. :key="index"
  465. @dblclick="editFormationInfo(item)"
  466. >
  467. <div style="cursor: pointer">
  468. <div>
  469. <span>编号:&nbsp;&nbsp;</span
  470. ><label>{{ item.number }}</label>
  471. </div>
  472. <div>
  473. <span>经度:&nbsp;&nbsp;</span
  474. ><label>{{ item.longitude }}</label>
  475. </div>
  476. <div>
  477. <span>纬度:&nbsp;&nbsp;</span
  478. ><label>{{ item.latitude }}</label>
  479. </div>
  480. <div>
  481. <span>海拔:&nbsp;&nbsp;</span
  482. ><label>{{ item.altitude }}</label>
  483. </div>
  484. <div>
  485. <span>东向速度:&nbsp;&nbsp;</span
  486. ><label>{{ item.eastSpeed }}(m/s)</label>
  487. </div>
  488. <div>
  489. <span>北向速度:&nbsp;&nbsp;</span
  490. ><label>{{ item.northSpeed }}(m/s)</label>
  491. </div>
  492. <div>
  493. <span>天向速度:&nbsp;&nbsp;</span
  494. ><label>{{ item.skySpeed }}(m/s)</label>
  495. </div>
  496. <div>
  497. <span>航线类型:&nbsp;&nbsp;</span
  498. ><label>{{ item.flightPathType }}(m/s)</label>
  499. </div>
  500. <div>
  501. <span>噪音:&nbsp;&nbsp;</span
  502. ><label>{{ item.positionNoise }}(m/s)</label>
  503. </div>
  504. </div>
  505. </li>
  506. </div>
  507. </div>
  508. </div>
  509. </div>
  510. </el-form>
  511. <div slot="footer" class="dialog-footer">
  512. <el-button type="primary" @click="submitForm">确 定</el-button>
  513. <el-button @click="cancel">取 消</el-button>
  514. </div>
  515. </el-dialog>
  516. <el-drawer
  517. title="设置参数"
  518. size="20%"
  519. :visible.sync="drawerOpen"
  520. direction="rtl"
  521. :before-close="drawerClose"
  522. :show-close="false"
  523. :wrapperClosable="false"
  524. >
  525. <div class="demo-drawer__content" style="width: 80%; margin-left: 20px">
  526. <el-form
  527. label-width="80px"
  528. ref="formationInfoRef"
  529. :rules="formationInfoRules"
  530. :model="formationInfoForm"
  531. >
  532. <el-form-item label="编号" prop="number">
  533. <el-input
  534. v-model="formationInfoForm.number"
  535. placeholder="请输入编号"
  536. :disabled="this.addOrUpdate == 'update'"
  537. />
  538. </el-form-item>
  539. <el-form-item label="经度" prop="longitude">
  540. <el-input
  541. v-model="formationInfoForm.longitude"
  542. oninput="value=value.replace(/[^\d.]/g,'')"
  543. placeholder="请输入经度"
  544. />
  545. </el-form-item>
  546. <el-form-item label="纬度" prop="latitude">
  547. <el-input
  548. v-model="formationInfoForm.latitude"
  549. oninput="value=value.replace(/[^\d.]/g,'')"
  550. placeholder="请输入纬度"
  551. />
  552. </el-form-item>
  553. <el-form-item label="海拔" prop="altitude">
  554. <el-input
  555. v-model="formationInfoForm.altitude"
  556. placeholder="请输入海拔"
  557. />
  558. </el-form-item>
  559. <el-form-item label="东向速度" prop="eastSpeed">
  560. <el-input
  561. v-model="formationInfoForm.eastSpeed"
  562. placeholder="请输入东向速度"
  563. >
  564. <template slot="append">m/s</template>
  565. </el-input>
  566. </el-form-item>
  567. <el-form-item label="北向速度" prop="northSpeed">
  568. <el-input
  569. v-model="formationInfoForm.northSpeed"
  570. placeholder="请输入北向速度"
  571. >
  572. <template slot="append">m/s</template>
  573. </el-input>
  574. </el-form-item>
  575. <el-form-item label="天向速度" prop="skySpeed">
  576. <el-input
  577. v-model="formationInfoForm.skySpeed"
  578. placeholder="请输入天向速度"
  579. >
  580. <template slot="append">m/s</template>
  581. </el-input>
  582. </el-form-item>
  583. <el-form-item label="航线类型" prop="flightPathType">
  584. <el-select
  585. v-model="formationInfoForm.flightPathType"
  586. placeholder="航线类型"
  587. >
  588. <el-option label="直线" value="直线"></el-option>
  589. </el-select>
  590. </el-form-item>
  591. <el-form-item label="噪声" prop="positionNoise">
  592. <el-input
  593. v-model="formationInfoForm.positionNoise"
  594. placeholder="请输入噪声"
  595. />
  596. </el-form-item>
  597. </el-form>
  598. <div class="demo-drawer__footer">
  599. <el-button type="danger" @click="canvasInfoDel">删 除</el-button>
  600. <!-- :disabled="this.addOrUpdate == 'add'" -->
  601. <el-button type="primary" @click="handleDrawerSubmit"
  602. >确 定</el-button
  603. >
  604. </div>
  605. <ul style="color: rgb(125 121 121)">
  606. <li>
  607. <span class="info">左上角:</span><br />
  608. 经度:{{ this.siJiaoInfo.leftTop.lng }}<br />
  609. 纬度:{{ this.siJiaoInfo.leftTop.lat }}
  610. </li>
  611. <li>
  612. <span class="info">右上角:</span><br />
  613. 经度:{{ this.siJiaoInfo.rightTop.lng }}<br />纬度:{{
  614. this.siJiaoInfo.rightTop.lat
  615. }}
  616. </li>
  617. <li>
  618. <span class="info">左下角:</span><br />
  619. 经度:{{ this.siJiaoInfo.leftBottom.lng }}<br />纬度:{{
  620. this.siJiaoInfo.leftBottom.lat
  621. }}
  622. </li>
  623. <li>
  624. <span class="info">右下角:</span><br />
  625. 经度:{{ this.siJiaoInfo.rightBottom.lng }}<br />纬度:{{
  626. this.siJiaoInfo.rightBottom.lat
  627. }}
  628. </li>
  629. </ul>
  630. </div>
  631. </el-drawer>
  632. <!-- 飞行轨迹展示对话框 -->
  633. <el-dialog
  634. :title="title"
  635. :visible.sync="showTrajectory"
  636. @opened="initTrajectory"
  637. :before-close="closeDialog"
  638. :fullscreen="true"
  639. append-to-body
  640. destroy-on-close
  641. :close-on-click-modal="false"
  642. >
  643. <div class="container">
  644. <div>
  645. <el-row type="flex" align="middle" style="height: 40px">
  646. <el-col :span="24">
  647. <el-button @click="handleCenter" type="primary">居中</el-button>
  648. <el-button
  649. v-if="!isUpdateView"
  650. @click="transFormation"
  651. type="warning"
  652. >转换队形</el-button
  653. >
  654. <el-button v-if="!isUpdateView" @click="endHandle" type="danger"
  655. >结束</el-button
  656. >
  657. <div class="distance">
  658. <el-select
  659. v-model="firstAir"
  660. @change="handleFirstAir"
  661. placeholder="请选择"
  662. style="width: 180px"
  663. >
  664. <el-option
  665. v-for="item in allSpriteInfo"
  666. :key="item.id"
  667. :label="item.label"
  668. :value="item.id"
  669. >
  670. </el-option>
  671. </el-select>
  672. ---&gt;
  673. <el-select
  674. v-model="secondAir"
  675. @change="handleSecondAir"
  676. placeholder="请选择"
  677. style="width: 180px"
  678. :disabled="!firstAir"
  679. >
  680. <el-option
  681. v-for="item in allSpriteInfo"
  682. :key="item.id"
  683. :label="item.label"
  684. :value="item.id"
  685. >
  686. </el-option>
  687. </el-select>
  688. =
  689. <el-input
  690. placeholder="距离"
  691. v-model="airDistance"
  692. :disabled="true"
  693. style="width: 220px"
  694. >
  695. <template slot="prepend">距离</template>
  696. <template slot="append">m</template>
  697. </el-input>
  698. --
  699. <el-input
  700. placeholder="夹角"
  701. v-model="airAngle"
  702. :disabled="true"
  703. style="width: 220px"
  704. >
  705. <template slot="prepend">夹角</template>
  706. </el-input>
  707. </div>
  708. <el-progress
  709. style="width: 450px; display: inline-block"
  710. :text-inside="true"
  711. :stroke-width="26"
  712. v-if="isUpdateView"
  713. :percentage="percentage > 100 ? 100 : percentage"
  714. ></el-progress>
  715. </el-col>
  716. </el-row>
  717. <el-row>
  718. <div ref="pixiContainer" class="pixi-container"></div>
  719. </el-row>
  720. </div>
  721. <div class="right-board">
  722. <div style="float: right">
  723. <el-link type="primary" @click="showConfig">显示配置</el-link>
  724. </div>
  725. <el-divider content-position="left">隐藏列表</el-divider>
  726. <div style="height: 30px">
  727. <div style="margin-left: 10px" v-if="hideList.length !== 0">
  728. <el-tag
  729. v-for="item in hideList"
  730. @click="showSprite(item)"
  731. style="cursor: pointer; margin-left: 5px"
  732. >{{ item }}</el-tag
  733. >
  734. </div>
  735. <div style="margin-left: 10px" v-else>暂无</div>
  736. </div>
  737. <el-divider content-position="left">平台无人机</el-divider>
  738. <div style="height: 190px; overflow: auto">
  739. <div v-if="configList.includes('经度')">
  740. <span>经度:&nbsp;&nbsp;</span
  741. ><label>{{ platformInfo.longitude }}</label>
  742. </div>
  743. <div v-if="configList.includes('纬度')">
  744. <span>纬度:&nbsp;&nbsp;</span
  745. ><label>{{ platformInfo.latitude }}</label>
  746. </div>
  747. <div v-if="configList.includes('海拔')">
  748. <span>海拔:&nbsp;&nbsp;</span
  749. ><label>{{ platformInfo.altitude }}m</label>
  750. </div>
  751. <div v-if="configList.includes('东向速度')">
  752. <span>东向速度:&nbsp;&nbsp;</span
  753. ><label>{{ platformInfo.eastSpeed }}m/s</label>
  754. </div>
  755. <div v-if="configList.includes('北向速度')">
  756. <span>北向速度:&nbsp;&nbsp;</span
  757. ><label>{{ platformInfo.northSpeed }}m/s</label>
  758. </div>
  759. <div v-if="configList.includes('天向速度')">
  760. <span>天向速度:&nbsp;&nbsp;</span
  761. ><label>{{ platformInfo.skySpeed }}m/s</label>
  762. </div>
  763. <div>
  764. <span>方位角:&nbsp;&nbsp;</span>
  765. <label>{{ platformInfo.azimuthAngle }}</label>
  766. </div>
  767. <div>
  768. <span>目标飞机:&nbsp;&nbsp;</span>
  769. <label>{{ platformInfo.targetAircraftNumber }}</label>
  770. </div>
  771. <div>
  772. <span>距离目标飞机距离:&nbsp;&nbsp;</span>
  773. <label>{{ platformInfo.distance }}m</label>
  774. </div>
  775. <div>
  776. <span>与目标飞机夹角:&nbsp;&nbsp;</span>
  777. <label>{{ platformInfo.bearing }}</label>
  778. </div>
  779. </div>
  780. <el-divider content-position="left">目标无人机群</el-divider>
  781. <div style="height: 480px; overflow: auto">
  782. <li v-for="(item, key) in targetLblAry" :key="key" class="item">
  783. <div
  784. v-if="!hideList.includes(item.id)"
  785. @click="showSprite(item.id)"
  786. style="cursor: pointer"
  787. >
  788. <div>
  789. <span>编号:&nbsp;&nbsp;</span><label>{{ item.id }}</label>
  790. <!-- <span style="margin-left: 10px" v-if="hideList.includes(item.id)"
  791. >( 隐 藏 )</span
  792. > -->
  793. </div>
  794. <div v-if="configList.includes('经度')">
  795. <span>经度:&nbsp;&nbsp;</span
  796. ><label>{{ item.longitude }}</label>
  797. </div>
  798. <div v-if="configList.includes('纬度')">
  799. <span>纬度:&nbsp;&nbsp;</span
  800. ><label>{{ item.latitude }}</label>
  801. </div>
  802. <div v-if="configList.includes('海拔')">
  803. <span>海拔:&nbsp;&nbsp;</span
  804. ><label>{{ item.altitude }}</label>
  805. </div>
  806. <div v-if="configList.includes('东向速度')">
  807. <span>东向速度:&nbsp;&nbsp;</span
  808. ><label>{{ item.eastSpeed }}</label>
  809. </div>
  810. <div v-if="configList.includes('北向速度')">
  811. <span>北向速度:&nbsp;&nbsp;</span
  812. ><label>{{ item.northSpeed }}</label>
  813. </div>
  814. <div v-if="configList.includes('天向速度')">
  815. <span>天向速度:&nbsp;&nbsp;</span
  816. ><label>{{ item.skySpeed }}</label>
  817. </div>
  818. </div>
  819. </li>
  820. </div>
  821. </div>
  822. </div>
  823. </el-dialog>
  824. <!-- 显示配置对话框 -->
  825. <el-dialog
  826. title="显示配置"
  827. :close-on-click-modal="false"
  828. :visible.sync="showConfigVisible"
  829. width="30%"
  830. >
  831. <el-checkbox-group
  832. v-model="showConfigList"
  833. style="display: flex; flex-direction: column"
  834. >
  835. <el-checkbox
  836. v-for="item in configData"
  837. :label="item.label"
  838. ></el-checkbox>
  839. </el-checkbox-group>
  840. <span slot="footer" class="dialog-footer">
  841. <el-button @click="showConfigVisible = false">取 消</el-button>
  842. <el-button type="primary" @click="selectConfigSubmit">确 定</el-button>
  843. </span>
  844. </el-dialog>
  845. </div>
  846. </template>
  847. <script>
  848. import {
  849. listTask,
  850. getTask,
  851. delTask,
  852. addTask,
  853. updateTask,
  854. } from "@/api/system/task";
  855. import * as PIXI from "pixi.js";
  856. import platformPNG from "../../textures/platform.png";
  857. import uavTarPNG from "../../textures/uav-tar.png";
  858. import noisePNG from "../../textures/noise.png";
  859. export default {
  860. name: "Task",
  861. dicts: [
  862. "uavps_task_status",
  863. "uavps_target_formation_type",
  864. "uavps_task_nose_type",
  865. ],
  866. data() {
  867. return {
  868. // 遮罩层
  869. loading: true,
  870. // 选中数组
  871. ids: [],
  872. // 非单个禁用
  873. single: true,
  874. // 非多个禁用
  875. multiple: true,
  876. // 显示搜索条件
  877. showSearch: true,
  878. // 总条数
  879. total: 0,
  880. // 任务数据表格数据
  881. taskList: [],
  882. // 弹出层标题
  883. title: "",
  884. // 是否显示弹出层
  885. open: false,
  886. // 噪声数据文件路径时间范围
  887. daterangeCreateTime: [],
  888. // 查询参数
  889. queryParams: {
  890. pageNum: 1,
  891. pageSize: 10,
  892. bizName: null,
  893. status: null,
  894. startTime: null,
  895. endTime: null,
  896. createTime: null,
  897. },
  898. // 表单参数
  899. form: {
  900. platformUav: {},
  901. fixedMultiTargetFormation: {},
  902. },
  903. // 表单校验
  904. rules: {
  905. bizName: [
  906. { required: true, message: "任务名称不能为空", trigger: "blur" },
  907. ],
  908. multiTarget: [
  909. {
  910. required: true,
  911. message: "编队类型不能为空",
  912. trigger: "change",
  913. },
  914. ],
  915. noiseType: [
  916. {
  917. required: true,
  918. message: "噪声类型不能为空",
  919. trigger: "change",
  920. },
  921. ],
  922. noiseVariance: [
  923. { required: true, message: "噪声方差不能为空", trigger: "blur" },
  924. ],
  925. noiseMean: [
  926. { required: true, message: "噪声均值不能为空", trigger: "blur" },
  927. ],
  928. "platformUav.longitude": [
  929. {
  930. required: true,
  931. message: "平台无人机经度不能为空",
  932. trigger: "blur",
  933. },
  934. ],
  935. "platformUav.latitude": [
  936. {
  937. required: true,
  938. message: "平台无人机纬度不能为空",
  939. trigger: "blur",
  940. },
  941. ],
  942. "platformUav.altitude": [
  943. {
  944. required: true,
  945. message: "平台无人机海拔不能为空",
  946. trigger: "blur",
  947. },
  948. ],
  949. "platformUav.eastSpeed": [
  950. {
  951. required: true,
  952. message: "平台无人机东向速度不能为空",
  953. trigger: "blur",
  954. },
  955. ],
  956. "platformUav.northSpeed": [
  957. {
  958. required: true,
  959. message: "平台无人机北向速度不能为空",
  960. trigger: "blur",
  961. },
  962. ],
  963. "platformUav.skySpeed": [
  964. {
  965. required: true,
  966. message: "平台无人机天向速度不能为空",
  967. trigger: "blur",
  968. },
  969. ],
  970. "fixedMultiTargetFormation.targetTotal": [
  971. {
  972. required: true,
  973. message: "固定编队无人机飞机数量不能为空",
  974. trigger: "blur",
  975. },
  976. ],
  977. "fixedMultiTargetFormation.longitude": [
  978. {
  979. required: true,
  980. message: "固定编队无人机经度不能为空",
  981. trigger: "blur",
  982. },
  983. ],
  984. "fixedMultiTargetFormation.latitude": [
  985. {
  986. required: true,
  987. message: "固定编队无人机纬度不能为空",
  988. trigger: "blur",
  989. },
  990. ],
  991. "fixedMultiTargetFormation.altitude": [
  992. {
  993. required: true,
  994. message: "固定编队无人机海拔不能为空",
  995. trigger: "blur",
  996. },
  997. ],
  998. "fixedMultiTargetFormation.eastSpeed": [
  999. {
  1000. required: true,
  1001. message: "固定编队无人机东向速度不能为空",
  1002. trigger: "blur",
  1003. },
  1004. ],
  1005. "fixedMultiTargetFormation.northSpeed": [
  1006. {
  1007. required: true,
  1008. message: "固定编队无人机北向速度不能为空",
  1009. trigger: "blur",
  1010. },
  1011. ],
  1012. "fixedMultiTargetFormation.skySpeed": [
  1013. {
  1014. required: true,
  1015. message: "固定编队无人机天向速度不能为空",
  1016. trigger: "blur",
  1017. },
  1018. ],
  1019. },
  1020. infoRules: {
  1021. centerLongitude: [
  1022. { required: true, message: "经度不能为空", trigger: "blur" },
  1023. ],
  1024. centerLatitude: [
  1025. { required: true, message: "纬度不能为空", trigger: "blur" },
  1026. ],
  1027. lengthKm: [
  1028. { required: true, message: "边长距离不能为空", trigger: "blur" },
  1029. ],
  1030. },
  1031. formationInfoRules: {
  1032. number: [{ required: true, message: "编号不能为空", trigger: "blur" }],
  1033. longitude: [
  1034. { required: true, message: "经度不能为空", trigger: "blur" },
  1035. ],
  1036. latitude: [
  1037. { required: true, message: "纬度不能为空", trigger: "blur" },
  1038. ],
  1039. altitude: [
  1040. { required: true, message: "海拔不能为空", trigger: "blur" },
  1041. ],
  1042. eastSpeed: [
  1043. { required: true, message: "东向速度不能为空", trigger: "blur" },
  1044. ],
  1045. northSpeed: [
  1046. { required: true, message: "北向速度不能为空", trigger: "blur" },
  1047. ],
  1048. skySpeed: [
  1049. { required: true, message: "天向速度不能为空", trigger: "blur" },
  1050. ],
  1051. flightPathType: [
  1052. { required: true, message: "航线类型不能为空", trigger: "change" },
  1053. ],
  1054. positionNoise: [
  1055. { required: true, message: "噪音不能为空", trigger: "blur" },
  1056. ],
  1057. },
  1058. parameterId: 0,
  1059. // 是否显示轨迹
  1060. showTrajectory: false,
  1061. pixiApp: null,
  1062. container: null,
  1063. webSocket: null,
  1064. curLongitude: null,
  1065. curLatitude: null,
  1066. curAltitude: null,
  1067. targetTotal: null,
  1068. formationTypeList: [
  1069. {
  1070. value: "1",
  1071. label: "固定编队",
  1072. },
  1073. {
  1074. value: "2",
  1075. label: "自由编队",
  1076. },
  1077. ],
  1078. noiseTypeList: [
  1079. {
  1080. value: "1",
  1081. label: "高斯",
  1082. },
  1083. {
  1084. value: "2",
  1085. label: "瑞利",
  1086. },
  1087. ],
  1088. // 自定义编队信息
  1089. formationInfoList: [],
  1090. formationInfoForm: {
  1091. number: null,
  1092. longitude: null,
  1093. latitude: null,
  1094. altitude: null,
  1095. eastSpeed: null,
  1096. northSpeed: null,
  1097. skySpeed: null,
  1098. flightPathType: "直线",
  1099. positionNoise: null,
  1100. },
  1101. // 判断填写的编队信息是新增加的还是修改之前的
  1102. addOrUpdate: "add",
  1103. // 自定义编队的飞机信息集合
  1104. formationInfoAirDataList: [],
  1105. // 编辑编队信息的
  1106. formationAirPiXiApp: null,
  1107. // 设定的画布信息
  1108. canvasInfo: {
  1109. centerLongitude: null,
  1110. centerLatitude: null,
  1111. lengthKm: null,
  1112. },
  1113. // 编辑编队信息的画布
  1114. pixiCanvas: null,
  1115. // 是否提交了编队信息
  1116. submitInfoFlag: false,
  1117. // 鼠标当前的经纬度
  1118. nowlongitude: null,
  1119. nowlatitude: null,
  1120. targetDataR: {},
  1121. targetDataN: {},
  1122. platformData: [],
  1123. // 真实路径的集合
  1124. pathGraphicsList: {},
  1125. pathGraphicsPointList: {},
  1126. isUpdateView: false, // 是否开启实现追踪,在回放时开启
  1127. percentage: 0,
  1128. // 多久更新一次轨迹
  1129. updatePathFlag: 75,
  1130. cycleCount: 0, // 循环计数
  1131. updatePlatformPathFlag: 75,
  1132. platformCycleCount: 0,
  1133. showConfigVisible: false, //显示配置dialog
  1134. showConfigList: [], // 选择要显示的值
  1135. configList: ["经度", "纬度", "海拔", "东向速度", "北向速度", "天向速度"], // 显示的值
  1136. configData: [
  1137. // 可选择的值
  1138. { key: "longitude", label: "经度" },
  1139. { key: "latitude", label: "纬度" },
  1140. { key: "altitude", label: "海拔" },
  1141. { key: "eastSpeed", label: "东向速度" },
  1142. { key: "northSpeed", label: "北向速度" },
  1143. { key: "skySpeed", label: "天向速度" },
  1144. ],
  1145. hideList: [], // 当下隐藏的飞机数组
  1146. allId: [], // 所有飞机的id
  1147. changeNoList: [], // 需要改变的飞机编号,放有id和应该显示的编号
  1148. targetLblAry: {},
  1149. targetUavAry: [],
  1150. targetUavCircleR: {},
  1151. targetUavCircleN: {},
  1152. targetUavCircleNumR: {},
  1153. targetUavCircleNumN: {},
  1154. drawerOpen: false,
  1155. platformSprite: null, // 平台无人机
  1156. // 平台无人机显示的数据
  1157. platformInfo: {},
  1158. platformPathData: {}, //平台无人机位置
  1159. platformGraphics: null, //平台无人机轨迹
  1160. playbackTime: null, // 回放的时间
  1161. isClickGroup: false, // 是否点击了飞机,在计算两飞机距离时使用
  1162. firstLng: null, // 第一次点击的飞机的经纬度
  1163. firstLat: null,
  1164. airDistance: null, // 两点间距离
  1165. airAngle: null, // 两点间夹角
  1166. allSpriteInfo: [], // 所有飞机的信息
  1167. firstAir: "",
  1168. secondAir: "",
  1169. siJiaoInfo: {
  1170. leftTop: {},
  1171. rightTop: {},
  1172. leftBottom: {},
  1173. rightBottom: {},
  1174. }, // 自定义画布四个角的经纬度信息
  1175. trackGraphicsLine: null,
  1176. // 是否持续更新选择的两个飞机的距离和夹角
  1177. twoAirInfo: false,
  1178. };
  1179. },
  1180. created() {
  1181. this.getList();
  1182. },
  1183. methods: {
  1184. /** 查询任务数据列表 */
  1185. getList() {
  1186. this.loading = true;
  1187. this.queryParams.params = {};
  1188. if (null != this.daterangeCreateTime && "" != this.daterangeCreateTime) {
  1189. this.queryParams.params["beginCreateTime"] =
  1190. this.daterangeCreateTime[0];
  1191. this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
  1192. }
  1193. listTask(this.queryParams).then((response) => {
  1194. this.taskList = response.rows;
  1195. this.total = response.total;
  1196. this.loading = false;
  1197. });
  1198. },
  1199. // 取消按钮
  1200. cancel() {
  1201. this.open = false;
  1202. this.reset();
  1203. this.closeUpdataDialog();
  1204. },
  1205. // 表单重置
  1206. reset() {
  1207. this.form = {
  1208. bizId: null,
  1209. bizName: null,
  1210. bizType: null,
  1211. multiTarget: null,
  1212. noiseType: null,
  1213. noiseVariance: null,
  1214. noiseMean: null,
  1215. platformUav: {},
  1216. smartAlgorithm: null,
  1217. fixedMultiTargetFormation: {},
  1218. customizedMultiTargetFormation: null,
  1219. status: null,
  1220. startTime: null,
  1221. endTime: null,
  1222. filePath: null,
  1223. createBy: null,
  1224. createTime: null,
  1225. updateBy: null,
  1226. updateTime: null,
  1227. };
  1228. this.siJiaoInfo = {
  1229. leftTop: {},
  1230. rightTop: {},
  1231. leftBottom: {},
  1232. rightBottom: {},
  1233. };
  1234. this.resetForm("form");
  1235. },
  1236. /** 搜索按钮操作 */
  1237. handleQuery() {
  1238. this.queryParams.pageNum = 1;
  1239. this.getList();
  1240. },
  1241. /** 重置按钮操作 */
  1242. resetQuery() {
  1243. this.daterangeCreateTime = [];
  1244. this.resetForm("queryForm");
  1245. this.handleQuery();
  1246. },
  1247. // 多选框选中数据
  1248. handleSelectionChange(selection) {
  1249. this.ids = selection.map((item) => item.bizId);
  1250. this.single = selection.length !== 1;
  1251. this.multiple = !selection.length;
  1252. },
  1253. /** 新增按钮操作 */
  1254. handleAdd() {
  1255. this.reset();
  1256. this.open = true;
  1257. this.title = "添加任务数据";
  1258. },
  1259. /** 修改按钮操作 */
  1260. handleUpdate(row) {
  1261. this.reset();
  1262. const bizId = row.bizId || this.ids;
  1263. getTask(bizId).then((response) => {
  1264. this.form = response.data;
  1265. if (this.form.multiTarget == "2") {
  1266. const info = this.form.customizedMultiTargetFormation[0];
  1267. this.formationInfoList = this.form.customizedMultiTargetFormation;
  1268. //自由编队
  1269. this.$nextTick(() => {
  1270. this.initFormationApp();
  1271. this.canvasInfo = {
  1272. centerLongitude: info.longitude,
  1273. centerLatitude: info.latitude,
  1274. lengthKm: 20,
  1275. };
  1276. this.airPosShow(
  1277. this.form.customizedMultiTargetFormation,
  1278. "fixedWing"
  1279. );
  1280. const leftTop = this.calculateLatLng(0, 800);
  1281. const rightTop = this.calculateLatLng(800, 0);
  1282. const leftBottom = this.calculateLatLng(0, 800);
  1283. const rightBottom = this.calculateLatLng(800, 800);
  1284. this.siJiaoInfo = { leftTop, rightTop, leftBottom, rightBottom };
  1285. this.addAirSprite();
  1286. });
  1287. }
  1288. this.title = "修改任务数据";
  1289. this.open = true;
  1290. });
  1291. },
  1292. // 根据后端返回的自定义编队信息,显示飞机位置
  1293. airPosShow(info, type) {
  1294. let texture = new PIXI.Texture.from(uavTarPNG);
  1295. info.forEach((item) => {
  1296. const { x, y } = this.calculateXY(item.longitude, item.latitude);
  1297. const sprite = new PIXI.Sprite(texture); // 示例飞机图标
  1298. sprite.anchor.set(0.5, 0.5);
  1299. sprite.scale.set(0.04);
  1300. sprite.x = x;
  1301. sprite.y = y;
  1302. sprite.id = item.number;
  1303. this.formationAirPiXiApp.stage.addChild(sprite);
  1304. });
  1305. },
  1306. calculateXY(longitude, latitude) {
  1307. // 计算每像素对应的实际距离
  1308. const kmPerPixel = this.canvasInfo.lengthKm / this.pixiCanvas.clientWidth;
  1309. // 经纬度差值转实际距离
  1310. const deltaY = (latitude - this.canvasInfo.centerLatitude) * 111;
  1311. const deltaX =
  1312. (longitude - this.canvasInfo.centerLongitude) *
  1313. 111 *
  1314. Math.cos((this.canvasInfo.centerLatitude * Math.PI) / 180);
  1315. // 实际距离转像素偏移量
  1316. const offsetX = deltaX / kmPerPixel;
  1317. const offsetY = deltaY / kmPerPixel;
  1318. // 像素坐标
  1319. const x = this.pixiCanvas.clientWidth / 2 + offsetX;
  1320. const y = this.pixiCanvas.clientHeight / 2 - offsetY;
  1321. return { x, y };
  1322. },
  1323. // 选择第一个飞机
  1324. handleFirstAir(val) {
  1325. this.secondAir = null;
  1326. this.twoAirInfo = false;
  1327. },
  1328. // 选择第二个飞机
  1329. handleSecondAir(val) {
  1330. this.twoAirInfo = true;
  1331. // let firstLng, firstLat, secondLng, secondLat;
  1332. // if (this.firstAir == 100001) {
  1333. // firstLng = this.platformInfo.longitude;
  1334. // firstLat = this.platformInfo.latitude;
  1335. // } else {
  1336. // firstLng = this.targetLblAry[`${this.firstAir}`].longitude;
  1337. // firstLat = this.targetLblAry[`${this.firstAir}`].latitude;
  1338. // }
  1339. // if (this.secondAir == 100001) {
  1340. // secondLng = this.platformInfo.longitude;
  1341. // secondLat = this.platformInfo.latitude;
  1342. // } else {
  1343. // secondLng = this.targetLblAry[`${val}`].longitude;
  1344. // secondLat = this.targetLblAry[`${val}`].latitude;
  1345. // }
  1346. // this.airDistance = this.calculateDistance(
  1347. // firstLat,
  1348. // firstLng,
  1349. // secondLat,
  1350. // secondLng
  1351. // );
  1352. },
  1353. /** 提交按钮 */
  1354. submitForm() {
  1355. this.$refs["form"].validate((valid) => {
  1356. if (valid) {
  1357. if (this.form.multiTarget == "2") {
  1358. this.form.customizedMultiTargetFormation = this.formationInfoList;
  1359. }
  1360. if (this.form.bizId != null) {
  1361. updateTask(this.form).then((response) => {
  1362. this.$modal.msgSuccess("修改成功");
  1363. this.open = false;
  1364. this.closeUpdataDialog();
  1365. this.getList();
  1366. });
  1367. } else {
  1368. addTask(this.form).then((response) => {
  1369. this.$modal.msgSuccess("新增成功");
  1370. this.open = false;
  1371. this.closeUpdataDialog();
  1372. this.getList();
  1373. });
  1374. }
  1375. }
  1376. });
  1377. },
  1378. /** 删除按钮操作 */
  1379. handleDelete(row) {
  1380. const bizIds = row.bizId || this.ids;
  1381. this.$modal
  1382. .confirm('是否确认删除任务数据编号为"' + bizIds + '"的数据项?')
  1383. .then(function () {
  1384. return delTask(bizIds);
  1385. })
  1386. .then(() => {
  1387. this.getList();
  1388. this.$modal.msgSuccess("删除成功");
  1389. })
  1390. .catch(() => {});
  1391. },
  1392. /** 导出按钮操作 */
  1393. handleExport() {
  1394. this.download(
  1395. "system/task/export",
  1396. {
  1397. ...this.queryParams,
  1398. },
  1399. `task_${new Date().getTime()}.xlsx`
  1400. );
  1401. },
  1402. showConfig() {
  1403. this.showConfigList = this.configList;
  1404. this.showConfigVisible = true;
  1405. },
  1406. selectConfigSubmit() {
  1407. this.configList = this.showConfigList;
  1408. this.showConfigVisible = false;
  1409. },
  1410. // 选择编队类型
  1411. typeChange(val) {
  1412. if (val == "2") {
  1413. this.$nextTick(() => {
  1414. this.$message.warning(`请先输入画布信息!`);
  1415. this.initFormationApp();
  1416. });
  1417. }
  1418. },
  1419. handleRun(row) {
  1420. this.parameterId = row.bizId || this.ids;
  1421. this.showTrajectory = true;
  1422. this.title = "飞行轨迹";
  1423. },
  1424. handlePlayback(row) {
  1425. this.parameterId = row.bizId || this.ids;
  1426. this.playbackTime = row.totalDuration;
  1427. this.title = "回放";
  1428. this.showTrajectory = true;
  1429. this.isUpdateView = true;
  1430. },
  1431. initWebSocket() {
  1432. const wsUri = "ws://127.0.0.1:8080/websocket/message";
  1433. this.webSocket = new WebSocket(wsUri);
  1434. const self = this;
  1435. this.webSocket.onopen = function (event) {
  1436. if (self.title == "回放") {
  1437. self.webSocket.send("REPLAY:" + self.parameterId);
  1438. } else {
  1439. self.webSocket.send("RUN:" + self.parameterId);
  1440. }
  1441. console.log("WebSocket连接成功!");
  1442. };
  1443. this.webSocket.onmessage = function (event) {
  1444. self.processMessage(JSON.parse(event.data));
  1445. };
  1446. this.webSocket.onclose = function (event) {
  1447. console.log("WebSocket连接断开!");
  1448. };
  1449. this.webSocket.onerror = function (event) {
  1450. console.log(
  1451. "WebSocket连接异常: " +
  1452. event.code +
  1453. " " +
  1454. event.reason +
  1455. " " +
  1456. event.wasClean
  1457. );
  1458. };
  1459. },
  1460. transFormation() {
  1461. let msg = "TRANSFORMATION:" + this.parameterId;
  1462. console.info("transFormation", msg);
  1463. this.webSocket.send(msg);
  1464. },
  1465. endHandle() {
  1466. let msg = "END:" + this.parameterId;
  1467. this.webSocket.send(msg);
  1468. this.showTrajectory = false;
  1469. this.loading = true;
  1470. setTimeout(() => {
  1471. this.getList();
  1472. }, 1500);
  1473. },
  1474. // 居中
  1475. handleCenter() {
  1476. const firstKey = Object.keys(this.targetDataR)[0];
  1477. // 获取到第一个飞机的位置
  1478. const posX = this.targetDataR[`${firstKey}`].x;
  1479. const posY = this.targetDataR[`${firstKey}`].y;
  1480. // 获取画布的中心点的位置
  1481. const centerX = this.container.clientWidth / 2;
  1482. const centerY = this.container.clientHeight / 2;
  1483. // 缩放的值
  1484. const scaleX = this.pixiApp.stage.scale.x || 1;
  1485. const scaleY = this.pixiApp.stage.scale.y || 1;
  1486. // 计算需要移动的距离
  1487. const offsetX = centerX - posX * scaleX;
  1488. const offsetY = centerY - posY * scaleY;
  1489. this.pixiApp.stage.position.set(offsetX, offsetY);
  1490. },
  1491. // 销毁画布信息
  1492. destroyTrajectory() {
  1493. if (this.webSocket) {
  1494. this.webSocket.close();
  1495. }
  1496. if (this.pixiApp) {
  1497. // 移除并销毁所有子对象
  1498. const removedChildren = this.pixiApp.stage.removeChildren(
  1499. 0,
  1500. this.pixiApp.stage.children.length
  1501. );
  1502. removedChildren.forEach((child) => {
  1503. child.destroy({
  1504. children: true,
  1505. texture: false,
  1506. baseTexture: false,
  1507. });
  1508. });
  1509. this.pixiApp.stage.removeChildren();
  1510. this.pixiApp.renderer.clear();
  1511. this.pixiApp.destroy(true);
  1512. this.pixiApp = null;
  1513. }
  1514. if (this.platformGraphics && this.platformGraphics.length) {
  1515. if (this.platformGraphics.parent) {
  1516. this.platformGraphics.parent.removeChild(this.platformGraphics);
  1517. }
  1518. this.platformGraphics.clear();
  1519. this.platformGraphics.destroy(true);
  1520. this.platformGraphics = null;
  1521. }
  1522. if (this.trackGraphicsLine && this.trackGraphicsLine.length) {
  1523. if (this.trackGraphicsLine.parent) {
  1524. this.trackGraphicsLine.parent.removeChild(this.trackGraphicsLine);
  1525. }
  1526. this.trackGraphicsLine.clear();
  1527. this.trackGraphicsLine.destroy(true);
  1528. this.trackGraphicsLine = null;
  1529. }
  1530. this.clearAllAry();
  1531. },
  1532. initTrajectory() {
  1533. // this.clearAllAry();
  1534. this.initPixi();
  1535. this.initWebSocket();
  1536. },
  1537. // 处理WebSocket消息
  1538. processMessage(data) {
  1539. if (this.title == "回放") {
  1540. this.percentage = Math.ceil((data.time / this.playbackTime) * 100);
  1541. }
  1542. // type:1平台无人机、2真实目标、3噪声目标
  1543. switch (data.type) {
  1544. case "1": //平台无人机
  1545. this.showPlatformAir(data);
  1546. break;
  1547. case "2": //真实目标 R真实数据
  1548. this.showTargetRAircraft(data, "R");
  1549. break;
  1550. case "3": //噪声目标 N噪声数据
  1551. this.showTargetNAircraft(data, "N");
  1552. break;
  1553. default:
  1554. break;
  1555. }
  1556. },
  1557. initPixi() {
  1558. this.container = this.$refs.pixiContainer;
  1559. if (!this.container) return;
  1560. // 创建Pixi应用
  1561. this.pixiApp = new PIXI.Application({
  1562. width: this.container.clientWidth,
  1563. height: this.container.clientHeight,
  1564. antialias: true,
  1565. transparent: false,
  1566. resolution: 1,
  1567. backgroundAlpha: 0,
  1568. });
  1569. this.pixiApp.renderer.backgroundColor = 0x66ccff;
  1570. this.container.appendChild(this.pixiApp.view);
  1571. // 添加视窗控制
  1572. this.enableViewControl(this.pixiApp);
  1573. // 增加追踪线
  1574. this.trackGraphicsLine = new PIXI.Graphics();
  1575. this.trackGraphicsLine.lineStyle(1, 0xf5f7fa, 1);
  1576. this.pixiApp.stage.addChild(this.trackGraphicsLine);
  1577. },
  1578. // 平台无人机展示
  1579. showPlatformAir(platformData) {
  1580. const data = platformData.platformAircraft;
  1581. if (this.platformData.length == 0) {
  1582. const platformTexture = new PIXI.Texture.from(platformPNG);
  1583. this.platformSprite = new PIXI.Sprite(platformTexture);
  1584. this.platformSprite.anchor.set(0.5, 0.5);
  1585. this.platformSprite.scale.set(0.1);
  1586. this.platformSprite.buttonMode = true; // 鼠标悬停时显示手型
  1587. this.platformSprite.interactive = true;
  1588. this.platformSprite.x = data.coordinateX;
  1589. this.platformSprite.y = data.coordinateY;
  1590. const label = "平台无人机";
  1591. this.allSpriteInfo.push({
  1592. id: 100001,
  1593. label,
  1594. });
  1595. this.platformData.push(this.platformSprite);
  1596. this.pixiApp.stage.addChild(this.platformSprite);
  1597. // 创建路径
  1598. this.platformGraphics = new PIXI.Graphics();
  1599. this.platformGraphics.lineStyle(1, 0x00ee00, 1);
  1600. const point = { x: this.platformSprite.x, y: this.platformSprite.y };
  1601. this.platformPathData = point;
  1602. this.platformInfo = data;
  1603. this.pixiApp.stage.addChild(this.platformGraphics);
  1604. } else {
  1605. this.platformSprite.x = data.coordinateX;
  1606. this.platformSprite.y = data.coordinateY;
  1607. this.platformSprite.rotation = data.azimuthAngle * (Math.PI / 180);
  1608. // this.platformSprite.rotation = Math.atan2(
  1609. // data.northSpeed,
  1610. // data.eastSpeed
  1611. // );
  1612. // this.platformInfo = data;
  1613. Object.assign(this.platformInfo, data);
  1614. if (this.platformCycleCount % this.updatePlatformPathFlag == 0) {
  1615. // 更新路径绘制
  1616. this.updatePlatformPath(this.platformSprite.x, this.platformSprite.y);
  1617. }
  1618. }
  1619. this.platformCycleCount++;
  1620. },
  1621. // 无人机集群 真实数据
  1622. showTargetRAircraft(data) {
  1623. const targetData = data.targetAircraft[0];
  1624. this.targetTotal = targetData.aircrafts.length;
  1625. let empty = Object.keys(this.targetDataR);
  1626. // 还没有真实数据时,先初始化数据。
  1627. if (empty.length == 0) {
  1628. for (let i = 0; i < this.targetTotal; i++) {
  1629. const label = "目标无人机" + targetData.aircrafts[i].aircraftNumber;
  1630. this.allSpriteInfo.push({
  1631. id: targetData.aircrafts[i].aircraftNumber,
  1632. label,
  1633. });
  1634. this.allId.push(targetData.aircrafts[i].aircraftNumber);
  1635. // (飞机相关数据,,是否为真实数据)
  1636. this.addAir(targetData.aircrafts[i], true);
  1637. // 绑定鼠标事件,飞机隐藏
  1638. // group.on("pointerdown", () => {
  1639. // this.hideSprite(group.id);
  1640. // });
  1641. }
  1642. } else {
  1643. // 每次更新
  1644. const newData = data.targetAircraft[0].aircrafts;
  1645. let isHide = false,
  1646. targetDataKeys,
  1647. flagCount = 0;
  1648. if (this.title == "回放") {
  1649. targetDataKeys = Object.keys(this.targetDataR);
  1650. const count = targetDataKeys.length;
  1651. // 当存的数据大于返回数据时,证明有数据隐藏
  1652. if (count > newData.length) {
  1653. isHide = true;
  1654. }
  1655. }
  1656. if (this.twoAirInfo) {
  1657. let firstLng, firstLat, secondLng, secondLat;
  1658. if (this.firstAir == 100001) {
  1659. firstLng = this.platformInfo.longitude;
  1660. firstLat = this.platformInfo.latitude;
  1661. } else {
  1662. firstLng = this.targetLblAry[`${this.firstAir}`].longitude;
  1663. firstLat = this.targetLblAry[`${this.firstAir}`].latitude;
  1664. }
  1665. if (this.secondAir == 100001) {
  1666. secondLng = this.platformInfo.longitude;
  1667. secondLat = this.platformInfo.latitude;
  1668. } else {
  1669. secondLng = this.targetLblAry[`${this.secondAir}`].longitude;
  1670. secondLat = this.targetLblAry[`${this.secondAir}`].latitude;
  1671. }
  1672. this.airDistance = this.calculateDistance(
  1673. firstLat,
  1674. firstLng,
  1675. secondLat,
  1676. secondLng
  1677. );
  1678. this.airAngle = this.calculateDistance(
  1679. firstLat,
  1680. firstLng,
  1681. secondLat,
  1682. secondLng
  1683. );
  1684. }
  1685. for (let i = 0; i < newData.length; i++) {
  1686. const id = newData[i].aircraftNumber;
  1687. const targetUav = this.targetDataR[`${id}`];
  1688. if (this.title == "回放" && isHide) {
  1689. targetDataKeys.splice(
  1690. targetDataKeys.findIndex((item) => item === id),
  1691. 1
  1692. );
  1693. flagCount++;
  1694. if (flagCount == newData.length) {
  1695. this.delHideAir(targetDataKeys);
  1696. }
  1697. }
  1698. if (targetUav) {
  1699. targetUav.x = newData[i].coordinateX;
  1700. targetUav.y = newData[i].coordinateY;
  1701. const eastSpeed = newData[i].eastSpeed;
  1702. const northSpeed = newData[i].northSpeed;
  1703. targetUav.rotation = Math.atan2(northSpeed, eastSpeed);
  1704. const cx = targetUav.x - 5;
  1705. const cy = targetUav.y - 3;
  1706. const circle = this.targetUavCircleR[`${id}`];
  1707. circle.x = cx;
  1708. circle.y = cy;
  1709. const text = this.targetUavCircleNumR[`${id}`];
  1710. text.x = cx;
  1711. text.y = cy;
  1712. if (this.platformInfo.targetAircraftNumber == id) {
  1713. this.trackGraphicsLine.clear();
  1714. this.trackGraphicsLine.lineStyle(1, 0xf5f7fa, 1);
  1715. const returnData = this.calculateRelativePosition(
  1716. this.platformInfo.latitude,
  1717. this.platformInfo.longitude,
  1718. 0,
  1719. newData[i].latitude,
  1720. newData[i].longitude,
  1721. 0
  1722. );
  1723. this.platformInfo.distance = Number(
  1724. returnData.distance.toFixed()
  1725. );
  1726. this.platformInfo.bearing = Number(returnData.bearing.toFixed(2));
  1727. this.trackGraphicsLine.moveTo(
  1728. this.platformInfo.coordinateX,
  1729. this.platformInfo.coordinateY
  1730. );
  1731. this.trackGraphicsLine.lineTo(
  1732. newData[i].coordinateX,
  1733. newData[i].coordinateY
  1734. );
  1735. }
  1736. } else {
  1737. this.addAir(newData[i], true);
  1738. const label = "目标无人机" + newData[i].aircraftNumber;
  1739. this.allSpriteInfo.push({
  1740. id: targetData.aircrafts[i].aircraftNumber,
  1741. label,
  1742. });
  1743. }
  1744. if (targetUav && this.cycleCount % this.updatePathFlag == 0) {
  1745. // 更新路径绘制
  1746. this.updatePath(id, targetUav.x, targetUav.y);
  1747. }
  1748. this.cycleCount++;
  1749. }
  1750. if (data.targetAircraft != null) {
  1751. for (let i = 0; i < this.targetTotal; i++) {
  1752. const id = targetData.aircrafts[i].aircraftNumber;
  1753. let index = this.changeNoList.findIndex(
  1754. (item) => item.changeNo == id
  1755. );
  1756. // 多目标集群信息显示
  1757. const lbl = {
  1758. id: id,
  1759. longitude: targetData.aircrafts[i].longitude.toFixed(7),
  1760. latitude: targetData.aircrafts[i].latitude.toFixed(7),
  1761. altitude: targetData.aircrafts[i].altitude + " m",
  1762. eastSpeed: targetData.aircrafts[i].eastSpeed.toFixed(1) + " m/s",
  1763. northSpeed:
  1764. targetData.aircrafts[i].northSpeed.toFixed(1) + " m/s",
  1765. skySpeed: targetData.aircrafts[i].skySpeed.toFixed(1) + " m/s",
  1766. };
  1767. if (index !== -1) {
  1768. const findItem = this.changeNoList[index];
  1769. for (const key in this.targetLblAry) {
  1770. if (this.targetLblAry.hasOwnProperty(key)) {
  1771. if (key === findItem.id) {
  1772. delete this.targetLblAry[key];
  1773. // 插入新属性和值
  1774. this.targetLblAry[findItem.changeNo] = lbl;
  1775. this.changeNoList.splice(index, 1);
  1776. }
  1777. }
  1778. }
  1779. }
  1780. this.targetLblAry[`${id}`] = lbl;
  1781. // if (this.targetLblAry.length === 0) {
  1782. // this.targetLblAry.push(lbl);
  1783. // } else {
  1784. // this.targetLblAry.splice(i, 1, lbl);
  1785. // }
  1786. }
  1787. }
  1788. }
  1789. },
  1790. // 新建一个飞机
  1791. addAir(data, isZhen) {
  1792. const targetTexture = new PIXI.Texture.from(uavTarPNG);
  1793. const noiseTexture = new PIXI.Texture.from(noisePNG);
  1794. const group = new PIXI.Container();
  1795. group.id = data.aircraftNumber;
  1796. // 创建无人机
  1797. let dude;
  1798. if (isZhen) {
  1799. dude = new PIXI.Sprite(targetTexture);
  1800. dude.anchor.set(0.5, 0.5);
  1801. dude.scale.set(0.01);
  1802. } else {
  1803. dude = new PIXI.Sprite(noiseTexture);
  1804. dude.anchor.set(0.5, 0.5);
  1805. dude.scale.set(0.08);
  1806. }
  1807. dude.x = data.coordinateX;
  1808. dude.y = data.coordinateY;
  1809. dude.id = data.aircraftNumber;
  1810. const id = dude.id + "";
  1811. group.addChild(dude);
  1812. // 创建圆圈 圆圈相对于飞机的位置
  1813. const cx = dude.x - 5;
  1814. const cy = dude.y - 3;
  1815. //编号-圆圈
  1816. const circle = new PIXI.Graphics();
  1817. circle.beginFill(0xff0000); // 红色
  1818. circle.drawCircle(0, 0, 8);
  1819. circle.endFill();
  1820. circle.x = cx;
  1821. circle.y = cy;
  1822. circle.scale.set(0.4);
  1823. group.addChild(circle);
  1824. //编号-数字
  1825. const text = new PIXI.Text(data.aircraftNumber, {
  1826. fontFamily: "Arial",
  1827. fontSize: 12,
  1828. fill: 0xffffff,
  1829. align: "center",
  1830. });
  1831. text.anchor.set(0.5);
  1832. // 计算文字位置以便在圆形内居中
  1833. text.x = cx;
  1834. text.y = cy;
  1835. text.scale.set(0.4);
  1836. group.addChild(text);
  1837. if (isZhen) {
  1838. group.typeName = "zhenshi";
  1839. group.buttonMode = true; // 鼠标悬停时显示手型
  1840. group.interactive = true;
  1841. this.targetDataR[`${id}`] = dude;
  1842. this.targetUavCircleR[`${id}`] = circle;
  1843. this.targetUavCircleNumR[`${id}`] = text;
  1844. } else {
  1845. dude.alpha = 0.7;
  1846. circle.alpha = 0.7;
  1847. text.alpha = 0.7;
  1848. this.targetDataN[`${id}`] = dude;
  1849. this.targetUavCircleN[`${id}`] = circle;
  1850. this.targetUavCircleNumN[`${id}`] = text;
  1851. }
  1852. this.pixiApp.stage.addChild(group);
  1853. // 真实数据创建路径
  1854. if (isZhen) {
  1855. const pathGraphics = new PIXI.Graphics();
  1856. pathGraphics.lineStyle(1, 0xeeee00, 1);
  1857. const point = { x: dude.x, y: dude.y };
  1858. pathGraphics.id = data.aircraftNumber;
  1859. this.pathGraphicsPointList[`${id}`] = point;
  1860. this.pathGraphicsList[`${id}`] = pathGraphics;
  1861. this.pixiApp.stage.addChild(pathGraphics);
  1862. }
  1863. },
  1864. // 删除回放时隐藏的飞机
  1865. delHideAir(targetDataKeys) {
  1866. const hideId = targetDataKeys[0];
  1867. let group = [];
  1868. this.pixiApp.stage.children.forEach((child) => {
  1869. // 检查 child 是否是 Container 类型
  1870. if (child instanceof PIXI.Container) {
  1871. if (child.id == hideId) {
  1872. group.push(child);
  1873. }
  1874. }
  1875. });
  1876. group.forEach((child) => {
  1877. if (!(child instanceof PIXI.Graphics)) {
  1878. if (child.parent) {
  1879. child.parent.removeChild(child);
  1880. }
  1881. child.destroy();
  1882. if (this.targetDataR[hideId]) {
  1883. delete this.targetDataR[hideId];
  1884. }
  1885. if (this.targetLblAry[hideId]) {
  1886. delete this.targetLblAry[hideId];
  1887. }
  1888. const idx = this.allSpriteInfo.findIndex((info) => {
  1889. return info.id == hideId;
  1890. });
  1891. if (idx > -1) {
  1892. this.allSpriteInfo.splice(idx, 1);
  1893. }
  1894. }
  1895. });
  1896. },
  1897. // 无人机集群 噪音数据
  1898. showTargetNAircraft(data) {
  1899. const targetData = data.targetAircraft[0];
  1900. // this.targetTotal = targetData.aircrafts.length;
  1901. const targetTotal = targetData.aircrafts.length;
  1902. let empty = Object.keys(this.targetDataN);
  1903. // 还没有数据时,先初始化数据。
  1904. if (empty.length == 0) {
  1905. for (let i = 0; i < targetTotal; i++) {
  1906. this.addAir(targetData.aircrafts[i], false);
  1907. }
  1908. } else {
  1909. // 每次更新
  1910. const newData = data.targetAircraft[0].aircrafts;
  1911. let isHide = false,
  1912. targetDataKeys,
  1913. flagCount = 0;
  1914. if (this.title == "回放") {
  1915. targetDataKeys = Object.keys(this.targetDataR);
  1916. const count = targetDataKeys.length;
  1917. // 当存的数据大于返回数据时,证明有数据隐藏
  1918. if (count > newData.length) {
  1919. isHide = true;
  1920. }
  1921. }
  1922. for (let i = 0; i < newData.length; i++) {
  1923. const id = newData[i].aircraftNumber;
  1924. const targetUav = this.targetDataN[`${id}`];
  1925. // 有隐藏的话要删除飞机
  1926. if (this.title == "回放" && isHide) {
  1927. targetDataKeys.splice(
  1928. targetDataKeys.findIndex((item) => item === id),
  1929. 1
  1930. );
  1931. flagCount++;
  1932. if (flagCount == newData.length) {
  1933. this.delHideAir(targetDataKeys);
  1934. }
  1935. }
  1936. if (targetUav) {
  1937. targetUav.x = newData[i].coordinateX;
  1938. targetUav.y = newData[i].coordinateY;
  1939. const eastSpeed = newData[i].eastSpeed;
  1940. const northSpeed = newData[i].northSpeed;
  1941. targetUav.rotation = Math.atan2(northSpeed, eastSpeed);
  1942. const cx = targetUav.x - 5;
  1943. const cy = targetUav.y - 3;
  1944. const circle = this.targetUavCircleN[`${id}`];
  1945. circle.x = cx;
  1946. circle.y = cy;
  1947. const text = this.targetUavCircleNumN[`${id}`];
  1948. text.x = cx;
  1949. text.y = cy;
  1950. } else {
  1951. this.addAir(newData[i], false);
  1952. }
  1953. }
  1954. }
  1955. },
  1956. // 飞机的显示与隐藏
  1957. hideSprite(id) {
  1958. let group = [];
  1959. this.pixiApp.stage.children.forEach((child) => {
  1960. // 检查 child 是否是 Container 类型
  1961. if (child instanceof PIXI.Container) {
  1962. if (child.id == id) {
  1963. group.push(child);
  1964. }
  1965. }
  1966. });
  1967. group.forEach((item) => {
  1968. if (item.typeName) {
  1969. const msg = "DISAPPEAR:" + item.id;
  1970. this.webSocket.send(msg);
  1971. }
  1972. item.visible = false;
  1973. });
  1974. this.hideList.push(id);
  1975. // this.changeNoList.push({ id: id, changeNo: id });
  1976. },
  1977. // 飞机的显示与隐藏
  1978. showSprite(id) {
  1979. if (this.isUpdateView) {
  1980. // 表示是回放,不用隐藏
  1981. return;
  1982. }
  1983. let group = [];
  1984. this.pixiApp.stage.children.forEach((child) => {
  1985. // 检查 child 是否是 Container 类型
  1986. if (child instanceof PIXI.Container) {
  1987. if (child.id == id) {
  1988. group.push(child);
  1989. }
  1990. }
  1991. });
  1992. // group有三个,真实飞机、噪音飞机、轨迹
  1993. if (this.hideList.includes(id)) {
  1994. const maxNum = Math.max(...this.allId) + 1;
  1995. group.forEach((item) => {
  1996. // 已经隐藏了,要显示
  1997. if (item.typeName) {
  1998. const msg = "SHOW:" + item.id;
  1999. this.webSocket.send(msg);
  2000. this.changeNoList.push({ id: id, changeNo: maxNum });
  2001. let index = this.hideList.indexOf(id);
  2002. if (index > -1) {
  2003. this.hideList.splice(index, 1);
  2004. }
  2005. this.allId.push(maxNum);
  2006. if (item.parent) {
  2007. item.parent.removeChild(item);
  2008. }
  2009. // 可选:销毁精灵并释放资源
  2010. item.destroy();
  2011. delete this.targetDataR[item.id];
  2012. }
  2013. // item.visible = true;
  2014. });
  2015. } else {
  2016. // 隐藏
  2017. group.forEach((item) => {
  2018. if (item.typeName) {
  2019. const msg = "DISAPPEAR:" + item.id;
  2020. this.webSocket.send(msg);
  2021. this.hideList.push(id);
  2022. let index = this.allSpriteInfo.findIndex((info) => {
  2023. return info.id == item.id;
  2024. });
  2025. this.allSpriteInfo.splice(index, 1);
  2026. }
  2027. // 只隐藏飞机,不隐藏轨迹
  2028. if (!(item instanceof PIXI.Graphics)) {
  2029. item.visible = false;
  2030. }
  2031. });
  2032. // this.changeNoList.push({ id: id, changeNo: id });
  2033. }
  2034. },
  2035. // 添加视窗控制
  2036. enableViewControl(app) {
  2037. app.stage.interactive = true;
  2038. // 拖曳状态变量
  2039. let dragging = false;
  2040. let lastPosition = null; // 上一次鼠标的位置
  2041. if (!(this.title == "添加任务数据" || this.title == "修改任务数据")) {
  2042. // 鼠标按下事件
  2043. app.view.addEventListener("mousedown", (event) => {
  2044. dragging = true;
  2045. lastPosition = { x: event.clientX, y: event.clientY };
  2046. });
  2047. // 鼠标移出画布时停止拖动
  2048. app.view.addEventListener("mouseleave", () => {
  2049. dragging = false;
  2050. });
  2051. // 鼠标移动事件
  2052. app.view.addEventListener("mousemove", (event) => {
  2053. if (dragging) {
  2054. const currentPosition = { x: event.clientX, y: event.clientY };
  2055. // 计算鼠标移动的距离
  2056. const dx = currentPosition.x - lastPosition.x;
  2057. const dy = currentPosition.y - lastPosition.y;
  2058. // 更新舞台的位置
  2059. app.stage.x += dx;
  2060. app.stage.y += dy;
  2061. // 更新上一次鼠标的位置
  2062. lastPosition = currentPosition;
  2063. }
  2064. });
  2065. // 鼠标松开事件
  2066. app.view.addEventListener("mouseup", () => {
  2067. dragging = false;
  2068. });
  2069. }
  2070. // 鼠标滚轮缩放
  2071. app.renderer.view.addEventListener("wheel", (e) => {
  2072. e.preventDefault();
  2073. const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
  2074. app.stage.scale.x *= zoomFactor;
  2075. app.stage.scale.y *= zoomFactor;
  2076. });
  2077. },
  2078. // 更新无人机飞行路径
  2079. updatePath(id, x, y) {
  2080. const lastPointX = this.pathGraphicsPointList[`${id}`].x;
  2081. const lastPointY = this.pathGraphicsPointList[`${id}`].y;
  2082. this.pathGraphicsList[`${id}`].moveTo(lastPointX, lastPointY);
  2083. this.pathGraphicsList[`${id}`].lineTo(x, y);
  2084. this.pathGraphicsPointList[`${id}`].x = x;
  2085. this.pathGraphicsPointList[`${id}`].y = y;
  2086. },
  2087. // 更新平台无人机飞行路径
  2088. updatePlatformPath(x, y) {
  2089. const lastPointX = this.platformPathData.x;
  2090. const lastPointY = this.platformPathData.y;
  2091. this.platformGraphics.moveTo(lastPointX, lastPointY);
  2092. this.platformGraphics.lineTo(x, y);
  2093. this.platformPathData.x = x;
  2094. this.platformPathData.y = y;
  2095. },
  2096. // 确定编队信息
  2097. canvasInfoSubmit() {
  2098. this.$refs["canvasInfoFormRef"].validate((valid) => {
  2099. if (valid) {
  2100. this.submitInfoFlag = true;
  2101. this.addAirSprite();
  2102. }
  2103. });
  2104. },
  2105. // 初始化自定义编队的画布
  2106. initFormationApp() {
  2107. this.pixiCanvas = this.$refs.pixiCanvas;
  2108. if (!this.pixiCanvas) return;
  2109. // 创建Pixi应用
  2110. this.formationAirPiXiApp = new PIXI.Application({
  2111. width: this.pixiCanvas.clientWidth,
  2112. height: this.pixiCanvas.clientHeight,
  2113. antialias: true,
  2114. transparent: false,
  2115. resolution: 1,
  2116. backgroundAlpha: 0,
  2117. });
  2118. this.formationAirPiXiApp.renderer.backgroundColor = 0x1099bb;
  2119. this.pixiCanvas.appendChild(this.formationAirPiXiApp.view);
  2120. // 绘制中心点
  2121. const graphics = new PIXI.Graphics();
  2122. graphics.beginFill(0xe53f32);
  2123. // 绘制一个圆形,半径为 5 像素
  2124. const centerX = this.pixiCanvas.clientWidth / 2;
  2125. const centerY = this.pixiCanvas.clientHeight / 2;
  2126. graphics.drawCircle(centerX, centerY, 3);
  2127. graphics.endFill();
  2128. this.formationAirPiXiApp.stage.addChild(graphics);
  2129. // this.enableViewControl(this.formationAirPiXiApp);
  2130. },
  2131. // 增加飞机编队
  2132. addAirSprite() {
  2133. // 判断是哪个机型
  2134. let texture = new PIXI.Texture.from(uavTarPNG);
  2135. // 创建飞机
  2136. const airSprite = new PIXI.Sprite(texture);
  2137. airSprite.anchor.set(0.5, 0.5);
  2138. airSprite.scale.set(0.04);
  2139. airSprite.position.set(
  2140. this.formationAirPiXiApp.screen.width / 2,
  2141. this.formationAirPiXiApp.screen.height / 2
  2142. );
  2143. this.formationAirPiXiApp.stage.addChild(airSprite);
  2144. this.formationAirPiXiApp.stage.eventMode = "static";
  2145. this.formationAirPiXiApp.stage.hitArea = this.formationAirPiXiApp.screen;
  2146. this.formationAirPiXiApp.view.addEventListener("pointermove", (e) => {
  2147. airSprite.x = e.offsetX;
  2148. airSprite.y = e.offsetY;
  2149. // 计算对应经纬度
  2150. const { lat, lng } = this.calculateLatLng(e.offsetX, e.offsetY);
  2151. this.nowlongitude = lng;
  2152. this.nowlatitude = lat;
  2153. });
  2154. this.formationAirPiXiApp.view.addEventListener("dblclick", (e) => {
  2155. // 创建新精灵
  2156. const newSprite = new PIXI.Sprite(texture);
  2157. newSprite.anchor.set(0.5, 0.5);
  2158. newSprite.scale.set(0.04);
  2159. newSprite.x = e.offsetX;
  2160. newSprite.y = e.offsetY;
  2161. // 计算对应经纬度
  2162. const { lat, lng } = this.calculateLatLng(e.offsetX, e.offsetY);
  2163. this.formationInfoForm = {
  2164. number: null,
  2165. longitude: null,
  2166. latitude: null,
  2167. altitude: null,
  2168. eastSpeed: null,
  2169. northSpeed: null,
  2170. skySpeed: null,
  2171. flightPathType: "直线",
  2172. positionNoise: null,
  2173. };
  2174. this.formationInfoForm.longitude = lng;
  2175. this.formationInfoForm.latitude = lat;
  2176. this.drawerOpen = true;
  2177. this.addOrUpdate = "add";
  2178. this.formationInfoAirDataList.push(newSprite);
  2179. this.formationAirPiXiApp.stage.addChild(newSprite);
  2180. });
  2181. },
  2182. calculateLatLng(x, y) {
  2183. // 计算鼠标相对于画布中心的偏移量(单位:像素)
  2184. const offsetX = x - this.pixiCanvas.clientWidth / 2;
  2185. const offsetY = y - this.pixiCanvas.clientHeight / 2;
  2186. // 将像素偏移量转换为实际距离(单位:公里)
  2187. const kmPerPixel = this.canvasInfo.lengthKm / this.pixiCanvas.clientWidth;
  2188. const deltaX = offsetX * kmPerPixel;
  2189. const deltaY = -offsetY * kmPerPixel; // 注意 y 轴方向相反
  2190. // 将实际距离转换为经纬度差值
  2191. const deltaLat = deltaY / 111; // 每纬度约 111 公里
  2192. const deltaLng =
  2193. deltaX /
  2194. (111 * Math.cos((this.canvasInfo.centerLatitude * Math.PI) / 180)); // 每经度约 111 * cos(lat) 公里
  2195. // 计算最终经纬度
  2196. const lat = Number(this.canvasInfo.centerLatitude) + Number(deltaLat);
  2197. const lng = Number(this.canvasInfo.centerLongitude) + Number(deltaLng);
  2198. return { lat, lng };
  2199. },
  2200. editFormationInfo(row) {
  2201. this.formationInfoForm = row;
  2202. this.drawerOpen = true;
  2203. this.addOrUpdate = "update";
  2204. },
  2205. handleDrawerSubmit() {
  2206. this.$refs["formationInfoRef"].validate((valid) => {
  2207. if (valid) {
  2208. this.formationInfoForm = this.ObjectStringToNumber(
  2209. this.formationInfoForm
  2210. );
  2211. let index = this.formationInfoList.findIndex(
  2212. (item) => item.number == this.formationInfoForm.number
  2213. );
  2214. // 查看是不是已在列表中
  2215. if (index !== -1) {
  2216. if (this.addOrUpdate == "add") {
  2217. // 如果在,并且是新增加的
  2218. this.$message.warning("该编号已存在,请重新输入");
  2219. return;
  2220. } else {
  2221. // 在,是要修改的。修改的还不能列表中的编号相同
  2222. this.formationInfoList[index] = this.formationInfoForm;
  2223. let spriteIndex;
  2224. this.formationAirPiXiApp.stage.children.forEach(
  2225. (child, index) => {
  2226. // 检查 child 是否是 Container 类型
  2227. if (child instanceof PIXI.Sprite) {
  2228. if (child.id == this.formationInfoForm.number) {
  2229. spriteIndex = index;
  2230. }
  2231. }
  2232. }
  2233. );
  2234. const sprite =
  2235. this.formationAirPiXiApp.stage.children[spriteIndex];
  2236. const { x, y } = this.calculateXY(
  2237. this.formationInfoForm.longitude,
  2238. this.formationInfoForm.latitude
  2239. );
  2240. sprite.x = x;
  2241. sprite.y = y;
  2242. this.drawerOpen = false;
  2243. }
  2244. } else {
  2245. //不在列表中
  2246. if (this.addOrUpdate == "add") {
  2247. // 新增加的
  2248. this.formationInfoList.push(this.formationInfoForm);
  2249. const length = this.formationInfoAirDataList.length;
  2250. this.$set(
  2251. this.formationInfoAirDataList[length - 1],
  2252. "id",
  2253. this.formationInfoForm.number
  2254. );
  2255. this.drawerOpen = false;
  2256. }
  2257. }
  2258. }
  2259. });
  2260. },
  2261. // 删除编队信息
  2262. canvasInfoDel() {
  2263. this.$modal
  2264. .confirm("是否确认删除该无人机")
  2265. .then(() => {
  2266. if (this.addOrUpdate == "add") {
  2267. // 增加的时候要删除
  2268. const len = this.formationAirPiXiApp.stage.children.length;
  2269. this.formationAirPiXiApp.stage.children[len - 1].destroy();
  2270. return;
  2271. } else {
  2272. this.formationInfoForm = this.ObjectStringToNumber(
  2273. this.formationInfoForm
  2274. );
  2275. let index = this.formationInfoList.findIndex(
  2276. (item) => item.number == this.formationInfoForm.number
  2277. );
  2278. if (index > -1) {
  2279. this.formationInfoAirDataList =
  2280. this.formationInfoAirDataList.filter((item) => {
  2281. return item.id !== this.formationInfoForm.number;
  2282. });
  2283. this.formationAirPiXiApp.stage.children.forEach(
  2284. (child, index) => {
  2285. // 检查 child 是否是 Container 类型
  2286. if (child instanceof PIXI.Sprite) {
  2287. if (child.id == this.formationInfoForm.number) {
  2288. if (child.parent) {
  2289. child.parent.removeChild(child);
  2290. }
  2291. child.destroy();
  2292. }
  2293. }
  2294. }
  2295. );
  2296. this.formationInfoList.splice(index, 1);
  2297. }
  2298. }
  2299. })
  2300. .then(() => {
  2301. this.drawerOpen = false;
  2302. this.$modal.msgSuccess("删除成功");
  2303. })
  2304. .catch((err) => {
  2305. console.log(err);
  2306. });
  2307. },
  2308. ObjectStringToNumber(obj) {
  2309. for (let key in obj) {
  2310. const parsedValue = Number(obj[key]); // 尝试将值转换为数字
  2311. if (!isNaN(parsedValue)) {
  2312. // 检查是否是有效的数字
  2313. obj[key] = parsedValue; // 如果是数字,更新字段值
  2314. }
  2315. }
  2316. return obj;
  2317. },
  2318. closeDialog() {
  2319. if (!this.isUpdateView) {
  2320. const msg = "END:" + this.parameterId;
  2321. this.webSocket.send(msg);
  2322. }
  2323. this.destroyTrajectory();
  2324. this.showTrajectory = false;
  2325. this.isUpdateView = false;
  2326. this.isClickGroup = false;
  2327. this.twoAirInfo = false;
  2328. this.loading = true;
  2329. this.airDistance = null;
  2330. this.airAngle = null;
  2331. setTimeout(() => {
  2332. this.getList();
  2333. }, 1500);
  2334. },
  2335. closeUpdataDialog() {
  2336. this.destroyTormationInfo();
  2337. this.canvasInfo = {
  2338. centerLongitude: null,
  2339. centerLatitude: null,
  2340. lengthKm: null,
  2341. };
  2342. this.submitInfoFlag = false;
  2343. this.formationAirPiXiApp = null;
  2344. this.pixiCanvas = null;
  2345. this.nowlongitude = null;
  2346. this.nowlatitude = null;
  2347. this.formationInfoList = [];
  2348. this.reset();
  2349. },
  2350. destroyTormationInfo() {
  2351. if (this.formationAirPiXiApp) {
  2352. const removedChildren = this.formationAirPiXiApp.stage.removeChildren(
  2353. 0,
  2354. this.formationAirPiXiApp.stage.children.length
  2355. );
  2356. removedChildren.forEach((child) => {
  2357. child.destroy({
  2358. children: true,
  2359. texture: false,
  2360. baseTexture: false,
  2361. });
  2362. });
  2363. this.formationAirPiXiApp.stage.removeChildren();
  2364. this.formationAirPiXiApp.renderer.clear();
  2365. this.formationAirPiXiApp.destroy(true);
  2366. this.formationAirPiXiApp = null;
  2367. }
  2368. },
  2369. drawerClose() {
  2370. this.formationInfoForm = {
  2371. number: null,
  2372. longitude: null,
  2373. latitude: null,
  2374. altitude: null,
  2375. eastSpeed: null,
  2376. northSpeed: null,
  2377. skySpeed: null,
  2378. flightPathType: "直线",
  2379. positionNoise: null,
  2380. };
  2381. this.addOrUpdate = "add";
  2382. },
  2383. clearAllAry() {
  2384. this.targetLblAry = {};
  2385. this.targetUavAry = [];
  2386. this.targetUavCircleR = {};
  2387. this.targetUavCircleN = {};
  2388. this.targetUavCircleNumR = {};
  2389. this.targetUavCircleNumN = {};
  2390. this.configList = [
  2391. "经度",
  2392. "纬度",
  2393. "海拔",
  2394. "东向速度",
  2395. "北向速度",
  2396. "天向速度",
  2397. ];
  2398. this.lastPosition = {
  2399. x: null,
  2400. y: null,
  2401. };
  2402. this.hideList = [];
  2403. this.allId = [];
  2404. this.changeNoList = [];
  2405. this.curLongitude = null;
  2406. this.curLatitude = null;
  2407. this.curAltitude = null;
  2408. this.pathGraphics = null;
  2409. this.cycleCount = 0;
  2410. this.isUpdateView = false;
  2411. this.pathGraphicsList = {};
  2412. this.pathGraphicsPointList = {};
  2413. this.targetDataR = {};
  2414. this.targetDataN = {};
  2415. this.nowlongitude = null;
  2416. this.nowlatitude = null;
  2417. this.submitInfoFlag = false;
  2418. this.pixiCanvas = null;
  2419. this.canvasInfo = {
  2420. centerLongitude: null,
  2421. centerLatitude: null,
  2422. lengthKm: null,
  2423. heightKm: null,
  2424. };
  2425. this.platformSprite = null;
  2426. this.platformGraphics = null;
  2427. this.platformPathData = {};
  2428. this.platformInfo = {};
  2429. this.platformData = [];
  2430. this.allSpriteInfo = [];
  2431. this.isClickGroup = false;
  2432. this.firstLng = null;
  2433. this.firstLat = null;
  2434. this.playbackTime = null;
  2435. this.firstAir = "";
  2436. this.secondAir = "";
  2437. this.percentage = 0;
  2438. },
  2439. // 综合计算(距离、方位角、航向夹角)
  2440. calculateRelativePosition(lat1, lon1, heading1, lat2, lon2, heading2) {
  2441. const distance = this.calculateDistance(lat1, lon1, lat2, lon2);
  2442. const bearing = this.calculateBearing(lat1, lon1, lat2, lon2);
  2443. // const headingDiff = this.calculateHeadingDiff(heading1, heading2);
  2444. return {
  2445. distance: distance, // 两飞机距离(米)
  2446. bearing: bearing, // 飞机2相对于飞机1的方位角(0°~360°)
  2447. // headingDiff: headingDiff, // 两飞机航向夹角(0°~180°)
  2448. };
  2449. },
  2450. // 计算两架飞机的航向夹角(航向差)
  2451. calculateHeadingDiff(heading1, heading2) {
  2452. const diff = Math.abs(heading1 - heading2);
  2453. return diff > 180 ? 360 - diff : diff;
  2454. },
  2455. // 计算飞机2相对于飞机1的方位角
  2456. calculateBearing(lat1, lon1, lat2, lon2) {
  2457. const phi1 = (lat1 * Math.PI) / 180;
  2458. const lambda1 = (lon1 * Math.PI) / 180;
  2459. const phi2 = (lat2 * Math.PI) / 180;
  2460. const lambda2 = (lon2 * Math.PI) / 180;
  2461. const y = Math.sin(lambda2 - lambda1) * Math.cos(phi2);
  2462. const x =
  2463. Math.cos(phi1) * Math.sin(phi2) -
  2464. Math.sin(phi1) * Math.cos(phi2) * Math.cos(lambda2 - lambda1);
  2465. let theta = (Math.atan2(y, x) * 180) / Math.PI;
  2466. return (theta + 360) % 360; // 确保在0°~360°范围内
  2467. },
  2468. // 计算两架飞机之间的距离(Haversine 公式)
  2469. calculateDistance(lat1, lon1, lat2, lon2) {
  2470. const R = 6371000; // 地球半径(米)
  2471. const phi1 = (lat1 * Math.PI) / 180;
  2472. const phi2 = (lat2 * Math.PI) / 180;
  2473. const deltaPhi = ((lat2 - lat1) * Math.PI) / 180;
  2474. const deltaLambda = ((lon2 - lon1) * Math.PI) / 180;
  2475. const a =
  2476. Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) +
  2477. Math.cos(phi1) *
  2478. Math.cos(phi2) *
  2479. Math.sin(deltaLambda / 2) *
  2480. Math.sin(deltaLambda / 2);
  2481. const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  2482. return R * c; // 返回距离(米)
  2483. },
  2484. },
  2485. };
  2486. </script>
  2487. <style>
  2488. .el-dialog__body {
  2489. /* padding: 0 !important; */
  2490. padding-top: 10px !important;
  2491. }
  2492. .pixi-container {
  2493. width: 100%;
  2494. /* height: 750px; */
  2495. height: calc(100vh - 140px);
  2496. position: relative;
  2497. }
  2498. .pixi-container canvas {
  2499. width: 100%;
  2500. height: 100%;
  2501. }
  2502. .container {
  2503. display: grid;
  2504. grid-template-columns: 85% 15%; /* 两栏按比例分 */
  2505. }
  2506. .right-board {
  2507. padding: 2px;
  2508. margin: 2px;
  2509. height: 800px;
  2510. /* 当内容超出元素边界时显示滚动条 */
  2511. }
  2512. .distance {
  2513. display: inline-block;
  2514. width: 1000px;
  2515. margin-left: 50px;
  2516. }
  2517. .info {
  2518. font-weight: 700;
  2519. color: #4f4a4a;
  2520. }
  2521. </style>