voyageDetail.vue 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
  1. <template>
  2. <div class="line-container-p24">
  3. <i class="el-icon-arrow-left"></i>
  4. <div
  5. class="dib go-back ml8 pointer"
  6. @click="router.replace('/voyage/voyageList')"
  7. >
  8. 返回航次列表
  9. </div>
  10. </div>
  11. <div class="container-title df aic jcsb">
  12. <div>航次信息</div>
  13. <el-button
  14. style="width: 220px; margin-right: 20px"
  15. type="primary"
  16. @click="downloadExcel"
  17. :loading="isLoadingExcel"
  18. >
  19. 下载船舶跟踪表
  20. </el-button>
  21. </div>
  22. <div class="line-container-p24">
  23. <div class="line">
  24. <div class="info-line">
  25. <div class="info-line-title">航次名称</div>
  26. <el-input
  27. class="info-line-text"
  28. v-model="voyage.voyageName"
  29. disabled
  30. ></el-input>
  31. </div>
  32. <div class="info-gap" v-if="shipAudits.length"></div>
  33. <div class="info-line">
  34. <div class="info-line-title">货主</div>
  35. <el-input
  36. class="info-line-text"
  37. v-model="voyage.cargoOwnerName"
  38. disabled
  39. ></el-input>
  40. </div>
  41. </div>
  42. <!-- <div class="line">
  43. <div class="info-line">
  44. <div class="info-line-title">船东</div>
  45. <el-input
  46. class="info-line-text"
  47. v-model="voyage.shipOwnerName"
  48. disabled
  49. ></el-input>
  50. </div>
  51. <div class="info-line">
  52. <div class="info-line-title">船东手机号</div>
  53. <el-input
  54. class="info-line-text"
  55. v-model="voyage.shipOwnerPhone"
  56. disabled
  57. ></el-input>
  58. </div>
  59. </div> -->
  60. <div class="line">
  61. <div class="info-line">
  62. <div class="info-line-title">船舶名称</div>
  63. <el-input
  64. class="info-line-text"
  65. v-model="voyage.shipName"
  66. disabled
  67. ></el-input>
  68. </div>
  69. <div
  70. class="info-gap"
  71. v-if="shipAudits.length"
  72. @click="isCertsVisable = true"
  73. >
  74. 汇很多认证
  75. </div>
  76. <el-dialog
  77. @open="showCerts"
  78. v-model="isCertsVisable"
  79. destroy-on-close
  80. width="30%"
  81. >
  82. <Certs ref="certs"></Certs>
  83. </el-dialog>
  84. <div class="info-line">
  85. <div class="info-line-title">MMSI</div>
  86. <el-input
  87. class="info-line-text"
  88. v-model="voyage.shipMmsi"
  89. disabled
  90. ></el-input>
  91. </div>
  92. </div>
  93. <div id="map-container" class="map-container"></div>
  94. <div class="line" style="margin-top: 30px">
  95. <div class="info-line">
  96. <div class="info-line-title">开始时间</div>
  97. <el-input
  98. class="info-line-text"
  99. v-model="voyage.startTime"
  100. disabled
  101. ></el-input>
  102. </div>
  103. <div class="info-line">
  104. <div class="info-line-title">结束时间</div>
  105. <el-input
  106. class="info-line-text"
  107. v-model="voyage.endTime"
  108. disabled
  109. ></el-input>
  110. </div>
  111. </div>
  112. <div class="line">
  113. <div class="info-line">
  114. <div class="info-line-title">装货港</div>
  115. <el-input
  116. class="info-line-text"
  117. v-model="voyage.loadPort"
  118. disabled
  119. ></el-input>
  120. </div>
  121. <div class="info-line">
  122. <div class="info-line-title">卸货港</div>
  123. <el-input
  124. class="info-line-text"
  125. v-model="voyage.dischargeProt"
  126. disabled
  127. ></el-input>
  128. </div>
  129. </div>
  130. <div class="line">
  131. <div class="info-line">
  132. <div class="info-line-title">货种</div>
  133. <el-input
  134. class="info-line-text"
  135. v-model="voyage.cargo"
  136. disabled
  137. ></el-input>
  138. </div>
  139. <div class="info-line">
  140. <div style="width: 120px !important" class="info-line-title">货量</div>
  141. <el-input
  142. style="width: 100px !important"
  143. class="info-line-text"
  144. v-model="voyage.tons"
  145. disabled
  146. ></el-input>
  147. <span class="unit">吨</span>
  148. <el-input
  149. style="width: 80px !important"
  150. class="info-line-text"
  151. v-model="voyage.Pieces"
  152. disabled
  153. ></el-input>
  154. <span class="unit">件</span>
  155. </div>
  156. </div>
  157. <div class="container-second-title df aic jcsb mt40">
  158. <div>船舶运输记录详情</div>
  159. <div style="margin-right: 20px">
  160. 信息更新时间 :
  161. <span style="font-size: 18px">
  162. {{ subStr(voyage.infoUpdateTime) }}</span
  163. >
  164. </div>
  165. </div>
  166. <div class="line-container-p24">
  167. <div class="line">
  168. <div class="info-line">
  169. <div class="info-line-title">到达装货港时间</div>
  170. <el-date-picker
  171. class="info-line-text"
  172. v-model="voyage.arrivalLoadPortTime"
  173. type="datetime"
  174. format="YYYY/MM/DD HH:mm:ss"
  175. value-format="YYYY/MM/DD HH:mm:ss"
  176. placeholder="到达装货港时间"
  177. :disabled="disabledStatus"
  178. ></el-date-picker>
  179. </div>
  180. <div class="info-line">
  181. <div class="info-line-title">实装货量</div>
  182. <el-input
  183. style="width: 100px !important"
  184. class="info-line-text"
  185. v-model="voyage.actualLoadTons"
  186. :disabled="disabledStatus"
  187. placeholder="实装吨位"
  188. ></el-input>
  189. <span class="unit">吨</span>
  190. <el-input
  191. style="width: 80px !important"
  192. class="info-line-text"
  193. v-model="voyage.actualLoadPieces"
  194. :disabled="disabledStatus"
  195. placeholder="实装件数"
  196. ></el-input>
  197. <span class="unit">件</span>
  198. </div>
  199. </div>
  200. <div class="line">
  201. <div class="info-line">
  202. <div class="info-line-title">装货开始时间</div>
  203. <el-date-picker
  204. class="info-line-text"
  205. v-model="voyage.loadStartTime"
  206. type="datetime"
  207. format="YYYY/MM/DD HH:mm:ss"
  208. value-format="YYYY/MM/DD HH:mm:ss"
  209. placeholder="装货开始时间"
  210. :disabled="disabledStatus"
  211. ></el-date-picker>
  212. </div>
  213. <div class="info-line">
  214. <div class="info-line-title">装货结束时间</div>
  215. <el-date-picker
  216. class="info-line-text"
  217. v-model="voyage.loadEndTime"
  218. type="datetime"
  219. format="YYYY/MM/DD HH:mm:ss"
  220. value-format="YYYY/MM/DD HH:mm:ss"
  221. placeholder="装货开始时间"
  222. :disabled="disabledStatus"
  223. ></el-date-picker>
  224. </div>
  225. </div>
  226. <div class="line">
  227. <div class="info-line">
  228. <div class="info-line-title">开航时间</div>
  229. <el-date-picker
  230. class="info-line-text"
  231. v-model="voyage.setSailTime"
  232. type="datetime"
  233. format="YYYY/MM/DD HH:mm:ss"
  234. value-format="YYYY/MM/DD HH:mm:ss"
  235. placeholder="开航时间"
  236. :disabled="disabledStatus"
  237. ></el-date-picker>
  238. </div>
  239. <div class="info-line">
  240. <div class="info-line-title">预计到港时间</div>
  241. <el-date-picker
  242. class="info-line-text"
  243. v-model="voyage.expectedArrivalTime"
  244. type="datetime"
  245. format="YYYY/MM/DD"
  246. value-format="YYYY/MM/DD HH:mm:ss"
  247. placeholder="预计到港时间"
  248. :disabled="disabledStatus"
  249. ></el-date-picker>
  250. </div>
  251. </div>
  252. <div class="line">
  253. <div class="info-line">
  254. <div class="info-line-title">实际到港时间</div>
  255. <el-date-picker
  256. class="info-line-text"
  257. v-model="voyage.actualArrivalTime"
  258. type="datetime"
  259. format="YYYY/MM/DD HH:mm:ss"
  260. value-format="YYYY/MM/DD HH:mm:ss"
  261. placeholder="实际到港时间"
  262. :disabled="disabledStatus"
  263. ></el-date-picker>
  264. </div>
  265. <!-- <div class="info-line">
  266. <div class="info-line-title">抵达目的地港时间</div>
  267. <el-date-picker
  268. class="info-line-text"
  269. v-model="voyage.arrivalPortTime"
  270. type="datetime"
  271. format="YYYY/MM/DD HH:mm:ss"
  272. value-format="YYYY/MM/DD HH:mm:ss"
  273. placeholder="抵达目的地港时间"
  274. :disabled="disabledStatus"
  275. ></el-date-picker>
  276. </div> -->
  277. </div>
  278. <div class="line">
  279. <div class="info-line">
  280. <div class="info-line-title">卸货开始时间</div>
  281. <el-date-picker
  282. class="info-line-text"
  283. v-model="voyage.dischargeStartTime"
  284. type="datetime"
  285. format="YYYY/MM/DD HH:mm:ss"
  286. value-format="YYYY/MM/DD HH:mm:ss"
  287. placeholder="卸货开始时间"
  288. :disabled="disabledStatus"
  289. ></el-date-picker>
  290. </div>
  291. <div class="info-line">
  292. <div class="info-line-title">卸货结束时间</div>
  293. <el-date-picker
  294. class="info-line-text"
  295. v-model="voyage.dischargeEndTime"
  296. type="datetime"
  297. format="YYYY/MM/DD HH:mm:ss"
  298. value-format="YYYY/MM/DD HH:mm:ss"
  299. placeholder="卸货结束时间"
  300. :disabled="disabledStatus"
  301. ></el-date-picker>
  302. </div>
  303. </div>
  304. <div class="line">
  305. <div class="info-line">
  306. <div class="info-line-title">实际卸货量</div>
  307. <el-input
  308. style="width: 100px !important"
  309. class="info-line-text"
  310. placeholder="实际卸货吨位"
  311. v-model="voyage.actualDischargeTons"
  312. :disabled="disabledStatus"
  313. ></el-input>
  314. <span class="unit">吨</span>
  315. <el-input
  316. style="width: 80px !important"
  317. class="info-line-text"
  318. placeholder="实际卸货件数"
  319. v-model="voyage.actualDischargePieces"
  320. :disabled="disabledStatus"
  321. ></el-input>
  322. <span class="unit">件</span>
  323. </div>
  324. </div>
  325. <div class="line">
  326. <div class="info-line">
  327. <div class="info-line-title">备注</div>
  328. <el-input
  329. class="info-line-textarea"
  330. v-model="voyage.remark"
  331. autosize
  332. type="textarea"
  333. :disabled="disabledStatus"
  334. ></el-input>
  335. </div>
  336. </div>
  337. </div>
  338. <hr class="hr m30-0" />
  339. <div class="container-second-title df aic jcsb">
  340. <div>卸货记录</div>
  341. <el-button
  342. v-auth="'DOWNLOADDISCHARGE'"
  343. @click="exportDischargeExcel"
  344. style="width: 220px; margin-right: 20px"
  345. type="primary"
  346. :loading="isDischargeLoadingExcel"
  347. >下载卸货信息</el-button
  348. >
  349. </div>
  350. <el-table :data="dischargeList" stripe style="width: 800px">
  351. <el-table-column
  352. type="index"
  353. label="序号"
  354. min-width="80"
  355. align="center"
  356. ></el-table-column>
  357. <el-table-column
  358. prop="dischargeTime"
  359. label="卸货时间"
  360. min-width="120"
  361. align="center"
  362. ></el-table-column>
  363. <el-table-column
  364. prop="dischargeTons"
  365. label="卸货吨位"
  366. min-width="100"
  367. align="center"
  368. ></el-table-column>
  369. <el-table-column
  370. prop="dischargePieces"
  371. label="卸货件数"
  372. min-width="100"
  373. align="center"
  374. ></el-table-column>
  375. <el-table-column label="磅单" min-width="150" align="center">
  376. <template v-slot="scope">
  377. <el-button
  378. @click="showUpdateDischarge(scope.row, scope.$index)"
  379. type="primary"
  380. size="small"
  381. v-if="scope.row.files"
  382. >
  383. 查看
  384. </el-button>
  385. </template>
  386. </el-table-column>
  387. </el-table>
  388. <el-dialog v-model="dialogVisible" title="图片预览" width="30%">
  389. <el-image
  390. :src="dialogImageUrl"
  391. style="height: 100%; width: 100%"
  392. ></el-image>
  393. </el-dialog>
  394. <div style="width: 100%; text-align: right; margin-top: 43px">
  395. <el-pagination
  396. background
  397. layout="prev, pager, next"
  398. :total="total"
  399. @current-change="pageChange"
  400. ></el-pagination>
  401. </div>
  402. <div class="hr m30-0"></div>
  403. <div class="container-second-title df aic jcsb mt40">
  404. <div>汽车装货记录详情</div>
  405. </div>
  406. <el-table :data="truckTableData" stripe>
  407. <el-table-column
  408. type="index"
  409. label="序号"
  410. min-width="120"
  411. align="center"
  412. ></el-table-column>
  413. <el-table-column
  414. prop="portName"
  415. label="港口名称"
  416. min-width="100"
  417. align="center"
  418. ></el-table-column>
  419. <el-table-column
  420. prop="weighTime"
  421. label="称重时间"
  422. min-width="120"
  423. align="center"
  424. ></el-table-column>
  425. <el-table-column
  426. prop="carNum"
  427. label="车号"
  428. min-width="100"
  429. align="center"
  430. ></el-table-column>
  431. <el-table-column
  432. prop="cargoName"
  433. label="货物名称"
  434. min-width="120"
  435. align="center"
  436. ></el-table-column>
  437. <el-table-column
  438. prop="shippingUnit"
  439. label="发货单位"
  440. min-width="100"
  441. align="center"
  442. ></el-table-column>
  443. <el-table-column
  444. prop="receivingUnit"
  445. label="收货单位"
  446. min-width="120"
  447. align="center"
  448. ></el-table-column>
  449. <el-table-column
  450. prop="grossWeight"
  451. label="毛重"
  452. min-width="100"
  453. align="center"
  454. ></el-table-column>
  455. <el-table-column
  456. prop="tare"
  457. label="皮重"
  458. min-width="120"
  459. align="center"
  460. ></el-table-column>
  461. <el-table-column
  462. prop="netWeight"
  463. label="净重"
  464. min-width="100"
  465. align="center"
  466. ></el-table-column>
  467. <el-table-column
  468. prop="shipName"
  469. label="货船名称"
  470. min-width="120"
  471. align="center"
  472. ></el-table-column>
  473. <el-table-column
  474. prop="weigher"
  475. label="司磅员"
  476. min-width="100"
  477. align="center"
  478. ></el-table-column>
  479. <el-table-column label="单据" min-width="150" align="center">
  480. <template v-slot="scope">
  481. <el-button
  482. @click="showTruckRecord(scope.row, scope.$index, '查看单据')"
  483. type="primary"
  484. size="small"
  485. >
  486. 查看
  487. </el-button>
  488. </template>
  489. </el-table-column>
  490. </el-table>
  491. <div style="text-align: right; margin-top: 43px">
  492. <el-pagination
  493. background
  494. layout="prev, pager, next"
  495. :total="truckTotal"
  496. @current-change="truckPageChange"
  497. ></el-pagination>
  498. </div>
  499. <el-dialog
  500. v-model="isAddTruckRecordVisable"
  501. title="查看记录"
  502. width="780px"
  503. center
  504. >
  505. <el-form
  506. :model="truckRecordForm"
  507. inline
  508. style="margin-bottom: 20px"
  509. label-width="100px"
  510. >
  511. <el-form-item label="港口名称">
  512. <el-input
  513. style="width: 240px"
  514. v-model="truckRecordForm.portName"
  515. placeholder="港口名称"
  516. ></el-input>
  517. </el-form-item>
  518. <el-form-item label="称重时间">
  519. <el-date-picker
  520. class="info-line-text"
  521. v-model="truckRecordForm.weighTime"
  522. type="datetime"
  523. format="YYYY/MM/DD HH:mm:ss"
  524. value-format="YYYY/MM/DD HH:mm:ss"
  525. placeholder="称重时间"
  526. ></el-date-picker>
  527. </el-form-item>
  528. <el-form-item label="车号">
  529. <el-input
  530. style="width: 240px"
  531. v-model="truckRecordForm.carNum"
  532. placeholder="车号"
  533. ></el-input>
  534. </el-form-item>
  535. <el-form-item label="货物名称">
  536. <el-input
  537. style="width: 240px"
  538. v-model="truckRecordForm.cargoName"
  539. placeholder="货物名称"
  540. ></el-input>
  541. </el-form-item>
  542. <el-form-item label="发货单位">
  543. <el-input
  544. style="width: 240px"
  545. v-model="truckRecordForm.shippingUnit"
  546. placeholder="发货单位"
  547. ></el-input>
  548. </el-form-item>
  549. <el-form-item label="毛重">
  550. <el-input
  551. style="width: 240px"
  552. v-model="truckRecordForm.grossWeight"
  553. placeholder="毛重"
  554. ></el-input>
  555. </el-form-item>
  556. <el-form-item label="收货单位">
  557. <el-input
  558. style="width: 240px"
  559. v-model="truckRecordForm.receivingUnit"
  560. placeholder="收货单位"
  561. ></el-input>
  562. </el-form-item>
  563. <el-form-item label="皮重">
  564. <el-input
  565. style="width: 240px"
  566. v-model="truckRecordForm.tare"
  567. placeholder="皮重"
  568. ></el-input>
  569. </el-form-item>
  570. <el-form-item label="运输单位">
  571. <el-input
  572. style="width: 240px"
  573. v-model="truckRecordForm.transUnit"
  574. placeholder="运输单位"
  575. ></el-input>
  576. </el-form-item>
  577. <el-form-item label="净重">
  578. <el-input
  579. style="width: 240px"
  580. v-model="truckRecordForm.netWeight"
  581. placeholder="净重"
  582. ></el-input>
  583. </el-form-item>
  584. <el-form-item label="货船名称">
  585. <el-input
  586. style="width: 240px"
  587. v-model="truckRecordForm.shipName"
  588. placeholder="货船名称"
  589. ></el-input>
  590. </el-form-item>
  591. <el-form-item label="司磅员">
  592. <el-input
  593. style="width: 240px"
  594. v-model="truckRecordForm.weigher"
  595. placeholder="司磅员"
  596. ></el-input>
  597. </el-form-item>
  598. <el-form-item label="汽车装货单">
  599. <Uploader
  600. actionUrl="#"
  601. :uploaderId="'truckLoad'"
  602. :params="truckLoadParams"
  603. @onSendFileList="getTruckLoadBillList"
  604. :fileList="truckRecordBillList"
  605. uploadText="上传装货单"
  606. :limit="1"
  607. disabled
  608. ></Uploader>
  609. </el-form-item>
  610. </el-form>
  611. </el-dialog>
  612. </div>
  613. <div class="container-title">单据信息</div>
  614. <div class="line-container-p24" style="padding-left: 60px">
  615. <div class="df aic">
  616. <div class="info-line-title">保险单:</div>
  617. <el-image
  618. style="width: 200px; height: 200px; margin-right: 20px"
  619. :src="item.viewUrl"
  620. v-for="item in policyList"
  621. :key="item"
  622. @click="openMediaModal(item.viewUrl, 1, '保险单查看')"
  623. ></el-image>
  624. </div>
  625. <hr class="hr m30-0" />
  626. <div class="df aic">
  627. <div class="info-line-title">运单:</div>
  628. <el-image
  629. style="width: 200px; height: 200px; margin-right: 20px"
  630. :src="item.viewUrl"
  631. v-for="item in voyageBill"
  632. :key="item"
  633. @click="openMediaModal(item.viewUrl, 1, '运单查看')"
  634. ></el-image>
  635. </div>
  636. </div>
  637. <div class="container-title">航次图片</div>
  638. <div class="line-container-p24">
  639. <div v-if="medias.length" class="medias-content df ffw">
  640. <div class="pic-container">
  641. <div v-for="(item, index) in medias" :key="item" class="pic-main">
  642. <div
  643. :class="[
  644. 'box',
  645. index % 2 == 0 ? '' : 'bottom-box',
  646. item.status == 1 ? 'now-box' : '',
  647. ]"
  648. >
  649. <div class="card-note">
  650. {{ item.shipName }} 拍摄于
  651. <br />
  652. {{ item.createTime }}
  653. </div>
  654. <div class="medias-box" style="position: relative">
  655. <el-image
  656. v-if="item.mediaType == 1"
  657. style="width: 100%; height: 100%"
  658. fit="contain"
  659. :src="item.downloadUrl"
  660. @click="openMediaModal(item.downloadUrl, 1, '图片审核')"
  661. ></el-image>
  662. <video
  663. style="width: 100%; height: 100%"
  664. v-else
  665. :src="item.downloadUrl"
  666. ></video>
  667. <img
  668. @click="openMediaModal(item.downloadUrl, 2, '视频审核')"
  669. v-if="item.mediaType == 2"
  670. src="../../assets/icon-player.png"
  671. style="
  672. object-fit: contain;
  673. width: 40px;
  674. height: 40px;
  675. position: absolute;
  676. top: calc(50% - 20px);
  677. left: calc(50% - 20px);
  678. background: #fff;
  679. border-radius: 50%;
  680. "
  681. alt=""
  682. />
  683. </div>
  684. </div>
  685. <div
  686. :class="[
  687. 's-line',
  688. index % 2 == 0 ? '' : 'top210px',
  689. item.status == 1 ? 'now-s-line' : '',
  690. ]"
  691. ></div>
  692. <div :class="['point', item.status == 1 ? '' : 'now-point']"></div>
  693. <div
  694. :class="['l-line', item.status == 1 ? 'now-l-line' : '']"
  695. v-if="index + 1 != medias.length"
  696. ></div>
  697. </div>
  698. </div>
  699. </div>
  700. <el-dialog v-model="mediaModal" :title="modalTitle">
  701. <el-image
  702. v-if="modalType == 1"
  703. style="height: 60vh; display: flex"
  704. fit="contain"
  705. :src="currentUrl"
  706. :preview-src-list="modalPreview"
  707. ></el-image>
  708. <video
  709. v-else
  710. autoplay
  711. controls
  712. style="width: 100%; height: 100%"
  713. :src="currentUrl"
  714. ></video>
  715. </el-dialog>
  716. <el-dialog
  717. v-model="updateDischargeDialog"
  718. title="查看记录"
  719. width="700px"
  720. center
  721. >
  722. <el-form :model="updateForm" style="margin-bottom: 20px">
  723. <!-- <el-form-item label="记录ID">
  724. <span style="padding-left: 20px">{{ updateForm.id }}</span>
  725. </el-form-item> -->
  726. <el-form-item label="卸货时间">
  727. <el-date-picker
  728. class="info-line-text"
  729. v-model="updateForm.dischargeTime"
  730. type="datetime"
  731. format="YYYY/MM/DD HH:mm:ss"
  732. value-format="YYYY/MM/DD HH:mm:ss"
  733. placeholder="卸货时间"
  734. disabled
  735. ></el-date-picker>
  736. </el-form-item>
  737. <el-form-item label="卸货吨位">
  738. <el-input
  739. style="width: 240px"
  740. v-model="updateForm.dischargeTons"
  741. placeholder="卸货吨位"
  742. disabled
  743. ></el-input>
  744. </el-form-item>
  745. <el-form-item label="磅单查看">
  746. <el-image
  747. style="width: 200px; height: 200px; margin-right: 20px"
  748. v-for="item in updatePoundBillList"
  749. :src="item.viewUrl"
  750. :key="item"
  751. @click="openMediaModal(item.viewUrl, 1, '磅单查看')"
  752. ></el-image>
  753. </el-form-item>
  754. </el-form>
  755. </el-dialog>
  756. </div>
  757. </template>
  758. <script setup>
  759. import { onMounted, reactive, ref, toRefs } from "vue";
  760. import api from "../../apis/fetch";
  761. import { useRoute } from "vue-router";
  762. import _ from "lodash";
  763. import router from "../../router";
  764. import store from "../../store";
  765. import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
  766. import downloadBlobFile from "../../utils/downloadBlobFile";
  767. import url from "../../apis/config";
  768. const route = useRoute();
  769. let map = ref();
  770. let voyage = ref({});
  771. let medias = ref([]);
  772. let coordinates = ref();
  773. let previewSrcList = ref([]);
  774. let policyList = ref([]);
  775. let previewPoundList = ref([]);
  776. let shipAudits = ref([]);
  777. let certs = ref(null);
  778. let isCertsVisable = ref(false);
  779. function showCerts() {
  780. setTimeout(() => {
  781. certs.value.initCerts(shipAudits.value);
  782. });
  783. }
  784. async function getVoyageDetail(type) {
  785. policyList.value = [];
  786. voyageBill.value = [];
  787. previewSrcList.value = [];
  788. let res = await api.getVoyageDetail({
  789. type: localStorage.userType,
  790. voyageId: route.query.id,
  791. });
  792. if (res.data.status == 0) {
  793. if (type) {
  794. ElNotification({
  795. type: "success",
  796. title: res.data.msg,
  797. });
  798. }
  799. coordinates.value = res.data.result.coordinates;
  800. voyage.value = res.data.result.voyage;
  801. medias.value = res.data.result.medias;
  802. shipAudits.value = res.data.result.shipAudits;
  803. for (let i of res.data.result.policys || []) {
  804. policyList.value.push({
  805. ...i,
  806. url: i.viewUrl,
  807. });
  808. }
  809. for (let i of res.data.result.waybills) {
  810. voyageBill.value.push({
  811. ...i,
  812. url: i.viewUrl,
  813. });
  814. }
  815. for (let i of medias.value) {
  816. previewSrcList.value.push(i.downloadUrl);
  817. }
  818. initMap();
  819. } else {
  820. console.log(res);
  821. ElNotification({
  822. type: "error",
  823. title: res.data.msg,
  824. });
  825. }
  826. }
  827. let total = ref(0);
  828. function pageChange(e) {
  829. dischargeCurrentPage.value = e;
  830. getDischargeList();
  831. }
  832. async function addDischarge() {
  833. if (!formInline.value.dischargeTime || !formInline.value.dischargeTons)
  834. return;
  835. let res = await api.addDischarge({
  836. ...formInline.value,
  837. voyageId: route.query.id,
  838. });
  839. if (res.data.status == 0) {
  840. getDischargeList(1);
  841. formInline.value = {};
  842. } else {
  843. console.log(res);
  844. }
  845. }
  846. async function deleteDischarge(id, index) {
  847. let res = await api.deleteDischarge({
  848. id,
  849. });
  850. if (res.data.status == 0) {
  851. dischargeList.value.splice(index, 1);
  852. ElNotification({
  853. type: "success",
  854. title: res.data.msg,
  855. });
  856. } else {
  857. console.log(res);
  858. ElNotification({
  859. type: "error",
  860. title: res.data.msg,
  861. });
  862. }
  863. }
  864. async function exportExcel() {}
  865. let dischargeCurrentPage = ref(1);
  866. let dischargeList = ref();
  867. let formInline = ref({});
  868. async function getDischargeList(type) {
  869. let res = await api.getDischargeList({
  870. voyageId: route.query.id,
  871. currentPage: dischargeCurrentPage.value,
  872. size: 10,
  873. });
  874. if (res.data.status == 0) {
  875. dischargeList.value = res.data.result;
  876. total.value = res.data.total;
  877. } else {
  878. console.log(res);
  879. }
  880. }
  881. let updateForm = ref({});
  882. let updateDischargeDialog = ref(false);
  883. let currentUpdateIndex = ref(-1);
  884. async function updateDischarge() {
  885. let res = await api.updateDischarge({
  886. ...updateForm.value,
  887. });
  888. if (res.data.status == 0) {
  889. dischargeList.value[currentUpdateIndex.value] = res.data.result;
  890. cancelUpdateDischarge();
  891. ElNotification({
  892. type: "success",
  893. title: res.data.msg,
  894. });
  895. } else {
  896. console.log(res);
  897. ElNotification({
  898. type: "error",
  899. title: res.data.msg,
  900. });
  901. }
  902. }
  903. let updatePoundParams = ref([]);
  904. let updatePoundBillList = ref([]);
  905. function showUpdateDischarge(item, index) {
  906. updateDischargeDialog.value = true;
  907. updatePoundBillList.value = [];
  908. let { id, dischargeTons, dischargeTime, voyageId, files } = item;
  909. updatePoundBillList.value = files;
  910. updateForm.value = {
  911. id,
  912. dischargeTons,
  913. dischargeTime,
  914. };
  915. currentUpdateIndex.value = index;
  916. }
  917. function cancelUpdateDischarge() {
  918. updateDischargeDialog.value = false;
  919. updateForm.value = {};
  920. currentUpdateIndex.value = -1;
  921. }
  922. function initMap() {
  923. map.value = new AMap.Map("map-container", {
  924. zoom: 11, //级别
  925. center: [120.563757, 31.891174], //中心点坐标
  926. mapStyle: "amap://styles/f48d96805f5fa7f5aada657c5ee37017",
  927. zoomEnable: false,
  928. dragEnable: false,
  929. });
  930. let toolBar = new AMap.ToolBar({
  931. position: {
  932. top: "40px",
  933. right: "40px",
  934. },
  935. });
  936. let hawkEye = new AMap.HawkEye({
  937. opened: false,
  938. });
  939. map.value.addControl(toolBar);
  940. map.value.addControl(hawkEye);
  941. let markers = [];
  942. for (let i of medias.value) {
  943. let content = `<div style='width:160px'>
  944. ${
  945. i.audit == 1
  946. ? `<img id='img${i.id}' style='width:160px;height:160px;object-fit:contain;' src='${i.viewUrl}'/>`
  947. : ``
  948. }
  949. <img src='https://hhd-pat-1255802371.cos.ap-shanghai.myqcloud.com/frontend/ship-red-icon.png' style='display:block;width:20px;height:20px;margin:6px auto'/
  950. </div>`;
  951. let marker = new AMap.Marker({
  952. content,
  953. position: new AMap.LngLat(i.longitude, i.latitude),
  954. offset: new AMap.Pixel(-75, i.audit == 1 ? -195 : -30),
  955. });
  956. if (i.audit == 1) {
  957. marker.on("click", () => {
  958. openMediaModal(i.viewUrl, 1, "航次图片", i);
  959. });
  960. }
  961. markers.push(marker);
  962. }
  963. let overlayGroups = new AMap.OverlayGroup(markers);
  964. map.value.on("complete", function () {
  965. let t = setTimeout(() => {
  966. map.value.add(overlayGroups);
  967. map.value.setFitView(markers, true, [200, 50, 0, 0], 18);
  968. clearTimeout(t);
  969. }, 2000);
  970. });
  971. }
  972. function setShipMarker(longitude = 121.524761, latitude = 31.228721) {
  973. map.value.setCenter([longitude, latitude]);
  974. var marker = new AMap.Marker({
  975. position: new AMap.LngLat(longitude, latitude),
  976. offset: new AMap.Pixel(-20, -20),
  977. size: new AMap.Size(80, 80),
  978. icon: "https://hhd-pat-1255802371.cos.ap-shanghai.myqcloud.com/frontend/ship-red-icon.png", // 添加 Icon 图标 URL
  979. title: "",
  980. });
  981. map.value.add(marker);
  982. }
  983. let disabledStatus = ref(true);
  984. let updateCache = {};
  985. function changeVoyageInfo() {
  986. updateCache = _.cloneDeep(voyage.value);
  987. disabledStatus.value = false;
  988. }
  989. function cancelVoyageChange() {
  990. voyage.value = updateCache;
  991. disabledStatus.value = true;
  992. }
  993. async function submitVoyageChange() {
  994. let {
  995. id,
  996. transStatus,
  997. loadStartTime,
  998. loadEndTime,
  999. dischargeStartTime,
  1000. dischargeEndTime,
  1001. actualDischargeTons,
  1002. remark,
  1003. } = voyage.value;
  1004. let res = await api.updateVoyage({
  1005. id,
  1006. transStatus,
  1007. loadStartTime,
  1008. loadEndTime,
  1009. dischargeStartTime,
  1010. dischargeEndTime,
  1011. actualDischargeTons,
  1012. remark,
  1013. });
  1014. if (res.data.status == 0) {
  1015. ElNotification({
  1016. type: "success",
  1017. title: res.data.msg,
  1018. });
  1019. disabledStatus.value = true;
  1020. } else {
  1021. ElNotification({
  1022. type: "error",
  1023. title: res.data.msg,
  1024. });
  1025. console.log(res);
  1026. }
  1027. }
  1028. let options = ref([
  1029. { value: 0, label: "请选择" },
  1030. {
  1031. value: 1,
  1032. label: "航行",
  1033. },
  1034. {
  1035. value: 2,
  1036. label: "停泊",
  1037. },
  1038. {
  1039. value: 3,
  1040. label: "装货",
  1041. },
  1042. {
  1043. value: 4,
  1044. label: "运输中",
  1045. },
  1046. {
  1047. value: 5,
  1048. label: "卸货",
  1049. },
  1050. ]);
  1051. async function finishVoyage() {
  1052. if (!voyage.value.dischargeEndTime) return;
  1053. let res = await api.finishVoyage({
  1054. voyageId: route.query.id,
  1055. });
  1056. if (res.data.status == 0) {
  1057. voyage.value.voyageStatus = 2;
  1058. ElNotification({
  1059. type: "success",
  1060. title: res.data.msg,
  1061. });
  1062. } else {
  1063. ElNotification({
  1064. type: "error",
  1065. title: res.data.msg,
  1066. });
  1067. console.log(res);
  1068. }
  1069. }
  1070. let currentUrl = ref("");
  1071. let mediaModal = ref(false);
  1072. let modalType = ref(1);
  1073. let modalTitle = ref();
  1074. let modalPreview = ref([]);
  1075. function openMediaModal(url, type, title) {
  1076. console.log(url, type, title);
  1077. modalPreview.value = [url];
  1078. modalTitle.value = title;
  1079. modalType.value = type;
  1080. currentUrl.value = url;
  1081. mediaModal.value = true;
  1082. }
  1083. async function auditMedia(mediaId, a, index, mediaType) {
  1084. console.log(mediaId, a, index, mediaType);
  1085. let res = await api.auditMedia({
  1086. mediaId,
  1087. audit: a,
  1088. });
  1089. if (res.data.status == 0) {
  1090. medias.value[index].audit = a;
  1091. ElNotification({
  1092. type: "success",
  1093. title: res.data.msg,
  1094. });
  1095. } else {
  1096. console.log(res);
  1097. }
  1098. }
  1099. let dialogImageUrl = ref();
  1100. let dialogVisible = ref(false);
  1101. function handlePictureCardPreview(file) {
  1102. dialogVisible.value = true;
  1103. dialogImageUrl.value = file.url;
  1104. }
  1105. async function handleRemoveBill(file, list) {
  1106. let cache = _.cloneDeep(voyageBill.value);
  1107. console.log(cache);
  1108. ElMessageBox.confirm("确认删除运单?", "Warning", {
  1109. confirmButtonText: "删除",
  1110. cancelButtonText: "取消",
  1111. type: "warning",
  1112. })
  1113. .then(async () => {
  1114. let { id } = file;
  1115. let res = await api.deleteWaybill({
  1116. id,
  1117. });
  1118. if (res.data.status == 0) {
  1119. ElMessage({
  1120. message: "删除成功!",
  1121. type: "success",
  1122. });
  1123. voyageBill.value = list;
  1124. }
  1125. })
  1126. .catch(() => {
  1127. voyageBill.value = cache;
  1128. ElMessage({
  1129. type: "info",
  1130. message: "取消删除",
  1131. });
  1132. });
  1133. }
  1134. let voyageBill = ref([]);
  1135. function billUploadSuccess(response, file, list) {
  1136. list[list.length - 1] = {
  1137. ...response.result,
  1138. url: response.result.viewUrl,
  1139. };
  1140. console.log(list);
  1141. voyageBill.value = list;
  1142. }
  1143. let billParams = ref({
  1144. voyageId: route.query.id,
  1145. });
  1146. async function calExpectedArrivalTime() {
  1147. let res = await api.calExpectedArrivalTime({
  1148. voyageId: route.query.id,
  1149. setSailTime: voyage.value.setSailTime,
  1150. });
  1151. if (res.data.status == 0) {
  1152. voyage.value.expectedArrivalTime = res.data.result;
  1153. }
  1154. }
  1155. let isLoadingExcel = ref(false);
  1156. async function downloadExcel() {
  1157. isLoadingExcel.value = true;
  1158. let res = await downloadBlobFile(
  1159. `${url.baseurl}/voyage/exportExcel`,
  1160. { voyageId: route.query.id },
  1161. "船舶跟踪表",
  1162. "post"
  1163. );
  1164. if (res) {
  1165. isLoadingExcel.value = false;
  1166. }
  1167. }
  1168. let isDischargeLoadingExcel = ref(false);
  1169. async function exportDischargeExcel() {
  1170. isDischargeLoadingExcel.value = true;
  1171. let res = await downloadBlobFile(
  1172. `${url.baseurl}/voyage/exportDischargeExcel`,
  1173. { voyageId: route.query.id },
  1174. "卸货记录表",
  1175. "post"
  1176. );
  1177. if (res) {
  1178. isDischargeLoadingExcel.value = false;
  1179. }
  1180. }
  1181. function subStr(str = "") {
  1182. return str.substring(0, 10);
  1183. }
  1184. let truckTableData = ref([]);
  1185. let truckCurrentPage = ref(1);
  1186. let truckTotal = ref(0);
  1187. async function getTruckLoadRecord() {
  1188. let res = await api.getTruckLoadRecord({
  1189. voyageId: route.query.id,
  1190. currentPage: truckCurrentPage.value,
  1191. size: 10,
  1192. });
  1193. truckTableData.value = res.data.result;
  1194. truckTotal.value = res.data.total;
  1195. }
  1196. function truckPageChange(e) {
  1197. truckCurrentPage.value = e;
  1198. getTruckLoadRecord();
  1199. }
  1200. let truckRecordForm = ref({});
  1201. let isAddTruckRecordVisable = ref(false);
  1202. let truckRecordBillList = ref([]);
  1203. function showTruckRecord(item, index, text) {
  1204. isAddTruckRecordVisable.value = true;
  1205. if (item.file) {
  1206. truckRecordBillList.value[0] = {
  1207. ...item.file,
  1208. url: item.file.viewUrl,
  1209. };
  1210. }
  1211. truckRecordForm.value = { ...item };
  1212. }
  1213. let truckLoadParams = ref({});
  1214. function getTruckLoadBillList() {}
  1215. onMounted(() => {
  1216. getVoyageDetail(1);
  1217. getDischargeList(1);
  1218. getTruckLoadRecord();
  1219. });
  1220. </script>
  1221. <style scoped>
  1222. .map-container {
  1223. width: 100%;
  1224. height: 500px;
  1225. }
  1226. .card-note {
  1227. height: 30px;
  1228. font-size: 12px;
  1229. font-family: PingFangSC-Regular, PingFang SC;
  1230. font-weight: 400;
  1231. color: #777777;
  1232. }
  1233. .medias-box {
  1234. width: 200px;
  1235. height: 200px;
  1236. margin-top: 20px;
  1237. }
  1238. .checkbox-group {
  1239. width: 200px;
  1240. height: 50px;
  1241. margin-top: 20px;
  1242. }
  1243. .medias-content {
  1244. width: 100%;
  1245. height: 600px;
  1246. background: #f7f7f7;
  1247. border-radius: 2px;
  1248. }
  1249. .pic-container {
  1250. width: 100%;
  1251. height: 100%;
  1252. box-sizing: border-box;
  1253. display: flex;
  1254. padding: 30px;
  1255. overflow-x: scroll;
  1256. overflow-y: hidden;
  1257. white-space: nowrap;
  1258. }
  1259. .pic-main {
  1260. position: relative;
  1261. width: 120px;
  1262. }
  1263. .box {
  1264. position: absolute;
  1265. height: 240px;
  1266. width: var(--box-width);
  1267. border: 5px solid #dddddd;
  1268. transition: all 0.5s;
  1269. background: #fff;
  1270. z-index: 10;
  1271. }
  1272. .point {
  1273. position: relative;
  1274. left: 93px;
  1275. top: 258px;
  1276. width: 16px;
  1277. height: 16px;
  1278. background-image: url(../../assets/blue-circle.png);
  1279. }
  1280. .s-line {
  1281. position: absolute;
  1282. left: 100px;
  1283. top: 242px;
  1284. height: 20px;
  1285. border-left: 2px dashed;
  1286. box-sizing: border-box;
  1287. border-color: #ddd;
  1288. }
  1289. .l-line {
  1290. position: relative;
  1291. bottom: 30px;
  1292. left: 111px;
  1293. top: 249px;
  1294. height: 3px;
  1295. width: 100px;
  1296. background-color: #dddddd;
  1297. }
  1298. .bottom-box {
  1299. top: 290px;
  1300. }
  1301. .top210px {
  1302. top: 270px;
  1303. }
  1304. .box:hover {
  1305. transform: scale(1.2);
  1306. }
  1307. .medias-box {
  1308. width: 80px;
  1309. height: 80px;
  1310. margin-top: 10px;
  1311. }
  1312. .card-note {
  1313. height: 30px;
  1314. font-size: 12px;
  1315. font-family: PingFangSC-Regular, PingFang SC;
  1316. font-weight: 400;
  1317. color: #777777;
  1318. padding: 10px 20px;
  1319. }
  1320. .medias-box {
  1321. width: 100%;
  1322. height: 100px;
  1323. margin-top: 20px;
  1324. }
  1325. .checkbox-group {
  1326. width: 180px;
  1327. height: 50px;
  1328. margin-top: 20px;
  1329. }
  1330. .el-checkbox {
  1331. margin: 0;
  1332. }
  1333. .now-box {
  1334. border: 5px solid #0094fe;
  1335. }
  1336. .now-l-line {
  1337. background-color: #0094fe;
  1338. }
  1339. .now-s-line {
  1340. border-color: #97caf6;
  1341. }
  1342. .now-point {
  1343. filter: grayscale(1);
  1344. }
  1345. .info-line-text-table {
  1346. width: 180px !important;
  1347. }
  1348. .upload-plus-icon {
  1349. height: 15%;
  1350. color: rgb(139, 147, 156);
  1351. line-height: 100px;
  1352. font-size: 40px;
  1353. font-weight: 200;
  1354. }
  1355. .upload-text {
  1356. height: 25%;
  1357. color: rgb(139, 147, 156);
  1358. }
  1359. .info-gap {
  1360. width: 40px;
  1361. font-size: 14px;
  1362. overflow: visible;
  1363. white-space: nowrap;
  1364. color: #006ebc;
  1365. cursor: pointer;
  1366. }
  1367. .unit {
  1368. font-size: 14px;
  1369. font-family: PingFangSC-Regular, PingFang SC;
  1370. font-weight: 400;
  1371. color: #353a42;
  1372. line-height: 100%;
  1373. margin: 0 10px;
  1374. }
  1375. </style>