vehicleConfigurationDetail.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. <template>
  2. <div class="vehicleConfigurationDetailPanel">
  3. <el-form ref="form" :model="form" :rules="rules" label-width="108px">
  4. <div class="inputBox flexBox">
  5. <span class="label">配置ID</span>
  6. <div>{{ form.configCode }}</div>
  7. </div>
  8. <div class="flexBox headBox">
  9. <el-form-item label="配置名称:" prop="configName">
  10. <el-input
  11. placeholder="请输入"
  12. maxlength="60"
  13. v-autoTrim="{ obj: form, key: 'configName' }"
  14. v-model="form.configName"
  15. >
  16. </el-input>
  17. </el-form-item>
  18. <el-form-item label="配置描述:" prop="configDescription">
  19. <el-input
  20. placeholder="请输入"
  21. maxlength="200"
  22. v-autoTrim="{ obj: form, key: 'configDescription' }"
  23. v-model="form.configDescription"
  24. >
  25. </el-input>
  26. </el-form-item>
  27. <el-form-item label="车辆名称:" prop="vehicleId">
  28. <el-select v-model="form.vehicleId" @change="vehicleChange">
  29. <el-option
  30. v-for="item in vehicleNameList"
  31. :label="item.vehicleName"
  32. :value="item.id"
  33. :key="item.id"
  34. ></el-option>
  35. </el-select>
  36. </el-form-item>
  37. <el-form-item label="车辆描述:" prop="vehicleDescription">
  38. <el-input
  39. placeholder="请输入"
  40. maxlength="200"
  41. v-autoTrim="{ obj: form, key: 'vehicleDescription' }"
  42. v-model="form.vehicleDescription"
  43. disabled
  44. >
  45. </el-input>
  46. </el-form-item>
  47. </div>
  48. </el-form>
  49. <div class="contentBox">
  50. <div>
  51. <handle-config-list
  52. ref="handleConfigList"
  53. @curItem="curItem"
  54. @addOne="addOne"
  55. @delOne="delOne"
  56. :configList="configList"
  57. :curOne="curOne"
  58. ></handle-config-list>
  59. </div>
  60. <div
  61. class="model"
  62. v-bind:style="{ width: width + 'px', height: width / 2 + 'px' }"
  63. >
  64. <!-- <img :src="modelImgSrc" width="100%" /> -->
  65. <canvas-sensor
  66. ref="canvasSensor"
  67. :coordinate="coordinate"
  68. :modelImgSrc="modelImgSrc"
  69. ></canvas-sensor>
  70. </div>
  71. <div class="conditions">
  72. <div class="btnBox">
  73. <el-button type="primary" @click="saveConfig"
  74. >保存设置</el-button
  75. >
  76. </div>
  77. <el-form
  78. ref="formA"
  79. :model="formA"
  80. :rules="rulesA"
  81. label-width="108px"
  82. >
  83. <div class="forms">
  84. <el-form-item label="X(m):" prop="sensorX">
  85. <el-input
  86. placeholder="请输入"
  87. maxlength="12"
  88. v-autoTrim="{ obj: formA, key: 'sensorX' }"
  89. v-model="formA.sensorX"
  90. @input="inputChange"
  91. >
  92. </el-input>
  93. </el-form-item>
  94. <el-form-item label="Y(m):" prop="sensorY">
  95. <el-input
  96. placeholder="请输入"
  97. maxlength="12"
  98. v-autoTrim="{ obj: formA, key: 'sensorY' }"
  99. v-model="formA.sensorY"
  100. @input="inputChange"
  101. >
  102. </el-input>
  103. </el-form-item>
  104. <el-form-item label="Z(m):" prop="sensorZ">
  105. <el-input
  106. placeholder="请输入"
  107. maxlength="12"
  108. v-autoTrim="{ obj: formA, key: 'sensorZ' }"
  109. v-model="formA.sensorZ"
  110. >
  111. </el-input>
  112. </el-form-item>
  113. <el-form-item label="横摆角(deg):" prop="sensorH">
  114. <el-input
  115. placeholder="请输入"
  116. maxlength="12"
  117. v-autoTrim="{ obj: formA, key: 'sensorH' }"
  118. v-model="formA.sensorH"
  119. @input="inputChange"
  120. >
  121. </el-input>
  122. </el-form-item>
  123. <el-form-item label="俯仰角(deg):" prop="sensorP">
  124. <el-input
  125. placeholder="请输入"
  126. maxlength="12"
  127. v-autoTrim="{ obj: formA, key: 'sensorP' }"
  128. v-model="formA.sensorP"
  129. >
  130. </el-input>
  131. </el-form-item>
  132. <el-form-item label="翻滚角(deg):" prop="sensorR">
  133. <el-input
  134. placeholder="请输入"
  135. maxlength="12"
  136. v-autoTrim="{ obj: formA, key: 'sensorR' }"
  137. v-model="formA.sensorR"
  138. >
  139. </el-input>
  140. </el-form-item>
  141. <el-form-item
  142. label="端口"
  143. prop="sensorPort"
  144. v-show="curOne.name === 'ogt'"
  145. >
  146. <el-input
  147. placeholder="请输入"
  148. maxlength="9"
  149. v-autoTrim="{ obj: formA, key: 'sensorPort' }"
  150. v-model="formA.sensorPort"
  151. >
  152. </el-input>
  153. </el-form-item>
  154. </div>
  155. </el-form>
  156. </div>
  157. </div>
  158. <div class="btns">
  159. <el-button
  160. v-if="form.share === '0' || form.share === ''"
  161. type="primary"
  162. @click="save(false)"
  163. >保存</el-button
  164. >
  165. <el-button
  166. v-if="form.share === '0' || form.share === '1'"
  167. type="primary"
  168. @click="save(true)"
  169. >另存为</el-button
  170. >
  171. <el-button type="primary" plain @click="cancel">取消</el-button>
  172. </div>
  173. </div>
  174. </template>
  175. <script>
  176. import handleConfigList from "./components/handleConfigList.vue";
  177. import canvasSensor from "./components/canvasVehicleConfiguration.vue";
  178. import { mapState } from "vuex";
  179. export default {
  180. name: "vehicleConfigurationDetail", // 车辆配置详情
  181. components: { handleConfigList, canvasSensor },
  182. data() {
  183. // 校验最多4位小数
  184. let validateNum = (rule, value, callback) => {
  185. !/^(-?(0|[1-9][0-9]*))(\.\d{1,4})?$/.test(value) &&
  186. callback(new Error(rule.message));
  187. callback();
  188. };
  189. // 校验不大于180且不小于-180
  190. let validateNumC = (rule, value, callback) => {
  191. if (value < -180 || value > 180) {
  192. callback(new Error(rule.message));
  193. return;
  194. }
  195. callback();
  196. };
  197. return {
  198. vehicleNameList: [],
  199. form: {
  200. id: "",
  201. configCode: "", // 配置ID
  202. configName: "", // 配置名称
  203. configDescription: "", // 配置描述
  204. vehicleName: "", // 车辆名称
  205. vehicleId: "", // 车辆名称
  206. vehicleDescription: "", // 车辆描述
  207. share: "",
  208. },
  209. formA: {
  210. sensorX: "", // 传感器横向偏移量(x轴)
  211. sensorY: "", // 传感器横向偏移量(y轴)
  212. sensorZ: "", // 传感器横向偏移量(z轴)
  213. sensorH: "", // 横摆角
  214. sensorP: "", // 俯仰角
  215. sensorR: "", // 横滚角
  216. sensorPort: "", // 端口
  217. },
  218. rules: {
  219. configName: [
  220. { required: true, message: "请输入", trigger: "blur" },
  221. ],
  222. configDescription: [
  223. { required: true, message: "请输入", trigger: "blur" },
  224. ],
  225. vehicleId: [
  226. {
  227. required: true,
  228. message: "请选择",
  229. trigger: "change",
  230. },
  231. ],
  232. vehicleDescription: [
  233. { required: true, message: "请输入", trigger: "blur" },
  234. ],
  235. },
  236. rulesA: {
  237. sensorX: [
  238. {
  239. required: true,
  240. message: "请输入",
  241. trigger: "blur",
  242. },
  243. {
  244. validator: validateNum,
  245. message: "请输入最多带有4位小数的数字",
  246. trigger: ["blur"],
  247. },
  248. ],
  249. sensorY: [
  250. {
  251. required: true,
  252. message: "请输入",
  253. trigger: "blur",
  254. },
  255. {
  256. validator: validateNum,
  257. message: "请输入最多带有4位小数的数字",
  258. trigger: ["blur"],
  259. },
  260. ],
  261. sensorZ: [
  262. {
  263. required: true,
  264. message: "请输入",
  265. trigger: "blur",
  266. },
  267. {
  268. validator: validateNum,
  269. message: "请输入最多带有4位小数的数字",
  270. trigger: ["blur"],
  271. },
  272. ],
  273. sensorH: [
  274. { required: true, message: "请输入", trigger: "blur" },
  275. {
  276. validator: validateNum,
  277. message: "请输入最多带有4位小数的数字",
  278. trigger: ["blur"],
  279. },
  280. {
  281. validator: validateNumC,
  282. message: "请输入不小于-180且不大于180的数字",
  283. trigger: ["blur"],
  284. },
  285. ],
  286. sensorP: [
  287. { required: true, message: "请输入", trigger: "blur" },
  288. {
  289. validator: validateNum,
  290. message: "请输入最多带有4位小数的数字",
  291. trigger: ["blur"],
  292. },
  293. {
  294. validator: validateNumC,
  295. message: "请输入不小于-180且不大于180的数字",
  296. trigger: ["blur"],
  297. },
  298. ],
  299. sensorR: [
  300. { required: true, message: "请输入", trigger: "blur" },
  301. {
  302. validator: validateNum,
  303. message: "请输入最多带有4位小数的数字",
  304. trigger: ["blur"],
  305. },
  306. {
  307. validator: validateNumC,
  308. message: "请输入不小于-180且不大于180的数字",
  309. trigger: ["blur"],
  310. },
  311. ],
  312. sensorPort: [
  313. { required: false, message: "请输入", trigger: "blur" },
  314. ],
  315. },
  316. // 传感器对象集合
  317. configList: {
  318. camera: [],
  319. ogt: [],
  320. lidar: [],
  321. gps: [],
  322. },
  323. // 用于当前选中项的展示
  324. curOne: {
  325. name: "",
  326. index: -1,
  327. },
  328. modelImgSrc: require("@/assets/common/image/others/carTopView.png"), // 车辆图片地址
  329. // canDraw: false,
  330. width: 300,
  331. coordinate: { x: 0, y: 0 }, // 存放传递给canvas的值
  332. };
  333. },
  334. watch: {
  335. curOne(val) {
  336. if (val.name === "ogt") {
  337. // 校验端口号
  338. let validatePort = (rule, value, callback) => {
  339. !/^(\d{5})$/.test(value) &&
  340. callback(new Error(rule.message));
  341. if (value > 65535 || value < 62000) {
  342. callback(new Error(rule.message));
  343. return;
  344. }
  345. callback();
  346. };
  347. let validatePortNoRepeat = (rule, value, callback) => {
  348. let ports = this.configList.ogt.map(
  349. (i) => i.sensorPort + ""
  350. );
  351. ports.splice(this.curOne.index, 1);
  352. if (ports.includes(value)) {
  353. callback(new Error(rule.message));
  354. return;
  355. } else {
  356. callback();
  357. }
  358. };
  359. this.rulesA.sensorPort[0].required = true;
  360. this.rulesA.sensorPort[1] = {
  361. validator: validatePort,
  362. message: "请输入62000至65535之间的5位数字",
  363. trigger: ["blur"],
  364. };
  365. this.rulesA.sensorPort[2] = {
  366. validator: validatePortNoRepeat,
  367. message: "端口号重复",
  368. trigger: ["blur"],
  369. };
  370. } else {
  371. this.rulesA.sensorPort[0].required = false;
  372. this.rulesA.sensorPort.length = 1;
  373. }
  374. },
  375. },
  376. computed: {
  377. ...mapState(["fileHost", "fileUrl"]),
  378. },
  379. methods: {
  380. // 获取车辆名称私有下拉列表
  381. async getMyVehicleList() {
  382. await this.$axios({
  383. method: "post",
  384. url: this.$api.modelLibrary.getMyVehicleList,
  385. data: {},
  386. }).then((res) => {
  387. if (res.code == 200 && res.info) {
  388. this.vehicleNameList = res.info;
  389. } else {
  390. this.$message.error(res.message || "获取信息失败");
  391. }
  392. });
  393. },
  394. getImgUrl(addr) {
  395. let url = "";
  396. if (process.env.VUE_APP_IS_DEV == "true") {
  397. url = this.fileHost + this.fileUrl;
  398. } else {
  399. url = this.fileUrl;
  400. }
  401. let token = localStorage.getItem("Authorization").split(" ")[1];
  402. let src = `${url}?objectName=${addr}&access_token=${token}`;
  403. return src;
  404. },
  405. vehicleChange(id) {
  406. let item = this.vehicleNameList.find((i) => i.id == id);
  407. this.form.vehicleDescription = item.description;
  408. this.modelImgSrc = this.getImgUrl(item.vehicleTopView);
  409. this.$refs.canvasSensor.drawBg();
  410. },
  411. // 验证各传感器数组的每一项表单数据是否齐全
  412. validateSensorsList() {
  413. // console.log(Object.keys(this.configList));
  414. // console.log(Object.values(this.configList));
  415. // console.log(Object.entries(this.configList));
  416. let lists = Object.entries(this.configList);
  417. for (let index = 0; index < lists.length; index++) {
  418. const element = lists[index];
  419. let oneIndex = element[1].findIndex(
  420. (i) => i.sensorX == null && i.sensorY == null
  421. );
  422. if (oneIndex > -1) {
  423. this.$refs.handleConfigList.configHandle(
  424. element[0],
  425. oneIndex
  426. );
  427. // 如果对应项未展开 让其展开
  428. if (
  429. element[0] === "camera" &&
  430. !this.$refs.handleConfigList.isActiveA
  431. ) {
  432. this.$refs.handleConfigList.isActiveA = true;
  433. } else if (
  434. element[0] === "ogt" &&
  435. !this.$refs.handleConfigList.isActiveB
  436. ) {
  437. this.$refs.handleConfigList.isActiveB = true;
  438. } else if (
  439. element[0] === "lidar" &&
  440. !this.$refs.handleConfigList.isActiveC
  441. ) {
  442. this.$refs.handleConfigList.isActiveC = true;
  443. } else if (
  444. element[0] === "gps" &&
  445. !this.$refs.handleConfigList.isActiveE
  446. ) {
  447. this.$refs.handleConfigList.isActiveE = true;
  448. }
  449. this.$message.error("请先设置数据并保存");
  450. return false;
  451. }
  452. }
  453. return true;
  454. },
  455. save(isAdd) {
  456. this.$refs.form.validate((valid) => {
  457. if (valid) {
  458. if (isAdd) {
  459. this.form.id = "";
  460. }
  461. if (!this.validateSensorsList()) return;
  462. let data = Object.assign({}, this.form, {
  463. configSensors: this.configList,
  464. });
  465. this.$axios({
  466. method: "post",
  467. url: this.$api.modelLibrary.saveConfig,
  468. data,
  469. }).then((res) => {
  470. if (res.code == 200) {
  471. this.$message.success("保存成功");
  472. this.cancel();
  473. } else {
  474. this.$message.error(res.message || "保存失败");
  475. }
  476. });
  477. }
  478. });
  479. },
  480. /**
  481. * info 新增传感器信息
  482. * type 传感器类型
  483. */
  484. addOne(info, type) {
  485. // 如果列表里没有对应类型的数组则需手动添加
  486. if (!this.configList[type]) {
  487. this.configList[type] = [];
  488. }
  489. //若直接传info即this.form,可能是由于传入了响应式,造成数据会变动
  490. this.configList[type].unshift({ ...info });
  491. },
  492. delOne(type, index) {
  493. this.configList[type].splice(index, 1);
  494. },
  495. curItem(item, isAdd) {
  496. this.$refs.formA.resetFields();
  497. this.curOne = item;
  498. if (isAdd) {
  499. this.formA.sensorX = "0";
  500. this.formA.sensorY = "0";
  501. this.formA.sensorZ = "0";
  502. this.formA.sensorH = "0";
  503. this.formA.sensorP = "0";
  504. this.formA.sensorR = "0";
  505. if (this.curOne.name === "ogt") {
  506. this.formA.sensorPort = "62001";
  507. }
  508. } else {
  509. Object.assign(
  510. this.formA,
  511. this.configList[item.name][item.index]
  512. );
  513. }
  514. this.$refs.canvasSensor.clear();
  515. this.inputChange();
  516. },
  517. saveConfig() {
  518. if (this.curOne.index < 0) {
  519. this.$message.info("请先选择传感器");
  520. return;
  521. }
  522. this.$refs.formA.validate((valid) => {
  523. if (valid) {
  524. Object.assign(
  525. this.configList[this.curOne.name][this.curOne.index],
  526. this.formA
  527. );
  528. this.$message.success("保存设置成功");
  529. }
  530. });
  531. },
  532. cancel() {
  533. this.$router.replace({ path: "/vehicleConfigurationList" });
  534. },
  535. domHandle() {
  536. $(document).ready(() => {
  537. this.width = $(
  538. ".vehicleConfigurationDetailPanel .model"
  539. ).outerWidth();
  540. // this.canDraw = true;
  541. });
  542. },
  543. inputChange() {
  544. if (this.curOne.index < 0) return;
  545. if (
  546. this.formA.sensorX === "" ||
  547. this.formA.sensorY === "" ||
  548. this.formA.sensorH === ""
  549. )
  550. return;
  551. this.coordinate = {
  552. x: this.formA.sensorX,
  553. y: this.formA.sensorY,
  554. h: this.formA.sensorH,
  555. };
  556. },
  557. },
  558. mounted() {
  559. setTimeout(() => {
  560. this.domHandle();
  561. }, 0);
  562. this.getMyVehicleList();
  563. if (this.$route.query.id) {
  564. let id = "";
  565. this.form.id = id = this.$route.query.id;
  566. this.form.share = this.$route.query.share;
  567. if (id) {
  568. this.$axios({
  569. method: "post",
  570. url: this.$api.modelLibrary.getConfigInfo,
  571. data: {
  572. id,
  573. },
  574. }).then((res) => {
  575. if (res.code == 200 && res.info) {
  576. this.form = res.info;
  577. this.configList = res.info.configSensors;
  578. this.modelImgSrc = this.getImgUrl(
  579. res.info.vehicleTopView
  580. );
  581. // this.validateSensorsList();
  582. } else {
  583. this.$message.error(res.message || "获取信息失败");
  584. }
  585. });
  586. }
  587. }
  588. },
  589. };
  590. </script>
  591. <style lang='less' scoped>
  592. .vehicleConfigurationDetailPanel {
  593. margin: 55px 50px 0;
  594. }
  595. .headBox {
  596. .el-form-item {
  597. margin-right: 20px;
  598. }
  599. }
  600. .inputBox.flexBox {
  601. margin-bottom: 22px;
  602. .label {
  603. width: 88px;
  604. }
  605. div {
  606. line-height: 32px;
  607. }
  608. }
  609. .contentBox {
  610. display: flex;
  611. justify-content: space-between;
  612. margin-top: 30px;
  613. .model {
  614. // width: 400px;
  615. flex: 1;
  616. margin: 0 10%;
  617. }
  618. .conditions {
  619. .btnBox {
  620. padding: 0 0 30px;
  621. }
  622. .forms /deep/ .el-form-item__label {
  623. width: 120px !important;
  624. }
  625. .forms /deep/ .el-form-item__content {
  626. margin-left: 120px !important;
  627. }
  628. }
  629. }
  630. .btns {
  631. padding-top: 30px;
  632. text-align: center;
  633. }
  634. </style>