voyageDetail.vue 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170
  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 class="df aic">
  13. <div class="mr30">航次信息</div>
  14. <el-tooltip
  15. v-if="blockchainInfo"
  16. class="box-item"
  17. effect="light"
  18. :content="blockchainInfo.hash"
  19. placement="top"
  20. >
  21. <div class="pointer" style="font-size: 14px; font-weight: normal">
  22. 汇很多科技区块链认证
  23. </div>
  24. </el-tooltip>
  25. </div>
  26. <el-button
  27. v-auth="'DOWNLOADSHIPTRACK'"
  28. style="width: 220px; margin-right: 20px"
  29. type="primary"
  30. @click="downloadExcel"
  31. :loading="isLoadingExcel"
  32. >
  33. 下载船舶跟踪表
  34. </el-button>
  35. </div>
  36. <div class="line-container-p24">
  37. <div v-auth="'VOYAGEINFO'">
  38. <div class="line">
  39. <div class="info-line">
  40. <div class="info-line-title">航次编号</div>
  41. <div style="width: 240px">
  42. {{
  43. cargoOwnerName == "物产中大化工集团有限公司"
  44. ? voyage.wuchanVoyageInfo?.hhdVoyageCode
  45. : voyage.code
  46. }}
  47. </div>
  48. </div>
  49. <div
  50. class="info-line"
  51. v-if="cargoOwnerName == '物产中大化工集团有限公司'"
  52. >
  53. <div class="info-line-title">销售状态</div>
  54. <div>
  55. <el-select
  56. style="width: 240px"
  57. v-model="voyage.wuchanVoyageInfo.saleStatus"
  58. placeholder="销售状态"
  59. size="small"
  60. @change="updateSaleStatus"
  61. >
  62. <el-option label="采购" value="采购" />
  63. <el-option label="销售" value="销售" />
  64. <el-option label="调拨" value="调拨" />
  65. <el-option label="其他" value="1其他" />
  66. </el-select>
  67. </div>
  68. </div>
  69. </div>
  70. <div class="line">
  71. <div class="info-line">
  72. <div class="info-line-title">航次名称</div>
  73. <el-input
  74. class="info-line-text"
  75. v-model="voyage.voyageName"
  76. disabled
  77. ></el-input>
  78. </div>
  79. <div class="info-gap" v-if="shipAudits.length"></div>
  80. <div class="info-line">
  81. <div class="info-line-title">货主</div>
  82. <el-input
  83. class="info-line-text"
  84. v-model="voyage.cargoOwnerName"
  85. disabled
  86. ></el-input>
  87. </div>
  88. </div>
  89. <!-- <div class="line">
  90. <div class="info-line">
  91. <div class="info-line-title">船东</div>
  92. <el-input
  93. class="info-line-text"
  94. v-model="voyage.shipOwnerName"
  95. disabled
  96. ></el-input>
  97. </div>
  98. <div class="info-line">
  99. <div class="info-line-title">船东手机号</div>
  100. <el-input
  101. class="info-line-text"
  102. v-model="voyage.shipOwnerPhone"
  103. disabled
  104. ></el-input>
  105. </div>
  106. </div> -->
  107. <div class="line">
  108. <div class="info-line">
  109. <div class="info-line-title">船舶名称</div>
  110. <el-input
  111. class="info-line-text"
  112. v-model="voyage.shipName"
  113. disabled
  114. ></el-input>
  115. </div>
  116. <div
  117. class="info-gap"
  118. v-if="shipAudits.length"
  119. @click="isCertsVisable = true"
  120. >
  121. 汇很多认证
  122. </div>
  123. <el-dialog
  124. @open="showCerts"
  125. v-model="isCertsVisable"
  126. destroy-on-close
  127. width="30%"
  128. >
  129. <Certs ref="certs"></Certs>
  130. </el-dialog>
  131. <div class="info-line">
  132. <div class="info-line-title">MMSI</div>
  133. <el-input
  134. class="info-line-text"
  135. v-model="voyage.shipMmsi"
  136. disabled
  137. ></el-input>
  138. </div>
  139. </div>
  140. <div class="line">
  141. <div class="info-line">
  142. <div class="info-line-title">承运商</div>
  143. <el-input
  144. class="info-line-text"
  145. v-model="voyage.proxyName"
  146. disabled
  147. ></el-input>
  148. </div>
  149. </div>
  150. <div id="map-container" class="map-container"></div>
  151. <div class="line" style="margin-top: 30px">
  152. <div class="info-line">
  153. <div class="info-line-title">开始时间</div>
  154. <el-input
  155. class="info-line-text"
  156. v-model="voyage.startTime"
  157. disabled
  158. ></el-input>
  159. </div>
  160. <div class="info-line">
  161. <div class="info-line-title">结束时间</div>
  162. <el-input
  163. class="info-line-text"
  164. v-model="voyage.endTime"
  165. disabled
  166. ></el-input>
  167. </div>
  168. </div>
  169. <div class="line">
  170. <div class="info-line">
  171. <div class="info-line-title">装货港</div>
  172. <el-input
  173. class="info-line-text"
  174. v-model="voyage.loadPort"
  175. disabled
  176. ></el-input>
  177. </div>
  178. </div>
  179. <div class="line">
  180. <div
  181. class="info-line"
  182. v-for="(item, index) in voyage.voyageDetails"
  183. :key="item.portId"
  184. >
  185. <div class="info-line-title">卸货港{{ " # " + (index + 1) }}</div>
  186. <el-input
  187. class="info-line-text"
  188. v-model="item.portName"
  189. disabled
  190. ></el-input>
  191. </div>
  192. </div>
  193. <div class="line">
  194. <div class="info-line">
  195. <div class="info-line-title">货种</div>
  196. <el-input
  197. class="info-line-text"
  198. v-model="voyage.cargo"
  199. disabled
  200. ></el-input>
  201. </div>
  202. <div class="info-line">
  203. <div style="width: 120px !important" class="info-line-title">
  204. 货量
  205. </div>
  206. <el-input
  207. style="width: 100px !important"
  208. class="info-line-text"
  209. v-model="voyage.tons"
  210. disabled
  211. ></el-input>
  212. <span class="unit">吨</span>
  213. <el-input
  214. style="width: 80px !important"
  215. class="info-line-text"
  216. v-model="voyage.pieces"
  217. disabled
  218. ></el-input>
  219. <span class="unit">件</span>
  220. </div>
  221. </div>
  222. <div class="line">
  223. <div class="info-line">
  224. <div class="info-line-title">合理卸货天数</div>
  225. <el-input
  226. class="info-line-text"
  227. v-model="voyage.reasonableUnloadingDays"
  228. disabled
  229. ></el-input>
  230. </div>
  231. </div>
  232. </div>
  233. <div v-auth="'SHIPTRANS'">
  234. <div class="container-second-title df aic jcsb mt40">
  235. <div>船舶运输记录详情</div>
  236. <div style="margin-right: 20px">
  237. 信息更新时间 :
  238. <span style="font-size: 18px">
  239. {{ subStr(voyage.infoUpdateTime) }}
  240. </span>
  241. </div>
  242. </div>
  243. <div class="line-container-p24">
  244. <div class="line">
  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.arrivalLoadPortTime"
  250. type="datetime"
  251. format="YYYY/MM/DD HH:mm"
  252. value-format="YYYY/MM/DD HH:mm:ss"
  253. placeholder="到达装货港时间"
  254. :disabled="disabledStatus"
  255. ></el-date-picker>
  256. </div>
  257. <div class="info-line">
  258. <div class="info-line-title">实装货量</div>
  259. <el-input
  260. style="width: 100px !important"
  261. class="info-line-text"
  262. v-model="voyage.actualLoadTons"
  263. :disabled="disabledStatus"
  264. placeholder="实装吨位"
  265. ></el-input>
  266. <span class="unit">吨</span>
  267. <el-input
  268. style="width: 80px !important"
  269. class="info-line-text"
  270. v-model="voyage.actualLoadPieces"
  271. :disabled="disabledStatus"
  272. placeholder="实装件数"
  273. ></el-input>
  274. <span class="unit">件</span>
  275. </div>
  276. </div>
  277. <div class="line">
  278. <div class="info-line">
  279. <div class="info-line-title">装货开始时间</div>
  280. <el-date-picker
  281. class="info-line-text"
  282. v-model="voyage.loadStartTime"
  283. type="datetime"
  284. format="YYYY/MM/DD HH:mm"
  285. value-format="YYYY/MM/DD HH:mm:ss"
  286. placeholder="装货开始时间"
  287. :disabled="disabledStatus"
  288. ></el-date-picker>
  289. </div>
  290. <div class="info-line">
  291. <div class="info-line-title">装货结束时间</div>
  292. <el-date-picker
  293. class="info-line-text"
  294. v-model="voyage.loadEndTime"
  295. type="datetime"
  296. format="YYYY/MM/DD HH:mm"
  297. value-format="YYYY/MM/DD HH:mm:ss"
  298. placeholder="装货开始时间"
  299. :disabled="disabledStatus"
  300. ></el-date-picker>
  301. </div>
  302. </div>
  303. <el-tabs
  304. v-model="currentPortId"
  305. type="border-card"
  306. class="demo-tabs mb20"
  307. >
  308. <el-tab-pane
  309. v-for="(item, index) in voyage.voyageDetails"
  310. :label="item.portName + ' # ' + (index + 1)"
  311. :key="item.portId"
  312. :name="item.portId + ''"
  313. >
  314. <div class="line">
  315. <div class="info-line">
  316. <div class="info-line-title">开航时间</div>
  317. <el-date-picker
  318. class="info-line-text"
  319. v-model="item.setSailTime"
  320. type="datetime"
  321. @change="calExpectedArrivalTime"
  322. format="YYYY/MM/DD HH:mm"
  323. value-format="YYYY/MM/DD HH:mm:ss"
  324. placeholder="开航时间"
  325. :disabled="disabledStatus"
  326. ></el-date-picker>
  327. </div>
  328. <div class="info-line">
  329. <div class="info-line-title">预计到港时间</div>
  330. <el-date-picker
  331. class="info-line-text"
  332. v-model="item.expectedArrivalTime"
  333. type="datetime"
  334. format="YYYY/MM/DD"
  335. value-format="YYYY/MM/DD HH:mm:ss"
  336. placeholder="预计到港时间"
  337. :disabled="disabledStatus"
  338. ></el-date-picker>
  339. </div>
  340. </div>
  341. <div class="line">
  342. <div class="info-line">
  343. <div class="info-line-title">实际到港时间</div>
  344. <el-date-picker
  345. class="info-line-text"
  346. v-model="item.actualArrivalTime"
  347. type="datetime"
  348. format="YYYY/MM/DD HH:mm"
  349. value-format="YYYY/MM/DD HH:mm:ss"
  350. placeholder="实际到港时间"
  351. :disabled="disabledStatus"
  352. ></el-date-picker>
  353. </div>
  354. <!-- <div class="info-line">
  355. <div class="info-line-title">抵达目的地港时间</div>
  356. <el-date-picker
  357. class="info-line-text"
  358. v-model="voyage.arrivalPortTime"
  359. type="datetime"
  360. format="YYYY/MM/DD HH:mm"
  361. value-format="YYYY/MM/DD HH:mm:ss"
  362. placeholder="抵达目的地港时间"
  363. :disabled="disabledStatus"
  364. ></el-date-picker>
  365. </div> -->
  366. </div>
  367. <div class="line">
  368. <div class="info-line">
  369. <div class="info-line-title">卸货开始时间</div>
  370. <el-date-picker
  371. class="info-line-text"
  372. v-model="item.dischargeStartTime"
  373. type="datetime"
  374. format="YYYY/MM/DD HH:mm"
  375. value-format="YYYY/MM/DD HH:mm:ss"
  376. placeholder="卸货开始时间"
  377. :disabled="disabledStatus"
  378. ></el-date-picker>
  379. </div>
  380. <div class="info-line">
  381. <div class="info-line-title">卸货结束时间</div>
  382. <el-date-picker
  383. class="info-line-text"
  384. v-model="item.dischargeEndTime"
  385. type="datetime"
  386. format="YYYY/MM/DD HH:mm"
  387. value-format="YYYY/MM/DD HH:mm:ss"
  388. placeholder="卸货结束时间"
  389. :disabled="disabledStatus"
  390. ></el-date-picker>
  391. </div>
  392. </div>
  393. <div class="line">
  394. <div class="info-line">
  395. <div class="info-line-title">实卸货量</div>
  396. <el-input
  397. style="width: 100px !important"
  398. class="info-line-text"
  399. placeholder="实际卸货吨位"
  400. v-model="item.actualDischargeTons"
  401. :disabled="disabledStatus"
  402. ></el-input>
  403. <span class="unit">吨</span>
  404. <el-input
  405. style="width: 80px !important"
  406. class="info-line-text"
  407. placeholder="实际卸货件数"
  408. v-model="item.actualDischargePieces"
  409. :disabled="disabledStatus"
  410. ></el-input>
  411. <span class="unit">件</span>
  412. </div>
  413. <!-- <div class="info-line">
  414. <div class="info-line-title">是否购买保险</div>
  415. <el-checkbox
  416. v-model="voyage.hasInsurance"
  417. :checked="voyage.hasInsurance == 1"
  418. :disabled="disabledStatus"
  419. label="购买保险"
  420. ></el-checkbox>
  421. </div> -->
  422. </div>
  423. </el-tab-pane>
  424. </el-tabs>
  425. <div class="line">
  426. <div class="info-line">
  427. <div class="info-line-title">备注</div>
  428. <el-input
  429. class="info-line-textarea"
  430. v-model="voyage.remark"
  431. autosize
  432. type="textarea"
  433. :disabled="disabledStatus"
  434. ></el-input>
  435. </div>
  436. </div>
  437. </div>
  438. </div>
  439. </div>
  440. <div class="container-title">卸货信息</div>
  441. <div class="line-container-p24">
  442. <el-tabs
  443. v-model="currentDiscPortId"
  444. type="card"
  445. class="demo-tabs"
  446. @tab-click="changeDiscPortTab"
  447. >
  448. <el-tab-pane
  449. v-for="(item, index) in voyage.voyageDetails"
  450. :label="item.portName + ' # ' + (index + 1)"
  451. :key="item.portId"
  452. :name="item.portId + ''"
  453. ></el-tab-pane>
  454. </el-tabs>
  455. <div>
  456. <div class="container-second-title df">
  457. <div>天气信息</div>
  458. <div
  459. class="ml10"
  460. style="
  461. font-size: 12px;
  462. color: rgba(0, 0, 0, 0.2);
  463. font-weight: normal;
  464. scale: 0.9;
  465. "
  466. >
  467. 天气信息为天气预报反应船舶所在区域即将发生天气情况
  468. <br />
  469. 预报下雨情况为预警信息请联系船东拍摄现场实际情况。
  470. </div>
  471. </div>
  472. <el-table style="width: 1200px" :data="weatherTableData" stripe>
  473. <el-table-column
  474. type="index"
  475. label="序号"
  476. min-width="120"
  477. align="center"
  478. ></el-table-column>
  479. <el-table-column
  480. prop="weather"
  481. label="天气"
  482. min-width="120"
  483. align="center"
  484. ></el-table-column>
  485. <el-table-column
  486. prop="temperature"
  487. label="温度"
  488. min-width="100"
  489. align="center"
  490. ></el-table-column>
  491. <el-table-column
  492. prop="winddirection"
  493. label="风向"
  494. min-width="100"
  495. align="center"
  496. ></el-table-column>
  497. <el-table-column
  498. prop="windpower"
  499. label="风力"
  500. min-width="100"
  501. align="center"
  502. ></el-table-column>
  503. <el-table-column
  504. prop="reporttime"
  505. label="记录时间"
  506. min-width="100"
  507. align="center"
  508. >
  509. <template v-slot="scope">
  510. {{ subTimeStr(scope.row.reporttime, 16) }}
  511. </template>
  512. </el-table-column>
  513. </el-table>
  514. <div style="width: 1200px; text-align: right; margin-top: 43px">
  515. <el-pagination
  516. background
  517. layout="prev, pager, next"
  518. :total="weatherTotal"
  519. @current-change="weatherPageChange"
  520. ></el-pagination>
  521. </div>
  522. <div class="hr m30-0"></div>
  523. </div>
  524. <div v-auth="'LABDETAIL_DISABLE'">
  525. <div class="container-second-title df aic jcsb">
  526. <div>提单信息</div>
  527. </div>
  528. <el-table :data="labTableData" stripe style="width: 1200px">
  529. <el-table-column
  530. type="index"
  531. label="序号"
  532. min-width="120"
  533. align="center"
  534. ></el-table-column>
  535. <el-table-column
  536. prop="billingDate"
  537. label="开单日期"
  538. min-width="120"
  539. align="center"
  540. ></el-table-column>
  541. <el-table-column
  542. prop="billingNum"
  543. label="开单数量"
  544. min-width="100"
  545. align="center"
  546. ></el-table-column>
  547. <el-table-column label="单据" min-width="150" align="center">
  548. <template v-slot="scope">
  549. <el-button
  550. @click="showLab(scope.row, scope.$index, '查看提单')"
  551. type="primary"
  552. size="small"
  553. >
  554. 查看
  555. </el-button>
  556. </template>
  557. </el-table-column>
  558. </el-table>
  559. <div style="text-align: right; margin-top: 43px; width: 1200px">
  560. <el-pagination
  561. background
  562. layout="prev, pager, next"
  563. :total="labTotal"
  564. @current-change="labPageChange"
  565. ></el-pagination>
  566. </div>
  567. <el-dialog
  568. v-model="isAddLabVisable"
  569. :title="labModalType"
  570. width="780px"
  571. center
  572. @close="cancelUploadLab"
  573. >
  574. <el-form
  575. :model="labForm"
  576. inline
  577. style="margin-bottom: 20px"
  578. label-width="100px"
  579. >
  580. <el-form-item label="开单日期">
  581. <el-date-picker
  582. class="info-line-text"
  583. v-model="labForm.billingDate"
  584. type="date"
  585. format="YYYY/MM/DD"
  586. value-format="YYYY/MM/DD"
  587. placeholder="开单日期"
  588. disabled
  589. ></el-date-picker>
  590. </el-form-item>
  591. <el-form-item label="开单数量">
  592. <el-input
  593. style="width: 240px"
  594. v-model="labForm.billingNum"
  595. placeholder="开单数量"
  596. disabled
  597. ></el-input>
  598. </el-form-item>
  599. <el-form-item label="提单">
  600. <Uploader
  601. disabled
  602. :actionUrl="store.state.wayBillUrl"
  603. :uploaderId="'labLoad'"
  604. :params="labParams"
  605. @onSendFileList="getLabBillList"
  606. :fileList="labBillList"
  607. uploadText="上传提单"
  608. :limit="1"
  609. ></Uploader>
  610. </el-form-item>
  611. </el-form>
  612. </el-dialog>
  613. <div class="hr m30-0"></div>
  614. </div>
  615. <div v-auth="'SHIPDISCHARGE'">
  616. <div class="container-second-title df aic jcsb">
  617. <div>卸货记录</div>
  618. <el-button
  619. v-auth="'DOWNLOADDISCHARGE'"
  620. @click="exportDischargeExcel"
  621. style="width: 220px; margin-right: 20px"
  622. type="primary"
  623. :loading="isDischargeLoadingExcel"
  624. >
  625. 下载卸货信息
  626. </el-button>
  627. </div>
  628. <el-table :data="dischargeList" stripe style="width: 1200px">
  629. <el-table-column
  630. type="index"
  631. label="序号"
  632. min-width="80"
  633. align="center"
  634. ></el-table-column>
  635. <el-table-column
  636. prop="dischargeTime"
  637. label="卸货时间"
  638. min-width="120"
  639. align="center"
  640. >
  641. <template v-slot="scope">
  642. {{ subTimeStr(scope.row.dischargeTime, 16) }}
  643. </template>
  644. </el-table-column>
  645. <el-table-column
  646. prop="dischargeTons"
  647. label="卸货吨位"
  648. min-width="100"
  649. align="center"
  650. ></el-table-column>
  651. <el-table-column
  652. prop="dischargePieces"
  653. label="卸货件数"
  654. min-width="100"
  655. align="center"
  656. ></el-table-column>
  657. <el-table-column label="磅单" min-width="150" align="center">
  658. <template v-slot="scope">
  659. <el-button
  660. @click="showUpdateDischarge(scope.row, scope.$index)"
  661. type="primary"
  662. size="small"
  663. v-if="scope.row.files"
  664. >
  665. 查看
  666. </el-button>
  667. </template>
  668. </el-table-column>
  669. </el-table>
  670. <div style="width: 1200px; text-align: right; margin-top: 43px">
  671. <el-pagination
  672. background
  673. layout="prev, pager, next"
  674. :total="total"
  675. @current-change="pageChange"
  676. ></el-pagination>
  677. </div>
  678. <div class="hr m30-0"></div>
  679. </div>
  680. <el-dialog v-model="dialogVisible" title="图片预览" width="30%">
  681. <el-image
  682. :src="dialogImageUrl"
  683. style="height: 100%; width: 100%"
  684. ></el-image>
  685. </el-dialog>
  686. <div v-auth="'CARLOAD'">
  687. <div class="container-second-title df aic jcsb mt40">
  688. <div>汽车装货记录详情</div>
  689. </div>
  690. <el-table :data="truckTableData" stripe>
  691. <el-table-column
  692. type="index"
  693. label="序号"
  694. min-width="120"
  695. align="center"
  696. ></el-table-column>
  697. <el-table-column
  698. prop="portName"
  699. label="港口名称"
  700. min-width="100"
  701. align="center"
  702. ></el-table-column>
  703. <el-table-column
  704. prop="weighTime"
  705. label="称重时间"
  706. min-width="120"
  707. align="center"
  708. >
  709. <template v-slot="scope">
  710. {{ subTimeStr(scope.row.weighTime, 16) }}
  711. </template>
  712. </el-table-column>
  713. <el-table-column
  714. prop="carNum"
  715. label="车号"
  716. min-width="100"
  717. align="center"
  718. ></el-table-column>
  719. <el-table-column
  720. prop="cargoName"
  721. label="货物名称"
  722. min-width="120"
  723. align="center"
  724. ></el-table-column>
  725. <el-table-column
  726. prop="shippingUnit"
  727. label="发货单位"
  728. min-width="100"
  729. align="center"
  730. ></el-table-column>
  731. <el-table-column
  732. prop="receivingUnit"
  733. label="收货单位"
  734. min-width="120"
  735. align="center"
  736. ></el-table-column>
  737. <el-table-column
  738. prop="grossWeight"
  739. label="毛重"
  740. min-width="100"
  741. align="center"
  742. ></el-table-column>
  743. <el-table-column
  744. prop="tare"
  745. label="皮重"
  746. min-width="120"
  747. align="center"
  748. ></el-table-column>
  749. <el-table-column
  750. prop="netWeight"
  751. label="净重"
  752. min-width="100"
  753. align="center"
  754. ></el-table-column>
  755. <el-table-column
  756. prop="shipName"
  757. label="货船名称"
  758. min-width="120"
  759. align="center"
  760. ></el-table-column>
  761. <el-table-column
  762. prop="weigher"
  763. label="司磅员"
  764. min-width="100"
  765. align="center"
  766. ></el-table-column>
  767. <el-table-column label="单据" min-width="150" align="center">
  768. <template v-slot="scope">
  769. <el-button
  770. @click="showTruckRecord(scope.row, scope.$index, '查看单据')"
  771. type="primary"
  772. size="small"
  773. >
  774. 查看
  775. </el-button>
  776. </template>
  777. </el-table-column>
  778. </el-table>
  779. <div style="text-align: right; margin-top: 43px">
  780. <el-pagination
  781. background
  782. layout="prev, pager, next"
  783. :total="truckTotal"
  784. @current-change="truckPageChange"
  785. ></el-pagination>
  786. </div>
  787. </div>
  788. <el-dialog
  789. v-model="isAddTruckRecordVisable"
  790. title="查看记录"
  791. width="780px"
  792. center
  793. >
  794. <el-form
  795. :model="truckRecordForm"
  796. inline
  797. style="margin-bottom: 20px"
  798. label-width="100px"
  799. >
  800. <el-form-item label="港口名称">
  801. <el-input
  802. style="width: 240px"
  803. v-model="truckRecordForm.portName"
  804. placeholder="港口名称"
  805. ></el-input>
  806. </el-form-item>
  807. <el-form-item label="称重时间">
  808. <el-date-picker
  809. class="info-line-text"
  810. v-model="truckRecordForm.weighTime"
  811. type="datetime"
  812. format="YYYY/MM/DD HH:mm"
  813. value-format="YYYY/MM/DD HH:mm:ss"
  814. placeholder="称重时间"
  815. ></el-date-picker>
  816. </el-form-item>
  817. <el-form-item label="车号">
  818. <el-input
  819. style="width: 240px"
  820. v-model="truckRecordForm.carNum"
  821. placeholder="车号"
  822. ></el-input>
  823. </el-form-item>
  824. <el-form-item label="货物名称">
  825. <el-input
  826. style="width: 240px"
  827. v-model="truckRecordForm.cargoName"
  828. placeholder="货物名称"
  829. ></el-input>
  830. </el-form-item>
  831. <el-form-item label="发货单位">
  832. <el-input
  833. style="width: 240px"
  834. v-model="truckRecordForm.shippingUnit"
  835. placeholder="发货单位"
  836. ></el-input>
  837. </el-form-item>
  838. <el-form-item label="毛重">
  839. <el-input
  840. style="width: 240px"
  841. v-model="truckRecordForm.grossWeight"
  842. placeholder="毛重"
  843. ></el-input>
  844. </el-form-item>
  845. <el-form-item label="收货单位">
  846. <el-input
  847. style="width: 240px"
  848. v-model="truckRecordForm.receivingUnit"
  849. placeholder="收货单位"
  850. ></el-input>
  851. </el-form-item>
  852. <el-form-item label="皮重">
  853. <el-input
  854. style="width: 240px"
  855. v-model="truckRecordForm.tare"
  856. placeholder="皮重"
  857. ></el-input>
  858. </el-form-item>
  859. <el-form-item label="运输单位">
  860. <el-input
  861. style="width: 240px"
  862. v-model="truckRecordForm.transUnit"
  863. placeholder="运输单位"
  864. ></el-input>
  865. </el-form-item>
  866. <el-form-item label="净重">
  867. <el-input
  868. style="width: 240px"
  869. v-model="truckRecordForm.netWeight"
  870. placeholder="净重"
  871. ></el-input>
  872. </el-form-item>
  873. <el-form-item label="货船名称">
  874. <el-input
  875. style="width: 240px"
  876. v-model="truckRecordForm.shipName"
  877. placeholder="货船名称"
  878. ></el-input>
  879. </el-form-item>
  880. <el-form-item label="司磅员">
  881. <el-input
  882. style="width: 240px"
  883. v-model="truckRecordForm.weigher"
  884. placeholder="司磅员"
  885. ></el-input>
  886. </el-form-item>
  887. <el-form-item label="汽车装货单">
  888. <Uploader
  889. actionUrl="#"
  890. :uploaderId="'truckLoad'"
  891. :params="truckLoadParams"
  892. @onSendFileList="getTruckLoadBillList"
  893. :fileList="truckRecordBillList"
  894. uploadText="上传装货单"
  895. :limit="1"
  896. disabled
  897. ></Uploader>
  898. </el-form-item>
  899. </el-form>
  900. </el-dialog>
  901. </div>
  902. <div v-auth="'BILLINFO'">
  903. <div class="container-title df aic jcsb">
  904. 单据信息
  905. <el-button
  906. style="margin-right: 30px"
  907. size="medium"
  908. type="primary"
  909. v-auth="'BILLDOWNLOAD'"
  910. @click="downloadBillZip"
  911. :loading="billZipLoading"
  912. >
  913. 下载单据
  914. </el-button>
  915. </div>
  916. <div class="line-container-p24" style="padding-left: 60px">
  917. <div class="df aic">
  918. <div class="info-line-title">保险单:</div>
  919. <el-image
  920. style="width: 200px; height: 200px; margin-right: 20px"
  921. :src="item.viewUrl"
  922. v-for="item in policyList"
  923. :key="item"
  924. @click="openMediaModal(item.viewUrl, 1, '保险单查看')"
  925. ></el-image>
  926. </div>
  927. <div v-if="voyage.hasInsurance == 1">
  928. <div class="hr mb20"></div>
  929. <div class="df aic jcsb">
  930. <div style="color: #0094fe">出险记录</div>
  931. <el-button
  932. type="primary"
  933. @click="
  934. (isAccidentVisable = true) && (accidentTitle = '新增出险记录')
  935. "
  936. >
  937. 新增出险记录
  938. </el-button>
  939. </div>
  940. <el-table :data="accidentList" style="width: 800px">
  941. <el-table-column
  942. type="index"
  943. label="序号"
  944. min-width="120"
  945. align="center"
  946. ></el-table-column>
  947. <el-table-column
  948. prop="remark"
  949. label="备注"
  950. min-width="120"
  951. align="center"
  952. ></el-table-column>
  953. <el-table-column
  954. prop="accidentTime"
  955. label="出险时间"
  956. min-width="100"
  957. align="center"
  958. ></el-table-column>
  959. <el-table-column
  960. prop="accidentTime"
  961. label="操作"
  962. min-width="100"
  963. align="center"
  964. >
  965. <template v-slot="scope">
  966. <el-button
  967. type="primary"
  968. size="small"
  969. @click="checkAccident(scope.row, true)"
  970. >
  971. 查看
  972. </el-button>
  973. <el-button
  974. type="info"
  975. size="small"
  976. @click="checkAccident(scope.row)"
  977. >
  978. 修改
  979. </el-button>
  980. <el-button
  981. type="danger"
  982. size="small"
  983. @click="deleteAccident(scope.row.id)"
  984. >
  985. 删除
  986. </el-button>
  987. </template>
  988. </el-table-column>
  989. </el-table>
  990. <div class="df jcfe">
  991. <el-pagination
  992. class="mt20"
  993. background
  994. layout="prev, pager, next"
  995. :total="accidentTotal"
  996. :current-page="accidentCurrentPage"
  997. @current-change="accidentPageChange"
  998. ></el-pagination>
  999. </div>
  1000. <el-dialog
  1001. :title="accidentTitle"
  1002. v-model="isAccidentVisable"
  1003. @close="resetAccidentForm"
  1004. destroy-on-close
  1005. >
  1006. <el-form :model="accidentForm" label-width="100px">
  1007. <el-form-item label="出险时间">
  1008. <el-date-picker
  1009. class="info-line-text"
  1010. v-model="accidentForm.accidentTime"
  1011. type="datetime"
  1012. format="YYYY/MM/DD HH:mm:ss"
  1013. value-format="YYYY/MM/DD HH:mm:ss"
  1014. placeholder="出险时间"
  1015. :disabled="isAccidentCheck"
  1016. ></el-date-picker>
  1017. </el-form-item>
  1018. <el-form-item label="备注">
  1019. <el-input
  1020. class="info-line-text"
  1021. v-model="accidentForm.remark"
  1022. placeholder="备注"
  1023. :disabled="isAccidentCheck"
  1024. ></el-input>
  1025. </el-form-item>
  1026. <el-form-item label="出险材料">
  1027. <div class="df">
  1028. <div
  1029. v-for="item in accidentForm.files"
  1030. v-bind:key="item.id"
  1031. style="position: relative"
  1032. >
  1033. <img
  1034. v-if="item.mediaType == 1"
  1035. style="
  1036. height: 148px;
  1037. width: 148px;
  1038. object-fit: contain;
  1039. margin-right: 10px;
  1040. "
  1041. @click="openMediaModal(item.viewUrl, 1, '图片查看')"
  1042. :src="item.viewUrl"
  1043. alt=""
  1044. />
  1045. <div v-else>
  1046. <div
  1047. style="height: 148px; width: 148px; margin-right: 10px"
  1048. >
  1049. <video
  1050. style="height: 100%; width: 100%"
  1051. @click="openMediaModal(item.viewUrl, 2, '视频查看')"
  1052. :src="item.viewUrl"
  1053. ></video>
  1054. </div>
  1055. </div>
  1056. <img
  1057. v-if="item.mediaType == 2"
  1058. @click="openMediaModal(item.viewUrl, 2, '视频查看')"
  1059. src="../../assets/icon-player.png"
  1060. style="
  1061. object-fit: contain;
  1062. width: 40px;
  1063. height: 40px;
  1064. position: absolute;
  1065. z-index: 100;
  1066. left: 50px;
  1067. top: 50px;
  1068. background: #fff;
  1069. border-radius: 50%;
  1070. "
  1071. alt=""
  1072. />
  1073. </div>
  1074. <el-upload
  1075. drag
  1076. multiple
  1077. :action="store.state.accidentUrl"
  1078. list-type="picture-card"
  1079. :data="accidentParams"
  1080. :on-success="accidentUploadSuccess"
  1081. :show-file-list="false"
  1082. :file-list="accidentList"
  1083. :before-upload="accidentBeforeUpload"
  1084. v-if="!isAccidentCheck"
  1085. >
  1086. <div class="upload-plus-icon">+</div>
  1087. <div class="upload-text">拖拽或点击上传</div>
  1088. </el-upload>
  1089. </div>
  1090. </el-form-item>
  1091. </el-form>
  1092. <div class="df aic jcfe mb30" v-if="!isAccidentCheck">
  1093. <el-button
  1094. type="primary"
  1095. v-if="accidentForm.id"
  1096. @click="addUpdateAccident"
  1097. >
  1098. 修改
  1099. </el-button>
  1100. <el-button type="primary" v-else @click="addUpdateAccident">
  1101. 新增
  1102. </el-button>
  1103. </div>
  1104. </el-dialog>
  1105. <div class="hr mt20 mb20"></div>
  1106. </div>
  1107. <div class="df aic">
  1108. <div class="info-line-title">运单:</div>
  1109. <el-image
  1110. style="width: 200px; height: 200px; margin-right: 20px"
  1111. :src="item.viewUrl"
  1112. v-for="item in voyageBill"
  1113. :key="item"
  1114. @click="openMediaModal(item.viewUrl, 1, '运单查看')"
  1115. ></el-image>
  1116. </div>
  1117. </div>
  1118. </div>
  1119. <div v-auth="'VOYAGEIMAGE'">
  1120. <div class="container-title">航次图片</div>
  1121. <div class="line-container-p24">
  1122. <div v-if="medias.length" class="medias-content df ffw">
  1123. <div class="pic-container">
  1124. <div v-for="(item, index) in medias" :key="item" class="pic-main">
  1125. <div
  1126. :class="[
  1127. 'box',
  1128. index % 2 == 0 ? '' : 'bottom-box',
  1129. item.status == 1 ? 'now-box' : '',
  1130. ]"
  1131. >
  1132. <div class="card-note">
  1133. {{ item.shipName }} 拍摄于
  1134. <br />
  1135. {{ item.createTime }}
  1136. <br />
  1137. 天气 : {{ item.weather?.weather }} - 气温 :
  1138. {{ item.weather?.temperature }}℃
  1139. </div>
  1140. <div class="medias-box mb10" style="position: relative">
  1141. <el-image
  1142. v-if="item.mediaType == 1"
  1143. style="width: 100%; height: 100%"
  1144. fit="contain"
  1145. :src="item.downloadUrl"
  1146. @click="openMediaModal(item.downloadUrl, 1, '图片审核')"
  1147. ></el-image>
  1148. <video
  1149. style="width: 100%; height: 100%"
  1150. v-else
  1151. :src="item.downloadUrl"
  1152. ></video>
  1153. <img
  1154. @click="openMediaModal(item.downloadUrl, 2, '视频审核')"
  1155. v-if="item.mediaType == 2"
  1156. src="../../assets/icon-player.png"
  1157. style="
  1158. object-fit: contain;
  1159. width: 40px;
  1160. height: 40px;
  1161. position: absolute;
  1162. top: calc(50% - 20px);
  1163. left: calc(50% - 20px);
  1164. background: #fff;
  1165. border-radius: 50%;
  1166. "
  1167. alt=""
  1168. />
  1169. </div>
  1170. </div>
  1171. <div
  1172. :class="[
  1173. 's-line',
  1174. index % 2 == 0 ? '' : 'top210px',
  1175. item.status == 1 ? 'now-s-line' : '',
  1176. ]"
  1177. ></div>
  1178. <div :class="['point', item.status == 1 ? '' : 'now-point']"></div>
  1179. <div
  1180. :class="['l-line', item.status == 1 ? 'now-l-line' : '']"
  1181. v-if="index + 1 != medias.length"
  1182. ></div>
  1183. </div>
  1184. </div>
  1185. </div>
  1186. <el-dialog v-model="mediaModal" :title="modalTitle">
  1187. <el-image
  1188. v-if="modalType == 1"
  1189. style="height: 60vh; display: flex"
  1190. fit="contain"
  1191. :src="currentUrl"
  1192. :preview-src-list="modalPreview"
  1193. ></el-image>
  1194. <video
  1195. v-else
  1196. autoplay
  1197. controls
  1198. style="width: 100%; height: 60vh"
  1199. :src="currentUrl"
  1200. ></video>
  1201. </el-dialog>
  1202. <el-dialog
  1203. v-model="updateDischargeDialog"
  1204. title="查看记录"
  1205. width="700px"
  1206. center
  1207. >
  1208. <el-form :model="updateForm" style="margin-bottom: 20px">
  1209. <!-- <el-form-item label="记录ID">
  1210. <span style="padding-left: 20px">{{ updateForm.id }}</span>
  1211. </el-form-item> -->
  1212. <el-form-item label="卸货时间">
  1213. <el-date-picker
  1214. class="info-line-text"
  1215. v-model="updateForm.dischargeTime"
  1216. type="datetime"
  1217. format="YYYY/MM/DD HH:mm"
  1218. value-format="YYYY/MM/DD HH:mm:ss"
  1219. placeholder="卸货时间"
  1220. disabled
  1221. ></el-date-picker>
  1222. </el-form-item>
  1223. <el-form-item label="卸货吨位">
  1224. <el-input
  1225. style="width: 240px"
  1226. v-model="updateForm.dischargeTons"
  1227. placeholder="卸货吨位"
  1228. disabled
  1229. ></el-input>
  1230. </el-form-item>
  1231. <el-form-item label="磅单查看">
  1232. <el-image
  1233. style="width: 200px; height: 200px; margin-right: 20px"
  1234. v-for="item in updatePoundBillList"
  1235. :src="item.viewUrl"
  1236. :key="item"
  1237. @click="openMediaModal(item.viewUrl, 1, '磅单查看')"
  1238. ></el-image>
  1239. </el-form-item>
  1240. </el-form>
  1241. </el-dialog>
  1242. </div>
  1243. </div>
  1244. </template>
  1245. <script setup>
  1246. import { onMounted, reactive, ref, toRefs } from "vue";
  1247. import api from "../../apis/fetch";
  1248. import { useRoute } from "vue-router";
  1249. import _ from "lodash";
  1250. import router from "../../router";
  1251. import store from "../../store";
  1252. import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
  1253. import downloadBlobFile from "../../utils/downloadBlobFile";
  1254. import url from "../../apis/config";
  1255. import { subTimeStr } from "utils/utils";
  1256. const route = useRoute();
  1257. let map = ref();
  1258. let voyage = ref({
  1259. wuchanVoyageInfo: {},
  1260. });
  1261. let medias = ref([]);
  1262. let coordinates = ref();
  1263. let previewSrcList = ref([]);
  1264. let policyList = ref([]);
  1265. let previewPoundList = ref([]);
  1266. let shipAudits = ref([]);
  1267. let certs = ref(null);
  1268. let isCertsVisable = ref(false);
  1269. function showCerts() {
  1270. setTimeout(() => {
  1271. certs.value.initCerts(shipAudits.value);
  1272. });
  1273. }
  1274. async function getVoyageDetail(isInit) {
  1275. policyList.value = [];
  1276. voyageBill.value = [];
  1277. previewSrcList.value = [];
  1278. let res = await api.getVoyageDetail({
  1279. type: localStorage.userType,
  1280. voyageId: route.query.id,
  1281. loginAccountId: localStorage.loginAccountId,
  1282. });
  1283. if (res.data.status == 0) {
  1284. ElNotification({
  1285. type: "success",
  1286. title: res.data.msg,
  1287. });
  1288. blockchainInfo.value = res.data.result.blockChain;
  1289. coordinates.value = res.data.result.coordinates;
  1290. voyage.value = res.data.result.voyage;
  1291. voyage.value.startTime = voyage.value.startTime.substring(0, 16);
  1292. currentPortId.value = voyage.value.voyageDetails[0].portId + "";
  1293. currentDiscPortId.value = voyage.value.voyageDetails[0].portId + "";
  1294. medias.value = res.data.result.medias;
  1295. shipAudits.value = res.data.result.shipAudits;
  1296. for (let i of res.data.result.policys || []) {
  1297. policyList.value.push({
  1298. ...i,
  1299. url: i.viewUrl,
  1300. });
  1301. }
  1302. for (let i of res.data.result.waybills) {
  1303. voyageBill.value.push({
  1304. ...i,
  1305. url: i.viewUrl,
  1306. });
  1307. }
  1308. for (let i of medias.value) {
  1309. previewSrcList.value.push(i.downloadUrl);
  1310. }
  1311. if (isInit) {
  1312. getDischargeList();
  1313. getTruckLoadRecord();
  1314. getLabList();
  1315. getPortWeatherList();
  1316. getAccidentList();
  1317. try {
  1318. initMap();
  1319. } catch (error) {}
  1320. }
  1321. } else {
  1322. console.log(res);
  1323. ElNotification({
  1324. type: "error",
  1325. title: res.data.msg,
  1326. });
  1327. }
  1328. }
  1329. let total = ref(0);
  1330. function pageChange(e) {
  1331. dischargeCurrentPage.value = e;
  1332. getDischargeList();
  1333. }
  1334. async function addDischarge() {
  1335. if (!formInline.value.dischargeTime || !formInline.value.dischargeTons)
  1336. return;
  1337. let res = await api.addDischarge({
  1338. ...formInline.value,
  1339. voyageId: route.query.id,
  1340. });
  1341. if (res.data.status == 0) {
  1342. getDischargeList(1);
  1343. formInline.value = {};
  1344. } else {
  1345. console.log(res);
  1346. }
  1347. }
  1348. async function deleteDischarge(id, index) {
  1349. let res = await api.deleteDischarge({
  1350. id,
  1351. });
  1352. if (res.data.status == 0) {
  1353. dischargeList.value.splice(index, 1);
  1354. ElNotification({
  1355. type: "success",
  1356. title: res.data.msg,
  1357. });
  1358. } else {
  1359. console.log(res);
  1360. ElNotification({
  1361. type: "error",
  1362. title: res.data.msg,
  1363. });
  1364. }
  1365. }
  1366. async function exportExcel() {}
  1367. let dischargeCurrentPage = ref(1);
  1368. let dischargeList = ref();
  1369. let formInline = ref({});
  1370. async function getDischargeList(type) {
  1371. let res = await api.getDischargeList({
  1372. voyageId: route.query.id,
  1373. portId: currentDiscPortId.value,
  1374. currentPage: dischargeCurrentPage.value,
  1375. size: 10,
  1376. loginAccountId: localStorage.loginAccountId,
  1377. });
  1378. if (res.data.status == 0) {
  1379. dischargeList.value = res.data.result;
  1380. total.value = res.data.total;
  1381. } else {
  1382. console.log(res);
  1383. }
  1384. }
  1385. let updateForm = ref({});
  1386. let updateDischargeDialog = ref(false);
  1387. let currentUpdateIndex = ref(-1);
  1388. async function updateDischarge() {
  1389. let res = await api.updateDischarge({
  1390. ...updateForm.value,
  1391. });
  1392. if (res.data.status == 0) {
  1393. dischargeList.value[currentUpdateIndex.value] = res.data.result;
  1394. cancelUpdateDischarge();
  1395. ElNotification({
  1396. type: "success",
  1397. title: res.data.msg,
  1398. });
  1399. } else {
  1400. console.log(res);
  1401. ElNotification({
  1402. type: "error",
  1403. title: res.data.msg,
  1404. });
  1405. }
  1406. }
  1407. let updatePoundParams = ref([]);
  1408. let updatePoundBillList = ref([]);
  1409. function showUpdateDischarge(item, index) {
  1410. updateDischargeDialog.value = true;
  1411. updatePoundBillList.value = [];
  1412. let { id, dischargeTons, dischargeTime, voyageId, files } = item;
  1413. updatePoundBillList.value = files;
  1414. updateForm.value = {
  1415. id,
  1416. dischargeTons,
  1417. dischargeTime,
  1418. };
  1419. currentUpdateIndex.value = index;
  1420. }
  1421. function cancelUpdateDischarge() {
  1422. updateDischargeDialog.value = false;
  1423. updateForm.value = {};
  1424. currentUpdateIndex.value = -1;
  1425. }
  1426. function initMap() {
  1427. map.value = new AMap.Map("map-container", {
  1428. zoom: 11, //级别
  1429. center: [120.563757, 31.891174], //中心点坐标
  1430. mapStyle: "amap://styles/f48d96805f5fa7f5aada657c5ee37017",
  1431. zoomEnable: false,
  1432. dragEnable: false,
  1433. });
  1434. let toolBar = new AMap.ToolBar({
  1435. position: {
  1436. top: "40px",
  1437. right: "40px",
  1438. },
  1439. });
  1440. let hawkEye = new AMap.HawkEye({
  1441. opened: false,
  1442. });
  1443. // map.value.addControl(toolBar);
  1444. map.value.addControl(hawkEye);
  1445. let markers = [];
  1446. for (let i of medias.value) {
  1447. let content = `<div style='width:160px'>
  1448. ${
  1449. i.audit == 1
  1450. ? `<img id='img${i.id}' style='width:160px;height:160px;object-fit:contain;' src='${i.viewUrl}'/>`
  1451. : ``
  1452. }
  1453. <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'/
  1454. </div>`;
  1455. let marker = new AMap.Marker({
  1456. content,
  1457. position: new AMap.LngLat(i.longitude, i.latitude),
  1458. offset: new AMap.Pixel(-75, i.audit == 1 ? -195 : -30),
  1459. });
  1460. if (i.audit == 1) {
  1461. marker.on("click", () => {
  1462. openMediaModal(i.viewUrl, 1, "航次图片", i);
  1463. });
  1464. }
  1465. markers.push(marker);
  1466. }
  1467. let overlayGroups = new AMap.OverlayGroup(markers);
  1468. map.value.on("complete", function () {
  1469. let t = setTimeout(() => {
  1470. map.value.add(overlayGroups);
  1471. map.value.setFitView(markers, true, [200, 50, 0, 0], 18);
  1472. clearTimeout(t);
  1473. }, 2000);
  1474. });
  1475. }
  1476. function setShipMarker(longitude = 121.524761, latitude = 31.228721) {
  1477. map.value.setCenter([longitude, latitude]);
  1478. var marker = new AMap.Marker({
  1479. position: new AMap.LngLat(longitude, latitude),
  1480. offset: new AMap.Pixel(-20, -20),
  1481. size: new AMap.Size(80, 80),
  1482. icon: "https://hhd-pat-1255802371.cos.ap-shanghai.myqcloud.com/frontend/ship-red-icon.png", // 添加 Icon 图标 URL
  1483. title: "",
  1484. });
  1485. map.value.add(marker);
  1486. }
  1487. let disabledStatus = ref(true);
  1488. let updateCache = {};
  1489. function changeVoyageInfo() {
  1490. updateCache = _.cloneDeep(voyage.value);
  1491. disabledStatus.value = false;
  1492. }
  1493. function cancelVoyageChange() {
  1494. voyage.value = updateCache;
  1495. disabledStatus.value = true;
  1496. }
  1497. async function submitVoyageChange() {
  1498. let {
  1499. id,
  1500. transStatus,
  1501. loadStartTime,
  1502. loadEndTime,
  1503. dischargeStartTime,
  1504. dischargeEndTime,
  1505. actualDischargeTons,
  1506. remark,
  1507. } = voyage.value;
  1508. let res = await api.updateVoyage({
  1509. id,
  1510. transStatus,
  1511. loadStartTime,
  1512. loadEndTime,
  1513. dischargeStartTime,
  1514. dischargeEndTime,
  1515. actualDischargeTons,
  1516. remark,
  1517. });
  1518. if (res.data.status == 0) {
  1519. ElNotification({
  1520. type: "success",
  1521. title: res.data.msg,
  1522. });
  1523. disabledStatus.value = true;
  1524. } else {
  1525. ElNotification({
  1526. type: "error",
  1527. title: res.data.msg,
  1528. });
  1529. console.log(res);
  1530. }
  1531. }
  1532. let options = ref([
  1533. { value: 0, label: "请选择" },
  1534. {
  1535. value: 1,
  1536. label: "航行",
  1537. },
  1538. {
  1539. value: 2,
  1540. label: "停泊",
  1541. },
  1542. {
  1543. value: 3,
  1544. label: "装货",
  1545. },
  1546. {
  1547. value: 4,
  1548. label: "运输中",
  1549. },
  1550. {
  1551. value: 5,
  1552. label: "卸货",
  1553. },
  1554. ]);
  1555. async function finishVoyage() {
  1556. if (!voyage.value.dischargeEndTime) return;
  1557. let res = await api.finishVoyage({
  1558. voyageId: route.query.id,
  1559. });
  1560. if (res.data.status == 0) {
  1561. voyage.value.voyageStatus = 2;
  1562. ElNotification({
  1563. type: "success",
  1564. title: res.data.msg,
  1565. });
  1566. } else {
  1567. ElNotification({
  1568. type: "error",
  1569. title: res.data.msg,
  1570. });
  1571. }
  1572. }
  1573. let currentUrl = ref("");
  1574. let mediaModal = ref(false);
  1575. let modalType = ref(1);
  1576. let modalTitle = ref();
  1577. let modalPreview = ref([]);
  1578. function openMediaModal(url, type, title) {
  1579. console.log(url, type, title);
  1580. modalPreview.value = [url];
  1581. modalTitle.value = title;
  1582. modalType.value = type;
  1583. currentUrl.value = url;
  1584. mediaModal.value = true;
  1585. }
  1586. async function auditMedia(mediaId, a, index, mediaType) {
  1587. console.log(mediaId, a, index, mediaType);
  1588. let res = await api.auditMedia({
  1589. mediaId,
  1590. audit: a,
  1591. });
  1592. if (res.data.status == 0) {
  1593. medias.value[index].audit = a;
  1594. ElNotification({
  1595. type: "success",
  1596. title: res.data.msg,
  1597. });
  1598. } else {
  1599. console.log(res);
  1600. }
  1601. }
  1602. let dialogImageUrl = ref();
  1603. let dialogVisible = ref(false);
  1604. function handlePictureCardPreview(file) {
  1605. dialogVisible.value = true;
  1606. dialogImageUrl.value = file.url;
  1607. }
  1608. async function handleRemoveBill(file, list) {
  1609. let cache = _.cloneDeep(voyageBill.value);
  1610. console.log(cache);
  1611. ElMessageBox.confirm("确认删除运单?", "Warning", {
  1612. confirmButtonText: "删除",
  1613. cancelButtonText: "取消",
  1614. type: "warning",
  1615. })
  1616. .then(async () => {
  1617. let { id } = file;
  1618. let res = await api.deleteWaybill({
  1619. id,
  1620. });
  1621. if (res.data.status == 0) {
  1622. ElMessage({
  1623. message: "删除成功!",
  1624. type: "success",
  1625. });
  1626. voyageBill.value = list;
  1627. }
  1628. })
  1629. .catch(() => {
  1630. voyageBill.value = cache;
  1631. ElMessage({
  1632. type: "info",
  1633. message: "取消删除",
  1634. });
  1635. });
  1636. }
  1637. let voyageBill = ref([]);
  1638. function billUploadSuccess(response, file, list) {
  1639. list[list.length - 1] = {
  1640. ...response.result,
  1641. url: response.result.viewUrl,
  1642. };
  1643. console.log(list);
  1644. voyageBill.value = list;
  1645. }
  1646. let billParams = ref({
  1647. voyageId: route.query.id,
  1648. });
  1649. async function calExpectedArrivalTime() {
  1650. let res = await api.calExpectedArrivalTime({
  1651. voyageId: route.query.id,
  1652. setSailTime: voyage.value.setSailTime,
  1653. });
  1654. if (res.data.status == 0) {
  1655. voyage.value.expectedArrivalTime = res.data.result;
  1656. }
  1657. }
  1658. let isLoadingExcel = ref(false);
  1659. async function downloadExcel() {
  1660. if (
  1661. !voyage.value.arrivalLoadPortTime ||
  1662. !voyage.value.loadStartTime ||
  1663. !voyage.value.loadEndTime
  1664. ) {
  1665. ElMessage({
  1666. message: "航次尚未开航,无船舶跟踪信息",
  1667. type: "warning",
  1668. });
  1669. return;
  1670. }
  1671. isLoadingExcel.value = true;
  1672. let res = await downloadBlobFile(
  1673. `${url.baseurl}/voyage/exportExcel`,
  1674. { voyageId: route.query.id },
  1675. `${voyage.value.voyageName}-船舶跟踪表`,
  1676. "post",
  1677. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
  1678. );
  1679. if (res) {
  1680. isLoadingExcel.value = false;
  1681. }
  1682. }
  1683. let isDischargeLoadingExcel = ref(false);
  1684. async function exportDischargeExcel() {
  1685. isDischargeLoadingExcel.value = true;
  1686. let res = await downloadBlobFile(
  1687. `${url.baseurl}/voyage/exportDischargeExcel`,
  1688. { voyageId: route.query.id },
  1689. `${voyage.value.voyageName}-卸货记录表`,
  1690. "post",
  1691. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
  1692. );
  1693. if (res) {
  1694. isDischargeLoadingExcel.value = false;
  1695. }
  1696. }
  1697. function subStr(str = "") {
  1698. return str.substring(0, 10);
  1699. }
  1700. let truckTableData = ref([]);
  1701. let truckCurrentPage = ref(1);
  1702. let truckTotal = ref(0);
  1703. async function getTruckLoadRecord() {
  1704. let res = await api.getTruckLoadRecord({
  1705. voyageId: route.query.id,
  1706. portId: currentDiscPortId.value,
  1707. currentPage: truckCurrentPage.value,
  1708. size: 10,
  1709. loginAccountId: localStorage.loginAccountId,
  1710. });
  1711. truckTableData.value = res.data.result;
  1712. truckTotal.value = res.data.total;
  1713. }
  1714. function truckPageChange(e) {
  1715. truckCurrentPage.value = e;
  1716. getTruckLoadRecord();
  1717. }
  1718. let truckRecordForm = ref({});
  1719. let isAddTruckRecordVisable = ref(false);
  1720. let truckRecordBillList = ref([]);
  1721. function showTruckRecord(item, index, text) {
  1722. isAddTruckRecordVisable.value = true;
  1723. if (item.file) {
  1724. truckRecordBillList.value[0] = {
  1725. ...item.file,
  1726. url: item.file.viewUrl,
  1727. };
  1728. }
  1729. truckRecordForm.value = { ...item };
  1730. }
  1731. let truckLoadParams = ref({});
  1732. function getTruckLoadBillList() {}
  1733. let blockchainInfo = ref("");
  1734. let billZipLoading = ref(false);
  1735. async function downloadBillZip() {
  1736. billZipLoading.value = true;
  1737. let res = await downloadBlobFile(
  1738. `${url.baseurl}/voyage/exportBillZip`,
  1739. { voyageId: route.query.id },
  1740. `${voyage.value.voyageName}-单据文件`,
  1741. "post",
  1742. "application/zip"
  1743. );
  1744. billZipLoading.value = false;
  1745. }
  1746. let weatherTableData = ref([]);
  1747. let weatherCurrentPage = ref(1);
  1748. let weatherTotal = ref(0);
  1749. async function getPortWeatherList() {
  1750. let res = await api.getPortWeatherList({
  1751. voyageId: route.query.id,
  1752. portId: currentDiscPortId.value,
  1753. size: 10,
  1754. currentPage: weatherCurrentPage.value,
  1755. });
  1756. weatherTableData.value = res.data.result;
  1757. weatherTotal.value = res.data.total;
  1758. }
  1759. function weatherPageChange(e) {
  1760. weatherCurrentPage.value = e;
  1761. getPortWeatherList();
  1762. }
  1763. let labTableData = ref([]);
  1764. let labTotal = ref(0);
  1765. let labCurrentPage = ref(1);
  1766. async function getLabList() {
  1767. let res = await api.getLabList({
  1768. voyageId: route.query.id,
  1769. portId: currentDiscPortId.value,
  1770. currentPage: labCurrentPage.value,
  1771. size: 10,
  1772. loginAccountId: localStorage.loginAccountId,
  1773. });
  1774. labTableData.value = res.data.result;
  1775. labTotal.value = res.data.total;
  1776. }
  1777. function labPageChange(e) {
  1778. labCurrentPage.value = e;
  1779. getLabList();
  1780. }
  1781. let isAddLabVisable = ref(false);
  1782. let labBillList = ref([]);
  1783. let labForm = ref({});
  1784. function showLab(item) {
  1785. isAddLabVisable.value = true;
  1786. if (item.file) {
  1787. labBillList.value[0] = {
  1788. ...item.file,
  1789. url: item.file.viewUrl,
  1790. };
  1791. }
  1792. labForm.value = { ...item };
  1793. }
  1794. let labModalType = ref("");
  1795. function cancelUploadLab() {}
  1796. let currentPortId = ref("");
  1797. let currentDiscPortId = ref("");
  1798. let currentDiscPortIndex = ref(0);
  1799. function changeDiscPortTab(e) {
  1800. currentDiscPortIndex.value = e.index;
  1801. currentDiscPortId.value =
  1802. voyage.value.voyageDetails[currentDiscPortIndex.value].portId + "";
  1803. weatherTableData.value = [];
  1804. weatherTotal.value = 0;
  1805. labTableData.value = [];
  1806. labTotal.value = 0;
  1807. dischargeList.value = [];
  1808. total.value = 0;
  1809. truckTableData.value = [];
  1810. truckTotal.value = 0;
  1811. labCurrentPage.value = 1;
  1812. dischargeCurrentPage.value = 1;
  1813. weatherCurrentPage.value = 1;
  1814. truckCurrentPage.value = 1;
  1815. getDischargeList();
  1816. getTruckLoadRecord();
  1817. getLabList();
  1818. getPortWeatherList();
  1819. }
  1820. let isAccidentVisable = ref(false);
  1821. let accidentForm = ref({
  1822. files: [],
  1823. });
  1824. let accidentParams = ref({
  1825. voyageId: route.query.id,
  1826. });
  1827. let accidentCurrentPage = ref(1);
  1828. let accidentTotal = ref(0);
  1829. async function getAccidentList() {
  1830. let res = await api.getAccidentList({
  1831. voyageId: route.query.id,
  1832. currentPage: accidentCurrentPage.value,
  1833. size: 10,
  1834. });
  1835. if (res.data.status == 0) {
  1836. accidentList.value = res.data.result;
  1837. accidentTotal.value = res.data.total;
  1838. } else {
  1839. accidentList.value = [];
  1840. accidentTotal.value = 0;
  1841. accidentCurrentPage.value = 1;
  1842. }
  1843. }
  1844. let accidentList = ref([]);
  1845. async function addUpdateAccident() {
  1846. // console.log("accidentForm", accidentForm.value);
  1847. let { accidentTime, remark, files, id } = accidentForm.value;
  1848. let arr = [];
  1849. for (let i of files) {
  1850. arr.push(i.id);
  1851. }
  1852. let postData = {
  1853. loginAccountId: localStorage.loginAccountId,
  1854. voyageId: route.query.id,
  1855. accidentTime,
  1856. remark,
  1857. voyageFileIds: arr.join(","),
  1858. id,
  1859. };
  1860. // console.log("postData", postData);
  1861. let res = await api[id ? "updateAccident" : "addAccident"](postData);
  1862. if (res.data.status == 0) {
  1863. resetAccidentForm();
  1864. isAccidentVisable.value = false;
  1865. }
  1866. getAccidentList();
  1867. }
  1868. function resetAccidentForm() {
  1869. isAccidentCheck.value = false;
  1870. accidentForm.value = {
  1871. files: [],
  1872. };
  1873. }
  1874. function accidentUploadSuccess(e) {
  1875. console.log(e);
  1876. accidentForm.value.files.push(e.result);
  1877. }
  1878. function accidentPageChange(e) {
  1879. accidentCurrentPage.value = e;
  1880. getAccidentList();
  1881. }
  1882. let accidentTitle = ref("");
  1883. function accidentBeforeUpload(e) {
  1884. if (e.type.indexOf("image") != -1) {
  1885. accidentParams.value.mediaType = 1;
  1886. } else {
  1887. accidentParams.value.mediaType = 2;
  1888. }
  1889. }
  1890. let isAccidentCheck = ref(false);
  1891. function checkAccident(item, boo) {
  1892. isAccidentCheck.value = boo;
  1893. if (boo) {
  1894. accidentTitle.value = "查看出险记录";
  1895. } else {
  1896. accidentTitle.value = "修改出险记录";
  1897. }
  1898. isAccidentVisable.value = true;
  1899. item.files = item.files || [];
  1900. accidentForm.value = item;
  1901. }
  1902. function deleteAccident(id) {
  1903. ElMessageBox.confirm("确认删除出险记录?", "提示", {
  1904. confirmButtonText: "确认",
  1905. cancelButtonText: "取消",
  1906. type: "info",
  1907. })
  1908. .then(async () => {
  1909. let res = await api.deleteAccident({
  1910. id,
  1911. });
  1912. if (res.data.status == 0) {
  1913. ElMessage({
  1914. type: "success",
  1915. message: "删除成功!",
  1916. });
  1917. }
  1918. getAccidentList();
  1919. })
  1920. .catch(() => {
  1921. ElMessage({
  1922. type: "info",
  1923. message: "取消删除",
  1924. });
  1925. });
  1926. }
  1927. async function updateSaleStatus(e) {
  1928. let res = await api.updateSaleStatus({
  1929. voyageId: route.query.id,
  1930. saleStatus: e,
  1931. });
  1932. console.log(res);
  1933. if (res.data.status == 0) {
  1934. ElMessage({
  1935. type: "success",
  1936. message: "修改成功!",
  1937. });
  1938. } else {
  1939. ElMessage({
  1940. type: "error",
  1941. message: res.data.msg,
  1942. });
  1943. }
  1944. }
  1945. const cargoOwnerName = ref(localStorage.userName);
  1946. onMounted(() => {
  1947. getVoyageDetail(true);
  1948. });
  1949. </script>
  1950. <style scoped>
  1951. .map-container {
  1952. width: 100%;
  1953. height: 500px;
  1954. }
  1955. .card-note {
  1956. height: 30px;
  1957. font-size: 12px;
  1958. font-family: PingFangSC-Regular, PingFang SC;
  1959. font-weight: 400;
  1960. color: #777777;
  1961. }
  1962. .checkbox-group {
  1963. width: 200px;
  1964. height: 50px;
  1965. margin-top: 20px;
  1966. }
  1967. .medias-content {
  1968. width: 100%;
  1969. height: 600px;
  1970. background: #f7f7f7;
  1971. border-radius: 2px;
  1972. }
  1973. .pic-container {
  1974. width: 100%;
  1975. height: 100%;
  1976. box-sizing: border-box;
  1977. display: flex;
  1978. padding: 30px;
  1979. overflow-x: scroll;
  1980. overflow-y: hidden;
  1981. white-space: nowrap;
  1982. }
  1983. .pic-main {
  1984. position: relative;
  1985. width: 120px;
  1986. }
  1987. .box {
  1988. position: absolute;
  1989. height: 240px;
  1990. width: var(--box-width);
  1991. border: 5px solid #dddddd;
  1992. transition: all 0.5s;
  1993. background: #fff;
  1994. z-index: 10;
  1995. }
  1996. .point {
  1997. position: relative;
  1998. left: 93px;
  1999. top: 258px;
  2000. width: 16px;
  2001. height: 16px;
  2002. background-image: url(../../assets/blue-circle.png);
  2003. }
  2004. .s-line {
  2005. position: absolute;
  2006. left: 100px;
  2007. top: 242px;
  2008. height: 20px;
  2009. border-left: 2px dashed;
  2010. box-sizing: border-box;
  2011. border-color: #ddd;
  2012. }
  2013. .l-line {
  2014. position: relative;
  2015. bottom: 30px;
  2016. left: 111px;
  2017. top: 249px;
  2018. height: 3px;
  2019. width: 100px;
  2020. background-color: #dddddd;
  2021. }
  2022. .bottom-box {
  2023. top: 290px;
  2024. }
  2025. .top210px {
  2026. top: 270px;
  2027. }
  2028. .box:hover {
  2029. transform: scale(1.2);
  2030. }
  2031. .card-note {
  2032. height: 30px;
  2033. font-size: 12px;
  2034. font-family: PingFangSC-Regular, PingFang SC;
  2035. font-weight: 400;
  2036. color: #777777;
  2037. padding: 10px 20px;
  2038. }
  2039. .medias-box {
  2040. width: 100%;
  2041. height: 140px;
  2042. margin-top: 40px;
  2043. }
  2044. .checkbox-group {
  2045. width: 180px;
  2046. height: 50px;
  2047. margin-top: 20px;
  2048. }
  2049. .el-checkbox {
  2050. margin: 0;
  2051. }
  2052. .now-box {
  2053. border: 5px solid #0094fe;
  2054. }
  2055. .now-l-line {
  2056. background-color: #0094fe;
  2057. }
  2058. .now-s-line {
  2059. border-color: #97caf6;
  2060. }
  2061. .now-point {
  2062. filter: grayscale(1);
  2063. }
  2064. .info-line-text-table {
  2065. width: 180px !important;
  2066. }
  2067. .upload-plus-icon {
  2068. height: 15%;
  2069. color: rgb(139, 147, 156);
  2070. line-height: 100px;
  2071. font-size: 40px;
  2072. font-weight: 200;
  2073. }
  2074. .upload-text {
  2075. height: 25%;
  2076. color: rgb(139, 147, 156);
  2077. }
  2078. .info-gap {
  2079. width: 40px;
  2080. font-size: 14px;
  2081. overflow: visible;
  2082. white-space: nowrap;
  2083. color: #006ebc;
  2084. cursor: pointer;
  2085. }
  2086. .unit {
  2087. font-size: 14px;
  2088. font-family: PingFangSC-Regular, PingFang SC;
  2089. font-weight: 400;
  2090. color: #353a42;
  2091. line-height: 100%;
  2092. margin: 0 10px;
  2093. }
  2094. .line {
  2095. flex-wrap: wrap;
  2096. margin: 0 20px;
  2097. }
  2098. .info-line {
  2099. margin-bottom: 20px;
  2100. }
  2101. .ml50 {
  2102. margin-left: 140px;
  2103. }
  2104. </style>