voyageDetail.vue 38 KB

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