index.vue 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211
  1. <template>
  2. <div class="runtime-config-simple">
  3. <!-- 页面头部 -->
  4. <div class="page-header">
  5. <div class="header-content">
  6. <div class="header-main">
  7. <h1 class="page-title">运行参数</h1>
  8. <p class="page-desc">配置系统运行参数,逐项编辑保存</p>
  9. </div>
  10. </div>
  11. <!-- 搜索框 -->
  12. <div class="search-section">
  13. <el-input
  14. v-model="searchKeyword"
  15. placeholder="搜索运行参数..."
  16. clearable
  17. prefix-icon="el-icon-search"
  18. class="search-input"
  19. />
  20. </div>
  21. </div>
  22. <!-- 主内容区域 -->
  23. <div class="page-content">
  24. <div class="config-cards">
  25. <!-- 控制参数卡片 - 全宽显示 -->
  26. <div class="config-card control-card" v-show="hasVisibleItems('control')">
  27. <div class="card-header">
  28. <h2 class="card-title">控制参数</h2>
  29. <p class="card-desc">车辆运动控制和行驶行为配置</p>
  30. </div>
  31. <div class="card-content control-content">
  32. <XtParamItem
  33. v-for="item in controlConfigItems"
  34. :key="item.key"
  35. v-show="isItemVisible(item)"
  36. :label="item.label"
  37. :value="config[item.key]"
  38. :type="item.type"
  39. :unit="item.unit"
  40. :placeholder="item.placeholder"
  41. :precision="item.precision"
  42. :step="item.step"
  43. :min="item.min"
  44. :max="item.max"
  45. :search-keyword="searchKeyword"
  46. @save="(value) => handleParamSave(item.key, value)"
  47. @cancel="handleParamCancel"
  48. />
  49. <div v-if="!hasVisibleItems('control')" class="empty-state">
  50. <i class="el-icon-search"></i>
  51. <p>无匹配的配置参数</p>
  52. </div>
  53. </div>
  54. </div>
  55. <!-- 其他参数分组 - 两列显示 -->
  56. <div class="other-params-grid" :class="{ 'two-columns': isLargeScreen }">
  57. <!-- 定位参数卡片 -->
  58. <div class="config-card" v-show="hasVisibleItems('positioning')">
  59. <div class="card-header">
  60. <h2 class="card-title">定位参数</h2>
  61. <p class="card-desc">地图和定位算法相关配置</p>
  62. </div>
  63. <div class="card-content">
  64. <XtParamItem
  65. v-for="item in positioningConfigItems"
  66. :key="item.key"
  67. v-show="isItemVisible(item)"
  68. :label="item.label"
  69. :value="config[item.key]"
  70. :type="item.type"
  71. :unit="item.unit"
  72. :placeholder="item.placeholder"
  73. :precision="item.precision"
  74. :step="item.step"
  75. :min="item.min"
  76. :max="item.max"
  77. :search-keyword="searchKeyword"
  78. @save="(value) => handleParamSave(item.key, value)"
  79. @cancel="handleParamCancel"
  80. />
  81. <div v-if="!hasVisibleItems('positioning')" class="empty-state">
  82. <i class="el-icon-search"></i>
  83. <p>无匹配的配置参数</p>
  84. </div>
  85. </div>
  86. </div>
  87. <!-- 避障参数卡片 -->
  88. <div class="config-card" v-show="hasVisibleItems('obstacle')">
  89. <div class="card-header">
  90. <h2 class="card-title">避障参数</h2>
  91. <p class="card-desc">激光雷达障碍物检测配置</p>
  92. </div>
  93. <div class="card-content">
  94. <XtParamItem
  95. v-for="item in obstacleConfigItems"
  96. :key="item.key"
  97. v-show="isItemVisible(item)"
  98. :label="item.label"
  99. :value="config[item.key]"
  100. :type="item.type"
  101. :unit="item.unit"
  102. :placeholder="item.placeholder"
  103. :precision="item.precision"
  104. :step="item.step"
  105. :min="item.min"
  106. :max="item.max"
  107. :search-keyword="searchKeyword"
  108. @save="(value) => handleParamSave(item.key, value)"
  109. @cancel="handleParamCancel"
  110. />
  111. <div v-if="!hasVisibleItems('obstacle')" class="empty-state">
  112. <i class="el-icon-search"></i>
  113. <p>无匹配的配置参数</p>
  114. </div>
  115. </div>
  116. </div>
  117. <!-- 规划参数卡片 -->
  118. <div class="config-card" v-show="hasVisibleItems('planning')">
  119. <div class="card-header">
  120. <h2 class="card-title">规划参数</h2>
  121. <p class="card-desc">路径规划和任务执行配置</p>
  122. </div>
  123. <div class="card-content">
  124. <XtParamItem
  125. v-for="item in planningConfigItems"
  126. :key="item.key"
  127. v-show="isItemVisible(item)"
  128. :label="item.label"
  129. :value="config[item.key]"
  130. :type="item.type"
  131. :unit="item.unit"
  132. :placeholder="item.placeholder"
  133. :precision="item.precision"
  134. :step="item.step"
  135. :min="item.min"
  136. :max="item.max"
  137. :options="item.options"
  138. :search-keyword="searchKeyword"
  139. @save="(value) => handleParamSave(item.key, value)"
  140. @cancel="handleParamCancel"
  141. />
  142. <div v-if="!hasVisibleItems('planning')" class="empty-state">
  143. <i class="el-icon-search"></i>
  144. <p>无匹配的配置参数</p>
  145. </div>
  146. </div>
  147. </div>
  148. <!-- 任务管理卡片 -->
  149. <div class="config-card" v-show="hasVisibleItems('task')">
  150. <div class="card-header">
  151. <h2 class="card-title">任务管理</h2>
  152. <p class="card-desc">自主充电和任务管理配置</p>
  153. </div>
  154. <div class="card-content">
  155. <XtParamItem
  156. v-for="item in taskConfigItems"
  157. :key="item.key"
  158. v-show="isItemVisible(item)"
  159. :label="item.label"
  160. :value="config[item.key]"
  161. :type="item.type"
  162. :unit="item.unit"
  163. :placeholder="item.placeholder"
  164. :precision="item.precision"
  165. :step="item.step"
  166. :min="item.min"
  167. :max="item.max"
  168. :options="item.options"
  169. :search-keyword="searchKeyword"
  170. @save="(value) => handleParamSave(item.key, value)"
  171. @cancel="handleParamCancel"
  172. />
  173. <div v-if="!hasVisibleItems('task')" class="empty-state">
  174. <i class="el-icon-search"></i>
  175. <p>无匹配的配置参数</p>
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. </div>
  181. </div>
  182. </div>
  183. </template>
  184. <script>
  185. import XtParamItem from '@/components/XtParamItem'
  186. // 尝试导入真实 API,失败则使用 Mock
  187. let runtimeApi
  188. try {
  189. runtimeApi = require('@/api/config/runtime')
  190. } catch (error) {
  191. // Fallback 到 Mock API
  192. runtimeApi = {
  193. getRuntimeParams: () => Promise.resolve({ code: 200, data: {} }),
  194. updateParam: ({ key, value }) => Promise.resolve({ code: 200, message: '参数保存成功' })
  195. }
  196. }
  197. export default {
  198. name: 'RuntimeConfig',
  199. components: {
  200. XtParamItem
  201. },
  202. data() {
  203. return {
  204. // 加载状态
  205. loading: false,
  206. // 搜索关键词
  207. searchKeyword: '',
  208. // 屏幕尺寸
  209. isLargeScreen: false,
  210. // 配置数据
  211. config: {
  212. // 控制参数
  213. forwardObstacleDetectionDistance: 3.0,
  214. reverseObstacleDetectionDistance: 1.5,
  215. avoidStopDistance: 0.5,
  216. forwardMaxFollowDistance: 5.0,
  217. forwardMinFollowDistance: 1.0,
  218. forwardFollowSpeedRatio: 0.8,
  219. reverseMinFollowDistance: 1.0,
  220. reverseFollowSpeedRatio: 0.6,
  221. decelerationCTEStartValue: 0.5,
  222. decelerationCTEMaxValue: 1.0,
  223. maxCTE: 2.0,
  224. maxDrivingSpeed: 2.0,
  225. maxReverseSpeed: 1.0,
  226. maxAcceleration: 1.0,
  227. maxDeceleration: 2.0,
  228. startupPhaseEndSpeed: 0.2,
  229. terminationPhaseEndSpeed: 0.1,
  230. maxAngularVelocity: 1.0,
  231. maxSteeringAngle: 0.5,
  232. lateralAcceleration: 1.5,
  233. deceleration: 2.0,
  234. inPlaceRotationAngleErrorTolerance: 0.1,
  235. nonInPlaceRotationStartupMaxAngle: 0.3,
  236. inPlaceRotationAngularVelocityRatio: 0.5,
  237. drivingSteeringAngularVelocityRatio: 0.8,
  238. reverseFeedCycles: 3,
  239. forwardPositioningForwardOffset: 0.5,
  240. reversePositioningRearOffset: 0.5,
  241. targetPointForwardDistance: 1.0,
  242. routeDistance: 10.0,
  243. obstacleDistance: 2.0,
  244. maxBypassAngle: 45,
  245. minBypassAngle: 15,
  246. bypassKeepDistance: 1.0,
  247. controller: 'PID',
  248. odometerTopicName: '/odom',
  249. enableDebugMode: false,
  250. maxPassageWidth: 3.0,
  251. minPassageWidth: 1.5,
  252. // 定位参数
  253. mapResolution: 0.05,
  254. chargingRoomMapResolution: 0.02,
  255. recursiveFilterParam: 0.8,
  256. filterParam: 0.9,
  257. chargingRoomFilterParam: 0.7,
  258. chargingRoomPositioningStep: 0.1,
  259. mapUpdateInterpolationDistance: 0.5,
  260. positioningCorrectionPointCloudTopicName: '/cloud_corrected',
  261. deadReckoningPointCloudTopicName: '/cloud_dead_reckoning',
  262. // 避障参数
  263. laserForwardObstacleDetectionStartDistance: 0.5,
  264. laserRearObstacleDetectionStartDistance: -1.0,
  265. laserLeftObstacleDetectionStartDistance: 0.3,
  266. laserRightObstacleDetectionStartDistance: 0.3,
  267. // 规划参数
  268. multiPointCycleEnabled: false,
  269. multiPointSortEnabled: false,
  270. obstacleReplanningTime: 30,
  271. // 任务管理
  272. autonomousChargingEnabled: false,
  273. autonomousChargingLowBatteryThreshold: 0.2
  274. },
  275. // 控制参数配置项
  276. controlConfigItems: [
  277. {
  278. key: 'forwardObstacleDetectionDistance',
  279. label: '前行障碍物探测距离',
  280. type: 'number',
  281. unit: 'm',
  282. placeholder: '0.0',
  283. precision: 1,
  284. step: 0.1,
  285. min: 0
  286. },
  287. {
  288. key: 'reverseObstacleDetectionDistance',
  289. label: '倒车障碍物探测距离',
  290. type: 'number',
  291. unit: 'm',
  292. placeholder: '0.0',
  293. precision: 1,
  294. step: 0.1,
  295. min: 0
  296. },
  297. {
  298. key: 'avoidStopDistance',
  299. label: '避停距离',
  300. type: 'number',
  301. unit: 'm',
  302. placeholder: '0.0',
  303. precision: 1,
  304. step: 0.1,
  305. min: 0
  306. },
  307. {
  308. key: 'forwardMaxFollowDistance',
  309. label: '前行最大跟点距离',
  310. type: 'number',
  311. unit: 'm',
  312. placeholder: '0.0',
  313. precision: 1,
  314. step: 0.1,
  315. min: 0
  316. },
  317. {
  318. key: 'forwardMinFollowDistance',
  319. label: '前行最小跟点距离',
  320. type: 'number',
  321. unit: 'm',
  322. placeholder: '0.0',
  323. precision: 1,
  324. step: 0.1,
  325. min: 0
  326. },
  327. {
  328. key: 'forwardFollowSpeedRatio',
  329. label: '前行跟点/速度系数',
  330. type: 'number',
  331. placeholder: '0.0',
  332. precision: 2,
  333. step: 0.01,
  334. min: 0,
  335. max: 1
  336. },
  337. {
  338. key: 'reverseMinFollowDistance',
  339. label: '倒车最小跟点距离',
  340. type: 'number',
  341. unit: 'm',
  342. placeholder: '0.0',
  343. precision: 1,
  344. step: 0.1,
  345. min: 0
  346. },
  347. {
  348. key: 'reverseFollowSpeedRatio',
  349. label: '倒车跟点/速度系数',
  350. type: 'number',
  351. placeholder: '0.0',
  352. precision: 2,
  353. step: 0.01,
  354. min: 0,
  355. max: 1
  356. },
  357. {
  358. key: 'decelerationCTEStartValue',
  359. label: '减速/CTE起始值',
  360. type: 'number',
  361. unit: 'm',
  362. placeholder: '0.0',
  363. precision: 2,
  364. step: 0.01,
  365. min: 0
  366. },
  367. {
  368. key: 'decelerationCTEMaxValue',
  369. label: '减速/CTE最大值',
  370. type: 'number',
  371. unit: 'm',
  372. placeholder: '0.0',
  373. precision: 2,
  374. step: 0.01,
  375. min: 0
  376. },
  377. {
  378. key: 'maxCTE',
  379. label: '最大CTE',
  380. type: 'number',
  381. unit: 'm',
  382. placeholder: '0.0',
  383. precision: 2,
  384. step: 0.01,
  385. min: 0
  386. },
  387. {
  388. key: 'maxDrivingSpeed',
  389. label: '最大行驶速度',
  390. type: 'number',
  391. unit: 'm/s',
  392. placeholder: '0.0',
  393. precision: 1,
  394. step: 0.1,
  395. min: 0
  396. },
  397. {
  398. key: 'maxReverseSpeed',
  399. label: '最大倒车速度',
  400. type: 'number',
  401. unit: 'm/s',
  402. placeholder: '0.0',
  403. precision: 1,
  404. step: 0.1,
  405. min: 0
  406. },
  407. {
  408. key: 'maxAcceleration',
  409. label: '最大加速度',
  410. type: 'number',
  411. unit: 'm/s²',
  412. placeholder: '0.0',
  413. precision: 1,
  414. step: 0.1,
  415. min: 0
  416. },
  417. {
  418. key: 'maxDeceleration',
  419. label: '最大减速度',
  420. type: 'number',
  421. unit: 'm/s²',
  422. placeholder: '0.0',
  423. precision: 1,
  424. step: 0.1,
  425. min: 0
  426. },
  427. {
  428. key: 'startupPhaseEndSpeed',
  429. label: '启动阶段截止速度',
  430. type: 'number',
  431. unit: 'm/s',
  432. placeholder: '0.0',
  433. precision: 2,
  434. step: 0.01,
  435. min: 0
  436. },
  437. {
  438. key: 'terminationPhaseEndSpeed',
  439. label: '终止阶段截止速度',
  440. type: 'number',
  441. unit: 'm/s',
  442. placeholder: '0.0',
  443. precision: 2,
  444. step: 0.01,
  445. min: 0
  446. },
  447. {
  448. key: 'maxAngularVelocity',
  449. label: '最大角速度',
  450. type: 'number',
  451. unit: 'rad/s',
  452. placeholder: '0.0',
  453. precision: 2,
  454. step: 0.01,
  455. min: 0
  456. },
  457. {
  458. key: 'maxSteeringAngle',
  459. label: '最大转向角度',
  460. type: 'number',
  461. unit: 'rad',
  462. placeholder: '0.0',
  463. precision: 3,
  464. step: 0.001,
  465. min: 0
  466. },
  467. {
  468. key: 'lateralAcceleration',
  469. label: '侧向加速度',
  470. type: 'number',
  471. unit: 'm/s²',
  472. placeholder: '0.0',
  473. precision: 1,
  474. step: 0.1,
  475. min: 0
  476. },
  477. {
  478. key: 'deceleration',
  479. label: '减速度',
  480. type: 'number',
  481. unit: 'm/s²',
  482. placeholder: '0.0',
  483. precision: 1,
  484. step: 0.1,
  485. min: 0
  486. },
  487. {
  488. key: 'inPlaceRotationAngleErrorTolerance',
  489. label: '原地旋转角度误差容限',
  490. type: 'number',
  491. unit: 'rad',
  492. placeholder: '0.0',
  493. precision: 3,
  494. step: 0.001,
  495. min: 0
  496. },
  497. {
  498. key: 'nonInPlaceRotationStartupMaxAngle',
  499. label: '非原地旋转起步最大角度',
  500. type: 'number',
  501. unit: 'rad',
  502. placeholder: '0.0',
  503. precision: 3,
  504. step: 0.001,
  505. min: 0
  506. },
  507. {
  508. key: 'inPlaceRotationAngularVelocityRatio',
  509. label: '原地旋转角速度系数',
  510. type: 'number',
  511. placeholder: '0.0',
  512. precision: 2,
  513. step: 0.01,
  514. min: 0,
  515. max: 1
  516. },
  517. {
  518. key: 'drivingSteeringAngularVelocityRatio',
  519. label: '行驶转向角速度系数',
  520. type: 'number',
  521. placeholder: '0.0',
  522. precision: 2,
  523. step: 0.01,
  524. min: 0,
  525. max: 1
  526. },
  527. {
  528. key: 'reverseFeedCycles',
  529. label: '倒车进给周期数',
  530. type: 'number',
  531. placeholder: '0',
  532. step: 1,
  533. min: 0
  534. },
  535. {
  536. key: 'forwardPositioningForwardOffset',
  537. label: '前行定位相对车中心前向偏移',
  538. type: 'number',
  539. unit: 'm',
  540. placeholder: '0.0',
  541. precision: 2,
  542. step: 0.01
  543. },
  544. {
  545. key: 'reversePositioningRearOffset',
  546. label: '倒车定位相对车中心后向偏移',
  547. type: 'number',
  548. unit: 'm',
  549. placeholder: '0.0',
  550. precision: 2,
  551. step: 0.01
  552. },
  553. {
  554. key: 'targetPointForwardDistance',
  555. label: '目标点前移距离',
  556. type: 'number',
  557. unit: 'm',
  558. placeholder: '0.0',
  559. precision: 1,
  560. step: 0.1,
  561. min: 0
  562. },
  563. {
  564. key: 'routeDistance',
  565. label: '路线距离',
  566. type: 'number',
  567. unit: 'm',
  568. placeholder: '0.0',
  569. precision: 1,
  570. step: 0.1,
  571. min: 0
  572. },
  573. {
  574. key: 'obstacleDistance',
  575. label: '障碍距离',
  576. type: 'number',
  577. unit: 'm',
  578. placeholder: '0.0',
  579. precision: 1,
  580. step: 0.1,
  581. min: 0
  582. },
  583. {
  584. key: 'maxBypassAngle',
  585. label: '最大绕障角度',
  586. type: 'number',
  587. unit: '°',
  588. placeholder: '0',
  589. step: 1,
  590. min: 0,
  591. max: 180
  592. },
  593. {
  594. key: 'minBypassAngle',
  595. label: '最小绕障角度',
  596. type: 'number',
  597. unit: '°',
  598. placeholder: '0',
  599. step: 1,
  600. min: 0,
  601. max: 180
  602. },
  603. {
  604. key: 'bypassKeepDistance',
  605. label: '绕障后保持距离',
  606. type: 'number',
  607. unit: 'm',
  608. placeholder: '0.0',
  609. precision: 1,
  610. step: 0.1,
  611. min: 0
  612. },
  613. {
  614. key: 'controller',
  615. label: '控制器',
  616. type: 'text',
  617. placeholder: '请输入控制器类型'
  618. },
  619. {
  620. key: 'odometerTopicName',
  621. label: '里程计主题名',
  622. type: 'text',
  623. placeholder: '请输入主题名'
  624. },
  625. {
  626. key: 'enableDebugMode',
  627. label: '开启调试模式',
  628. type: 'switch'
  629. },
  630. {
  631. key: 'maxPassageWidth',
  632. label: '最大通行宽度',
  633. type: 'number',
  634. unit: 'm',
  635. placeholder: '0.0',
  636. precision: 1,
  637. step: 0.1,
  638. min: 0
  639. },
  640. {
  641. key: 'minPassageWidth',
  642. label: '最小通行宽度',
  643. type: 'number',
  644. unit: 'm',
  645. placeholder: '0.0',
  646. precision: 1,
  647. step: 0.1,
  648. min: 0
  649. }
  650. ],
  651. // 定位参数配置项
  652. positioningConfigItems: [
  653. {
  654. key: 'mapResolution',
  655. label: '地图分辨率',
  656. type: 'number',
  657. placeholder: '0.00',
  658. precision: 3,
  659. step: 0.001,
  660. min: 0
  661. },
  662. {
  663. key: 'chargingRoomMapResolution',
  664. label: '充电房地图分辨率',
  665. type: 'number',
  666. placeholder: '0.00',
  667. precision: 3,
  668. step: 0.001,
  669. min: 0
  670. },
  671. {
  672. key: 'recursiveFilterParam',
  673. label: '递推滤波参数',
  674. type: 'number',
  675. placeholder: '0.0',
  676. precision: 2,
  677. step: 0.01,
  678. min: 0,
  679. max: 1
  680. },
  681. {
  682. key: 'filterParam',
  683. label: '滤波参数',
  684. type: 'number',
  685. placeholder: '0.0',
  686. precision: 2,
  687. step: 0.01,
  688. min: 0,
  689. max: 1
  690. },
  691. {
  692. key: 'chargingRoomFilterParam',
  693. label: '充电房滤波参数',
  694. type: 'number',
  695. placeholder: '0.0',
  696. precision: 2,
  697. step: 0.01,
  698. min: 0,
  699. max: 1
  700. },
  701. {
  702. key: 'chargingRoomPositioningStep',
  703. label: '充电房定位步长',
  704. type: 'number',
  705. unit: 'm',
  706. placeholder: '0.0',
  707. precision: 2,
  708. step: 0.01,
  709. min: 0
  710. },
  711. {
  712. key: 'mapUpdateInterpolationDistance',
  713. label: '地图更新插帧距离',
  714. type: 'number',
  715. unit: 'm',
  716. placeholder: '0.0',
  717. precision: 2,
  718. step: 0.01,
  719. min: 0
  720. },
  721. {
  722. key: 'positioningCorrectionPointCloudTopicName',
  723. label: '定位修正点云主题名',
  724. type: 'text',
  725. placeholder: '请输入主题名'
  726. },
  727. {
  728. key: 'deadReckoningPointCloudTopicName',
  729. label: '推算点云主题名',
  730. type: 'text',
  731. placeholder: '请输入主题名'
  732. }
  733. ],
  734. // 避障参数配置项
  735. obstacleConfigItems: [
  736. {
  737. key: 'laserForwardObstacleDetectionStartDistance',
  738. label: '激光前方障碍探测起始距离',
  739. type: 'number',
  740. unit: 'm',
  741. placeholder: '0.0',
  742. precision: 1,
  743. step: 0.1
  744. },
  745. {
  746. key: 'laserRearObstacleDetectionStartDistance',
  747. label: '激光后方障碍探测起始距离',
  748. type: 'number',
  749. unit: 'm',
  750. placeholder: '0.0',
  751. precision: 1,
  752. step: 0.1
  753. },
  754. {
  755. key: 'laserLeftObstacleDetectionStartDistance',
  756. label: '激光左侧障碍探测起始距离',
  757. type: 'number',
  758. unit: 'm',
  759. placeholder: '0.0',
  760. precision: 1,
  761. step: 0.1
  762. },
  763. {
  764. key: 'laserRightObstacleDetectionStartDistance',
  765. label: '激光右侧障碍探测起始距离',
  766. type: 'number',
  767. unit: 'm',
  768. placeholder: '0.0',
  769. precision: 1,
  770. step: 0.1
  771. }
  772. ],
  773. // 规划参数配置项
  774. planningConfigItems: [
  775. {
  776. key: 'multiPointCycleEnabled',
  777. label: '多点循环使能',
  778. type: 'switch'
  779. },
  780. {
  781. key: 'multiPointSortEnabled',
  782. label: '多点排序使能',
  783. type: 'switch'
  784. },
  785. {
  786. key: 'obstacleReplanningTime',
  787. label: '遇障重规划时间',
  788. type: 'number',
  789. unit: 's',
  790. placeholder: '0',
  791. step: 1,
  792. min: 0
  793. }
  794. ],
  795. // 任务管理配置项
  796. taskConfigItems: [
  797. {
  798. key: 'autonomousChargingEnabled',
  799. label: '自主充电使能',
  800. type: 'switch'
  801. },
  802. {
  803. key: 'autonomousChargingLowBatteryThreshold',
  804. label: '自主充电低电量阈值',
  805. type: 'number',
  806. placeholder: '0.0',
  807. precision: 2,
  808. step: 0.01,
  809. min: 0,
  810. max: 1
  811. }
  812. ]
  813. }
  814. },
  815. async created() {
  816. await this.loadConfig()
  817. this.calculateScreenSize()
  818. window.addEventListener('resize', this.calculateScreenSize)
  819. },
  820. beforeDestroy() {
  821. window.removeEventListener('resize', this.calculateScreenSize)
  822. },
  823. methods: {
  824. // 加载配置
  825. async loadConfig() {
  826. this.loading = true
  827. try {
  828. const response = await runtimeApi.getRuntimeParams()
  829. if (response.code === 200 && response.data) {
  830. this.config = { ...this.config, ...response.data }
  831. }
  832. } catch (error) {
  833. console.warn('加载运行参数失败,使用默认值:', error)
  834. this.$message.warning('加载配置失败,使用默认参数')
  835. } finally {
  836. this.loading = false
  837. }
  838. },
  839. // 保存单个参数
  840. async handleParamSave(key, value) {
  841. try {
  842. const response = await runtimeApi.updateParam({ key, value })
  843. if (response.code === 200) {
  844. this.config[key] = value
  845. // 不显示保存成功消息,由组件内部显示
  846. } else {
  847. throw new Error(response.message || '保存失败')
  848. }
  849. } catch (error) {
  850. this.$message.error('保存失败: ' + error.message)
  851. throw error // 重新抛出错误,让组件显示错误状态
  852. }
  853. },
  854. // 取消参数编辑
  855. handleParamCancel() {
  856. // 可以在这里添加取消编辑的逻辑
  857. console.log('参数编辑已取消')
  858. },
  859. // 检查项目是否可见(搜索过滤)
  860. isItemVisible(item) {
  861. if (!this.searchKeyword.trim()) {
  862. return true
  863. }
  864. const keyword = this.searchKeyword.toLowerCase().trim()
  865. return item.label.toLowerCase().includes(keyword)
  866. },
  867. // 检查分组是否有可见项目
  868. hasVisibleItems(groupType) {
  869. let items = []
  870. switch (groupType) {
  871. case 'control':
  872. items = this.controlConfigItems
  873. break
  874. case 'positioning':
  875. items = this.positioningConfigItems
  876. break
  877. case 'obstacle':
  878. items = this.obstacleConfigItems
  879. break
  880. case 'planning':
  881. items = this.planningConfigItems
  882. break
  883. case 'task':
  884. items = this.taskConfigItems
  885. break
  886. default:
  887. return false
  888. }
  889. return items.some(item => this.isItemVisible(item))
  890. },
  891. // 计算屏幕尺寸
  892. calculateScreenSize() {
  893. this.isLargeScreen = window.innerWidth >= 1440
  894. }
  895. }
  896. }
  897. </script>
  898. <style lang="scss" scoped>
  899. .runtime-config-simple {
  900. min-height: 100vh;
  901. background: var(--color-bg-secondary);
  902. padding-bottom: var(--spacing-6);
  903. .page-header {
  904. background: var(--color-bg-card);
  905. border-bottom: 1px solid var(--color-border-primary);
  906. padding: var(--spacing-6);
  907. margin-bottom: var(--spacing-6);
  908. box-shadow: 0 6px 18px rgba(2, 6, 23, 0.05);
  909. .header-content {
  910. max-width: 1200px;
  911. margin: 0 auto;
  912. .header-main {
  913. .page-title {
  914. color: var(--color-text-primary);
  915. font-size: var(--font-size-3xl);
  916. font-weight: var(--font-weight-bold);
  917. margin: 0 0 var(--spacing-2) 0;
  918. line-height: var(--line-height-tight);
  919. }
  920. .page-desc {
  921. color: var(--color-text-secondary);
  922. font-size: var(--font-size-base);
  923. margin: 0 0 var(--spacing-4) 0;
  924. line-height: var(--line-height-relaxed);
  925. }
  926. }
  927. }
  928. .search-section {
  929. max-width: 1200px;
  930. margin: 0 auto;
  931. .search-input {
  932. max-width: 400px;
  933. ::v-deep .el-input__inner {
  934. border-radius: var(--radius-lg);
  935. border: 1px solid var(--color-border-primary);
  936. background: var(--color-bg-card);
  937. transition: all var(--duration-200) var(--ease-out);
  938. &:focus {
  939. border-color: var(--color-primary);
  940. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15);
  941. }
  942. }
  943. ::v-deep .el-input__prefix {
  944. color: var(--color-text-tertiary);
  945. }
  946. }
  947. }
  948. }
  949. .page-content {
  950. max-width: 1200px;
  951. margin: 0 auto;
  952. padding: 0 var(--spacing-6);
  953. }
  954. }
  955. /* 配置卡片网格 */
  956. .config-cards {
  957. display: flex;
  958. flex-direction: column;
  959. gap: var(--spacing-5);
  960. }
  961. /* 控制参数卡片 - 全宽显示 */
  962. .control-card {
  963. width: 100%;
  964. .control-content {
  965. display: grid;
  966. grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  967. gap: 0;
  968. @media (max-width: 1024px) {
  969. grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
  970. }
  971. @media (max-width: 768px) {
  972. grid-template-columns: 1fr;
  973. }
  974. }
  975. }
  976. /* 其他参数分组网格 */
  977. .other-params-grid {
  978. display: grid;
  979. grid-template-columns: 1fr;
  980. gap: var(--spacing-5);
  981. &.two-columns {
  982. grid-template-columns: repeat(2, minmax(0, 1fr));
  983. }
  984. }
  985. /* 配置卡片样式 */
  986. .config-card {
  987. background: var(--color-bg-card);
  988. border-radius: var(--radius-xl);
  989. box-shadow: 0 6px 18px rgba(2, 6, 23, 0.05);
  990. border: 1px solid var(--color-border-primary);
  991. transition: all var(--duration-200) var(--ease-out);
  992. animation: slideInUp 0.3s ease-out;
  993. &:hover {
  994. box-shadow: 0 8px 24px rgba(2, 6, 23, 0.08);
  995. border-color: var(--color-border-secondary);
  996. }
  997. .card-header {
  998. padding: var(--spacing-5);
  999. border-bottom: 1px solid var(--color-border-secondary);
  1000. .card-title {
  1001. font-size: var(--font-size-lg);
  1002. font-weight: var(--font-weight-semibold);
  1003. color: var(--color-text-primary);
  1004. margin: 0 0 var(--spacing-1) 0;
  1005. line-height: var(--line-height-tight);
  1006. }
  1007. .card-desc {
  1008. font-size: var(--font-size-sm);
  1009. color: var(--color-text-tertiary);
  1010. margin: 0;
  1011. line-height: var(--line-height-relaxed);
  1012. opacity: 0.8;
  1013. }
  1014. }
  1015. .card-content {
  1016. padding: var(--spacing-4) var(--spacing-5) var(--spacing-5);
  1017. }
  1018. }
  1019. /* 空状态样式 */
  1020. .empty-state {
  1021. display: flex;
  1022. flex-direction: column;
  1023. align-items: center;
  1024. justify-content: center;
  1025. height: 120px;
  1026. color: var(--color-text-quaternary);
  1027. background: rgba(0, 0, 0, 0.02);
  1028. border-radius: var(--radius-base);
  1029. border: 1px dashed var(--color-border-secondary);
  1030. i {
  1031. font-size: var(--font-size-2xl);
  1032. margin-bottom: var(--spacing-2);
  1033. opacity: 0.5;
  1034. }
  1035. p {
  1036. margin: 0;
  1037. font-size: var(--font-size-sm);
  1038. opacity: 0.8;
  1039. }
  1040. }
  1041. // 滑入动画
  1042. @keyframes slideInUp {
  1043. from {
  1044. opacity: 0;
  1045. transform: translateY(20px);
  1046. }
  1047. to {
  1048. opacity: 1;
  1049. transform: translateY(0);
  1050. }
  1051. }
  1052. /* 响应式断点 */
  1053. @media (max-width: 768px) {
  1054. .runtime-config-simple {
  1055. .page-header {
  1056. padding: var(--spacing-4);
  1057. margin-bottom: var(--spacing-4);
  1058. .header-content {
  1059. .header-main {
  1060. .page-title {
  1061. font-size: var(--font-size-2xl);
  1062. }
  1063. .page-desc {
  1064. font-size: var(--font-size-sm);
  1065. }
  1066. }
  1067. }
  1068. .search-section {
  1069. .search-input {
  1070. max-width: 100%;
  1071. }
  1072. }
  1073. }
  1074. .page-content {
  1075. padding: 0 var(--spacing-4);
  1076. }
  1077. }
  1078. .config-cards {
  1079. gap: var(--spacing-4);
  1080. }
  1081. .other-params-grid {
  1082. grid-template-columns: 1fr !important;
  1083. gap: var(--spacing-4);
  1084. }
  1085. .config-card {
  1086. .card-header {
  1087. padding: var(--spacing-4);
  1088. }
  1089. .card-content {
  1090. padding: var(--spacing-3) var(--spacing-4) var(--spacing-4);
  1091. }
  1092. }
  1093. }
  1094. // 暗色主题适配
  1095. html.dark {
  1096. .runtime-config-simple {
  1097. .page-header {
  1098. background: var(--color-bg-tertiary);
  1099. box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
  1100. .search-section {
  1101. .search-input {
  1102. ::v-deep .el-input__inner {
  1103. background: var(--color-bg-quaternary);
  1104. border-color: var(--color-border-tertiary);
  1105. color: var(--color-text-primary);
  1106. }
  1107. }
  1108. }
  1109. }
  1110. }
  1111. .config-card {
  1112. background: var(--color-bg-tertiary);
  1113. box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
  1114. border-color: var(--color-border-tertiary);
  1115. &:hover {
  1116. box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  1117. border-color: var(--color-border-secondary);
  1118. }
  1119. .card-header {
  1120. border-bottom-color: var(--color-border-tertiary);
  1121. .card-title {
  1122. color: var(--color-text-primary);
  1123. }
  1124. .card-desc {
  1125. color: var(--color-text-quaternary);
  1126. }
  1127. }
  1128. }
  1129. .empty-state {
  1130. background: rgba(255, 255, 255, 0.03);
  1131. border-color: var(--color-border-tertiary);
  1132. }
  1133. }
  1134. </style>