voyageList.vue 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406
  1. <template>
  2. <div class="line-container-p24">
  3. <div class="df jcsb aic">
  4. <div class="df aic">
  5. <div
  6. @click="changeVoyageType(0)"
  7. :class="
  8. status == 0
  9. ? 'currentbtn radio-btns left-radius'
  10. : 'radio-btns left-radius'
  11. "
  12. >
  13. 全部航次
  14. </div>
  15. <div
  16. style="border-left: none"
  17. @click="changeVoyageType(1)"
  18. :class="status == 1 ? 'currentbtn radio-btns' : 'radio-btns'"
  19. >
  20. 装货中
  21. </div>
  22. <div
  23. style="border-left: none"
  24. @click="changeVoyageType(2)"
  25. :class="status == 2 ? 'currentbtn radio-btns' : 'radio-btns'"
  26. >
  27. 运输中
  28. </div>
  29. <div
  30. style="border-left: none"
  31. @click="changeVoyageType(3)"
  32. :class="status == 3 ? 'currentbtn radio-btns' : 'radio-btns'"
  33. >
  34. 卸货中
  35. </div>
  36. <div
  37. style="border-left: none"
  38. @click="changeVoyageType(4)"
  39. :class="status == 4 ? 'currentbtn radio-btns' : 'radio-btns'"
  40. >
  41. 历史航次
  42. </div>
  43. <div
  44. @click="changeVoyageType(5)"
  45. :class="
  46. status == 5
  47. ? 'currentbtn radio-btns right-radius'
  48. : 'radio-btns right-radius '
  49. "
  50. style="margin-right: 40px; border-left: none"
  51. >
  52. 合并航次
  53. </div>
  54. <!-- <div style="color: #333; margin-right: 10px; font-size: 14px">
  55. 预计到港时间:
  56. </div>
  57. <el-radio-group v-model="sortradio">
  58. <el-radio :label="-1">默认排序</el-radio>
  59. <el-radio :label="0">降序</el-radio>
  60. <el-radio :label="1">升序</el-radio>
  61. </el-radio-group> -->
  62. <el-input
  63. :placeholder="
  64. status != 5 ? '请输入船名/MMSI' : '船名/MMSI/合同编号/客户'
  65. "
  66. prefix-icon="el-icon-search"
  67. v-model="term"
  68. clearable
  69. style="width: 240px"
  70. @keyup.enter="getVoyageList(1)"
  71. ></el-input>
  72. <div class="search-btn" @click="getVoyageList(1)">查询</div>
  73. </div>
  74. <!-- <div class="cargo-owner-add" @click="voyageAddDialogVisible = true">
  75. 添加航次
  76. </div> -->
  77. <div>
  78. <div
  79. style="display: inline-block"
  80. class="mr20"
  81. v-if="cacheList.length > 1"
  82. >
  83. <el-button
  84. v-auth="'MERGEVOYAGE'"
  85. @click="showMergeModal()"
  86. size="medium"
  87. type="primary"
  88. >
  89. 合并航次
  90. </el-button>
  91. </div>
  92. <el-popover placement="bottom" :width="100" trigger="hover">
  93. <template #reference>
  94. <el-button size="medium" type="primary">导出</el-button>
  95. </template>
  96. <div
  97. style="
  98. display: flex;
  99. flex-direction: column;
  100. height: 180px;
  101. justify-content: space-between;
  102. "
  103. >
  104. <el-button
  105. type="primary"
  106. size="medium"
  107. v-auth="'DOWNLOADVOYAGELIST'"
  108. @click="showExportModal('航次列表')"
  109. >
  110. 导出航次列表
  111. </el-button>
  112. <el-button
  113. type="primary"
  114. size="medium"
  115. v-auth="'MULTDOWNLOADSHIPTRACK'"
  116. @click="showExportModal('航次跟踪')"
  117. >
  118. 导出航次跟踪
  119. </el-button>
  120. <el-button
  121. type="primary"
  122. size="medium"
  123. @click="showExportModal('卸货记录')"
  124. v-auth="'MULTDOWNLOADDISCHARGE'"
  125. >
  126. 导出卸货记录
  127. </el-button>
  128. <el-button
  129. v-auth="'DOWNLOADFYDI'"
  130. type="primary"
  131. size="medium"
  132. @click="downloadFYDI"
  133. >
  134. 下载FYDI指数
  135. </el-button>
  136. </div>
  137. </el-popover>
  138. </div>
  139. <el-dialog v-model="mergeVisable" title="合并航次" destroy-on-close>
  140. <el-table :data="cacheList" border stripe>
  141. <el-table-column
  142. prop="voyageName"
  143. label="航次名称"
  144. min-width="140"
  145. align="center"
  146. ></el-table-column>
  147. <el-table-column
  148. prop="dischargePort"
  149. label="卸货港"
  150. min-width="80"
  151. align="center"
  152. ></el-table-column>
  153. <el-table-column
  154. prop="cargo"
  155. label="货种"
  156. min-width="70"
  157. align="center"
  158. ></el-table-column>
  159. </el-table>
  160. <el-form
  161. class="mt20"
  162. ref="mergeFormRef"
  163. label-width="100px"
  164. :inline="true"
  165. :rules="mergeRules"
  166. :model="mergeForm"
  167. >
  168. <el-form-item label="合同编号">
  169. <el-input v-model="mergeForm.contractNum" />
  170. </el-form-item>
  171. <el-form-item label="客户名称">
  172. <el-input v-model="mergeForm.clientName" />
  173. </el-form-item>
  174. </el-form>
  175. <div class="df jcfe mt30">
  176. <el-button class="mr20" @click="mergeVisable = false" size="medium">
  177. 取消
  178. </el-button>
  179. <el-button
  180. v-if="mergeForm.contractNum && mergeForm.clientName"
  181. class="mr20"
  182. @click="mergeVoyage"
  183. size="medium"
  184. type="primary"
  185. >
  186. 合并航次
  187. </el-button>
  188. </div>
  189. </el-dialog>
  190. </div>
  191. <el-dialog
  192. v-model="exportModalVisable"
  193. :title="exportModalTitle"
  194. :close-on-click-modal="false"
  195. @close="isLoadingZip = false"
  196. width="200px"
  197. destroy-on-close
  198. >
  199. <div class="df aic jcsb">
  200. <div
  201. v-if="exportModalTitle != '航次列表' || cargoOwnerId == 7"
  202. class="df aic"
  203. >
  204. <div class="mr20">请选择月份:</div>
  205. <el-date-picker
  206. v-model="currentMonth"
  207. type="month"
  208. placeholder="请选择年月"
  209. value-format="YYYYMM"
  210. :disabled="isLoadingZip"
  211. />
  212. </div>
  213. <div></div>
  214. <el-button type="primary" @click="exportZip" :loading="isLoadingZip">
  215. 导出{{ exportModalTitle }}
  216. </el-button>
  217. </div>
  218. </el-dialog>
  219. <el-dialog
  220. v-model="voyageAddDialogVisible"
  221. title="添加航次"
  222. destroy-on-close
  223. >
  224. <el-form
  225. :rules="rules"
  226. label-position="right"
  227. label-width="80px"
  228. ref="addVoyageForm"
  229. :model="voyageForm"
  230. :before-close="resetAddVoyageForm"
  231. >
  232. <div class="df ffw">
  233. <!-- <el-form-item prop="voyageName" label="航次名称">
  234. <el-input v-model="voyageForm.voyageName"></el-input>
  235. </el-form-item>
  236. <el-form-item label=""></el-form-item> -->
  237. <el-form-item prop="shipName" label="船舶">
  238. <!-- <el-input v-model="voyageForm.shipOwnerId"></el-input> -->
  239. <el-autocomplete
  240. v-model="voyageForm.shipName"
  241. :fetch-suggestions="searchShip"
  242. placeholder="选择船舶"
  243. @blur="clear('shipId')"
  244. @select="selectShip"
  245. />
  246. </el-form-item>
  247. <el-form-item prop="cargoOwnerName" label="货主">
  248. <el-autocomplete
  249. v-model="voyageForm.cargoOwnerName"
  250. :fetch-suggestions="searchCargoOwner"
  251. @blur="clear('cargoOwnerId')"
  252. placeholder="选择货主"
  253. @select="selectCargoOwner"
  254. />
  255. </el-form-item>
  256. <el-form-item prop="startTime" label="开始时间">
  257. <el-date-picker
  258. v-model="voyageForm.startTime"
  259. type="date"
  260. value-format="YYYY/MM/DD"
  261. placeholder="航次开始时间"
  262. ></el-date-picker>
  263. </el-form-item>
  264. <el-form-item prop="endTime" label="结束时间">
  265. <el-date-picker
  266. v-model="voyageForm.endTime"
  267. type="date"
  268. value-format="YYYY/MM/DD"
  269. placeholder="航次结束时间"
  270. disabled
  271. ></el-date-picker>
  272. </el-form-item>
  273. <el-form-item prop="loadPort" label="装货港">
  274. <el-autocomplete
  275. v-model="voyageForm.loadPort"
  276. :fetch-suggestions="getCol"
  277. @blur="clear('loadPort')"
  278. placeholder="选择装货港"
  279. @select="selectLoadPort"
  280. />
  281. </el-form-item>
  282. <el-form-item prop="dischargeProt" label="卸货港">
  283. <el-autocomplete
  284. v-model="voyageForm.dischargeProt"
  285. :fetch-suggestions="getCol"
  286. @blur="clear('dischargeProt')"
  287. placeholder="选择卸货港"
  288. @select="selectDischargeProt"
  289. />
  290. </el-form-item>
  291. <el-form-item prop="cargo" label="货种">
  292. <el-input v-model="voyageForm.cargo"></el-input>
  293. </el-form-item>
  294. <el-form-item prop="tons" label="吨位">
  295. <el-input v-model="voyageForm.tons"></el-input>
  296. </el-form-item>
  297. </div>
  298. </el-form>
  299. <template #footer>
  300. <span class="dialog-footer">
  301. <el-button @click="resetAddVoyageForm">取消</el-button>
  302. <el-button type="primary" @click="addVoyage">确定</el-button>
  303. </span>
  304. </template>
  305. </el-dialog>
  306. <div
  307. class="df aic jcfs mt20"
  308. style="
  309. font-size: 14px;
  310. color: #333;
  311. width: calc(100vw - 300px);
  312. flex-wrap: wrap;
  313. "
  314. >
  315. <div class="df jcsb aic mb10 mr20">
  316. <div class="mr10">装货港:</div>
  317. <el-select
  318. style="width: 120px"
  319. v-model="voyageListPostData.loadPortId"
  320. placeholder="装货港"
  321. size="small"
  322. @change="getVoyageList(1)"
  323. @focus="getPortSelect"
  324. filterable
  325. clearable
  326. >
  327. <el-option
  328. v-for="item in portOptions"
  329. :key="item"
  330. :label="item.value"
  331. :value="item.key"
  332. />
  333. </el-select>
  334. </div>
  335. <div class="df jcsb aic mb10 mr20">
  336. <div class="mr10">卸货港:</div>
  337. <el-select
  338. style="width: 120px"
  339. v-model="voyageListPostData.discPortId"
  340. placeholder="卸货港"
  341. size="small"
  342. @change="getVoyageList(1)"
  343. @focus="getPortSelect"
  344. filterable
  345. clearable
  346. >
  347. <el-option
  348. v-for="item in portOptions"
  349. :key="item"
  350. :label="item.value"
  351. :value="item.key"
  352. />
  353. </el-select>
  354. </div>
  355. <div class="df jcsb aic mb10 mr20">
  356. <div class="mr10">到港状态:</div>
  357. <el-select
  358. style="width: 120px"
  359. v-model="voyageListPostData.isArrived"
  360. placeholder="到港状态"
  361. size="small"
  362. @change="getVoyageList(1)"
  363. >
  364. <el-option label="已到港" :value="0" />
  365. <el-option label="未到港" :value="1" />
  366. </el-select>
  367. </div>
  368. <div class="df jcsb aic mb10 mr20">
  369. <div class="mr10">航次状态:</div>
  370. <el-select
  371. style="width: 120px"
  372. v-model="voyageListPostData.abnormalStatus"
  373. placeholder="航次状态"
  374. size="small"
  375. @change="getVoyageList(1)"
  376. >
  377. <el-option label="正常" :value="0" />
  378. <el-option label="异常" :value="1" />
  379. </el-select>
  380. </div>
  381. <div class="df jcsb aic mb10 mr20">
  382. <div class="mr10">货种:</div>
  383. <el-select
  384. style="width: 100px"
  385. v-model="voyageListPostData.cargoId"
  386. placeholder="货种"
  387. size="small"
  388. @change="getVoyageList(1)"
  389. @focus="getCargoSelect"
  390. filterable
  391. clearable
  392. >
  393. <el-option
  394. v-for="item in cargoOptions"
  395. :key="item"
  396. :label="item.value"
  397. :value="item.key"
  398. />
  399. </el-select>
  400. </div>
  401. <div class="df jcsb aic mb10 mr20">
  402. <div class="mr10">保险状态:</div>
  403. <el-select
  404. style="width: 120px"
  405. v-model="voyageListPostData.hasInsurance"
  406. placeholder="保险状态"
  407. size="small"
  408. @change="getVoyageList(1)"
  409. >
  410. <el-option label="未购买" :value="0" />
  411. <el-option label="已购买" :value="1" />
  412. </el-select>
  413. </div>
  414. <el-button @click="resetFilter" class="mb10" size="small" type="primary">
  415. 重置
  416. </el-button>
  417. </div>
  418. <el-table
  419. :data="tableData"
  420. ref="tableRef"
  421. stripe
  422. style="width: 100%; margin-top: 12px"
  423. :row-style="rowStyle"
  424. @select="preSelect"
  425. @select-all="preSelectAll"
  426. @selection-change="preSelectionChange"
  427. >
  428. <el-table-column v-if="status != 5" type="selection" width="55" />
  429. <!-- <el-table-column
  430. type="index"
  431. label="序号"
  432. min-width="80"
  433. align="center"
  434. ></el-table-column> -->
  435. <el-table-column
  436. prop="contractNum"
  437. label="合同编号"
  438. sortable
  439. v-if="status == 5"
  440. min-width="100"
  441. align="center"
  442. ></el-table-column>
  443. <el-table-column
  444. prop="clientName"
  445. label="客户名称"
  446. sortable
  447. v-if="status == 5"
  448. min-width="100"
  449. align="center"
  450. ></el-table-column>
  451. <el-table-column
  452. prop="voyageName"
  453. label="航次编号"
  454. min-width="120"
  455. align="center"
  456. >
  457. <template v-slot="scope">
  458. {{ scope.row.wuchanVoyageInfo?.hhdVoyageCode }}
  459. </template>
  460. </el-table-column>
  461. <el-table-column
  462. prop="voyageName"
  463. label="航次名称"
  464. min-width="140"
  465. align="center"
  466. >
  467. <template v-slot="scope">
  468. {{ scope.row.voyageName
  469. }}{{ `${scope.row.contractNum ? " - 已合并" : ""}` }}
  470. </template>
  471. </el-table-column>
  472. <el-table-column
  473. prop="proxyName"
  474. label="承运商"
  475. min-width="120"
  476. align="center"
  477. ></el-table-column>
  478. <el-table-column
  479. prop="loadPort"
  480. label="装货港"
  481. min-width="90"
  482. align="center"
  483. ></el-table-column>
  484. <el-table-column
  485. prop="dischargePort"
  486. label="卸货港"
  487. min-width="80"
  488. align="center"
  489. ></el-table-column>
  490. <!-- <el-table-column
  491. prop="setSailTime"
  492. label="开航时间"
  493. min-width="100"
  494. align="center"
  495. ></el-table-column> -->
  496. <el-table-column
  497. prop="expectedArrivalTime"
  498. label="预计到港时间"
  499. sortable
  500. min-width="140"
  501. align="center"
  502. >
  503. <template v-slot="scope">
  504. {{ scope.row.arrived ? "已到港" : scope.row.expectedArrivalTime }}
  505. </template>
  506. </el-table-column>
  507. <el-table-column
  508. prop="abnormalStatus"
  509. label="航次状态"
  510. :filters="[
  511. { text: '正常', value: 0 },
  512. { text: '异常', value: 1 },
  513. ]"
  514. :filter-method="filterHandler"
  515. min-width="90"
  516. align="center"
  517. >
  518. <template v-slot="scope">
  519. {{ scope.row.abnormalStatus == 0 ? "正常" : "异常" }}
  520. </template>
  521. </el-table-column>
  522. <el-table-column
  523. prop="daysInPortStr"
  524. label="在港天数"
  525. sortable
  526. min-width="100"
  527. align="center"
  528. ></el-table-column>
  529. <el-table-column
  530. prop="todayPhotoCount"
  531. label="今日日报"
  532. min-width="100"
  533. align="center"
  534. ></el-table-column>
  535. <el-table-column
  536. prop="cargo"
  537. label="货种"
  538. min-width="70"
  539. align="center"
  540. ></el-table-column>
  541. <el-table-column
  542. prop="actualLoadTons"
  543. label="装载吨位"
  544. min-width="80"
  545. align="center"
  546. ></el-table-column>
  547. <el-table-column
  548. prop="unloadedtons"
  549. label="已卸货吨位"
  550. min-width="100"
  551. align="center"
  552. ></el-table-column>
  553. <el-table-column
  554. prop="remainTons"
  555. label="剩余吨位"
  556. min-width="80"
  557. align="center"
  558. ></el-table-column>
  559. <!-- <el-table-column
  560. prop="waybillStatus"
  561. sortable
  562. label="签单状态"
  563. min-width="100"
  564. align="center"
  565. >
  566. <template v-slot="scope">
  567. {{
  568. scope.row.waybillStatus == 0
  569. ? ""
  570. : scope.row.waybillStatus == 1
  571. ? "未签单"
  572. : "已签单"
  573. }}
  574. </template>
  575. </el-table-column> -->
  576. <!-- <el-table-column
  577. prop="transStatus"
  578. label="船舶状态"
  579. min-width="100"
  580. align="center"
  581. ></el-table-column> -->
  582. <el-table-column
  583. prop="hasInsurance"
  584. label="保险状态"
  585. min-width="90"
  586. :filters="[
  587. { text: '未购买', value: 0 },
  588. { text: '已购买', value: 1 },
  589. ]"
  590. :filter-method="filterHandler"
  591. align="center"
  592. >
  593. <template v-slot="scope">
  594. <span :style="scope.row.hasInsurance == 0 ? 'color:red' : ''">
  595. {{ scope.row.hasInsurance == 0 ? "未购买" : "已购买" }}
  596. </span>
  597. </template>
  598. </el-table-column>
  599. <!-- <el-table-column
  600. sortable
  601. prop="createTime"
  602. label="创建时间"
  603. min-width="100"
  604. align="center"
  605. >
  606. <template v-slot="scope">
  607. {{ subTimeStr(scope.row.createTime) }}
  608. </template>
  609. </el-table-column>-->
  610. <el-table-column
  611. prop="cargoOwnerRemark"
  612. label="备注"
  613. min-width="100"
  614. align="center"
  615. ></el-table-column>
  616. <el-table-column
  617. label="解除"
  618. min-width="80"
  619. align="center"
  620. v-if="status == 5"
  621. >
  622. <template v-slot="scope">
  623. <el-button
  624. v-auth="'UNMERGEVOYAGE'"
  625. @click="showUnmergeModal(scope.row.id)"
  626. type="text"
  627. size="small"
  628. >
  629. 解除合并
  630. </el-button>
  631. </template>
  632. </el-table-column>
  633. <el-table-column
  634. v-auth="'VOYAGEDETAIL'"
  635. label="操作"
  636. min-width="80"
  637. align="center"
  638. fixed="right"
  639. >
  640. <template v-slot="scope">
  641. <el-button
  642. @click="voyageDetail(scope.row.id, tableData)"
  643. type="text"
  644. size="small"
  645. >
  646. 查看详情
  647. </el-button>
  648. </template>
  649. </el-table-column>
  650. </el-table>
  651. <div style="width: 100%; text-align: right; margin-top: 43px">
  652. <el-pagination
  653. v-model:current-page="currentPage"
  654. v-model:page-size="pageSize"
  655. :page-sizes="[50, 100, 200]"
  656. background
  657. layout="sizes, prev, pager, next"
  658. :total="total"
  659. @size-change="sizeChange"
  660. @current-change="pageChange"
  661. />
  662. </div>
  663. </div>
  664. </template>
  665. <script setup>
  666. import { ref, h, reactive, toRefs, onMounted } from "vue";
  667. import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
  668. import store from "../../store";
  669. import router from "../../router";
  670. import md5 from "md5";
  671. import api from "../../apis/fetch";
  672. import _ from "lodash";
  673. import { subTimeStr } from "../../utils/utils";
  674. import downloadBlobFile from "../../utils/downloadBlobFile";
  675. import url from "../../apis/config";
  676. let currentPage = ref(1);
  677. let pageSize = ref(50);
  678. let term = ref("");
  679. let tableData = ref([]);
  680. let total = ref(0);
  681. let status = ref(3);
  682. let loginAccountId = ref("");
  683. let voyageListPostData = ref({});
  684. let cargoOwnerId = localStorage.userId;
  685. async function getVoyageList(type) {
  686. tableData.value = [];
  687. currentPage.value = type || currentPage.value;
  688. let res = await api.getVoyageList({
  689. ...voyageListPostData.value,
  690. loginAccountId: localStorage.loginAccountId,
  691. cargoOwnerId: localStorage.userId,
  692. shipId: 0,
  693. status: status.value,
  694. term: term.value,
  695. currentPage: currentPage.value,
  696. size: pageSize.value,
  697. });
  698. if (res.data.status == 0) {
  699. tableData.value = res.data.result;
  700. total.value = res.data.total;
  701. reSelect();
  702. } else {
  703. tableData.value = [];
  704. total.value = 0;
  705. }
  706. }
  707. function changeVoyageType(s) {
  708. currentPage.value = 1;
  709. status.value = s;
  710. cacheList.value = [];
  711. getVoyageList(1);
  712. }
  713. async function voyageDetail(id) {
  714. router.push({
  715. path: "/voyage/voyageDetail",
  716. query: {
  717. id,
  718. },
  719. });
  720. }
  721. function pageChange(e) {
  722. currentPage.value = e;
  723. getVoyageList();
  724. }
  725. function sizeChange() {
  726. currentPage.value = 1;
  727. getVoyageList();
  728. }
  729. function goToVoyageAdd() {
  730. router.push({
  731. path: "/voyage/voyageAdd",
  732. });
  733. }
  734. let voyageAddDialogVisible = ref(false);
  735. const rules = reactive({
  736. rules: {
  737. voyageName: [
  738. { required: false, message: "请填写航次名称", trigger: "blur" },
  739. ],
  740. shipName: [{ required: true, message: "请选择船舶", trigger: "blur" }],
  741. cargoOwnerName: [
  742. { required: true, message: "请选择货主", trigger: "blur" },
  743. ],
  744. startTime: [{ required: true, message: "请填写开始时间", trigger: "blur" }],
  745. loadPort: [{ required: true, message: "请填写装货港", trigger: "blur" }],
  746. dischargeProt: [
  747. { required: true, message: "请填写卸货港", trigger: "blur" },
  748. ],
  749. cargo: [{ required: true, message: "请填写货种", trigger: "blur" }],
  750. tons: [{ required: true, message: "请填写吨位", trigger: "blur" }],
  751. },
  752. });
  753. let voyageForm = reactive({
  754. voyageForm: {
  755. voyageName: "",
  756. cargoOwnerId: "",
  757. cargoOwnerName: "",
  758. startTime: "",
  759. endTime: "",
  760. loadPort: "",
  761. dischargeProt: "",
  762. cargo: "",
  763. tons: "",
  764. loadPortId: "",
  765. dischargeProtId: "",
  766. shipId: "",
  767. shipName: "",
  768. },
  769. });
  770. function clear(type) {
  771. setTimeout(() => {
  772. switch (type) {
  773. case "shipId": {
  774. let index = ref(-1);
  775. for (let i in shipsCache.value) {
  776. if (voyageForm.voyageForm.shipName == shipsCache.value[i].shipName) {
  777. index.value = i;
  778. break;
  779. }
  780. }
  781. if (index.value != -1) {
  782. voyageForm.voyageForm.shipId = shipsCache.value[index.value].shipId;
  783. } else {
  784. let b = shipsCache.value.some((item) => {
  785. return (
  786. item.shipId == voyageForm.voyageForm.shipId &&
  787. item.shipName == voyageForm.voyageForm.shipName
  788. );
  789. });
  790. voyageForm.voyageForm["shipId"] = "";
  791. voyageForm.voyageForm["shipName"] = "";
  792. }
  793. break;
  794. }
  795. case "cargoOwnerId": {
  796. let index = ref(-1);
  797. for (let i in cargoOwnersCache.value) {
  798. if (
  799. voyageForm.voyageForm.cargoOwnerName ==
  800. cargoOwnersCache.value[i].userName
  801. ) {
  802. index.value = i;
  803. break;
  804. }
  805. }
  806. if (index.value != -1) {
  807. voyageForm.voyageForm.cargoOwnerId =
  808. cargoOwnersCache.value[index.value].userId;
  809. } else {
  810. let b = cargoOwnersCache.value.some((item) => {
  811. return (
  812. item.userId == voyageForm.voyageForm.cargoOwnerId &&
  813. item.userName == voyageForm.voyageForm.cargoOwnerName
  814. );
  815. });
  816. if (!b) {
  817. voyageForm.voyageForm["cargoOwnerId"] = "";
  818. voyageForm.voyageForm["cargoOwnerName"] = "";
  819. }
  820. }
  821. break;
  822. }
  823. case "loadPort": {
  824. let index = ref(-1);
  825. for (let i in colCache.value) {
  826. if (voyageForm.voyageForm.loadPort == colCache.value[i].value) {
  827. index.value = i;
  828. break;
  829. }
  830. }
  831. if (index.value != -1) {
  832. voyageForm.voyageForm.loadPortId = colCache.value[index.value].key;
  833. } else {
  834. let b = colCache.value.some((item) => {
  835. return (
  836. item.value == voyageForm.voyageForm.loadPort &&
  837. item.key == voyageForm.voyageForm.loadPortId
  838. );
  839. });
  840. if (!b) {
  841. voyageForm.voyageForm["loadPort"] = "";
  842. voyageForm.voyageForm["loadPortId"] = "";
  843. }
  844. }
  845. break;
  846. }
  847. case "dischargeProt": {
  848. let index = ref(-1);
  849. for (let i in colCache.value) {
  850. if (voyageForm.voyageForm.dischargeProt == colCache.value[i].value) {
  851. index.value = i;
  852. break;
  853. }
  854. }
  855. if (index.value != -1) {
  856. voyageForm.voyageForm.dischargeProtId =
  857. colCache.value[index.value].key;
  858. } else {
  859. let b = colCache.value.some((item) => {
  860. return (
  861. item.value == voyageForm.voyageForm.dischargeProt &&
  862. item.key == voyageForm.voyageForm.dischargeProtId
  863. );
  864. });
  865. if (!b) {
  866. voyageForm.voyageForm["dischargeProt"] = "";
  867. voyageForm.voyageForm["dischargeProtId"] = "";
  868. }
  869. }
  870. break;
  871. }
  872. }
  873. }, 200);
  874. }
  875. let addVoyageForm = ref(null);
  876. async function addVoyage() {
  877. addVoyageForm.value.validate(async (valid) => {
  878. if (valid) {
  879. console.log("提交", voyageForm.voyageForm);
  880. let res = await api.addVoyage({
  881. ...voyageForm.voyageForm,
  882. });
  883. if (res.data.status == 0) {
  884. ElNotification({
  885. title: res.data.msg,
  886. type: "success",
  887. });
  888. resetAddVoyageForm();
  889. getVoyageList(1);
  890. } else {
  891. console.log(res);
  892. }
  893. }
  894. });
  895. }
  896. let shipsCache = ref([]);
  897. let colCache = ref([]);
  898. let cargoOwnersCache = ref([]);
  899. const searchShip = _.debounce(
  900. async (queryString, cb) => {
  901. if (!queryString) return;
  902. let res = await api.searchShip({
  903. term: queryString,
  904. });
  905. let ships = [];
  906. if (res.data.status == 0) {
  907. ships = res.data.result;
  908. for (let i of ships) {
  909. i.value = `${i.shipName}`;
  910. }
  911. shipsCache.value = ships;
  912. cb(ships);
  913. }
  914. },
  915. 1000,
  916. { leading: true }
  917. );
  918. const selectShip = (item) => {
  919. voyageForm.voyageForm.shipId = item.shipId;
  920. };
  921. const searchCargoOwner = _.debounce(
  922. async (queryString, cb) => {
  923. if (!queryString) return;
  924. let res = await api.searchUser({
  925. term: queryString,
  926. identity: 2,
  927. });
  928. let cargoOwners = [];
  929. if (res.data.status == 0) {
  930. cargoOwners = res.data.result;
  931. for (let i of cargoOwners) {
  932. i.value = `${i.userName}`;
  933. }
  934. cargoOwnersCache.value = cargoOwners;
  935. cb(cargoOwners);
  936. }
  937. },
  938. 1000,
  939. { leading: true }
  940. );
  941. const selectCargoOwner = (item) => {
  942. voyageForm.voyageForm.cargoOwnerId = item.userId;
  943. };
  944. const getCol = _.debounce(
  945. async (queryString, cb) => {
  946. if (!queryString) return;
  947. let res = await api.getCol({
  948. term: queryString,
  949. });
  950. if (res.data.status == 0) {
  951. colCache.value = [...colCache.value, ...res.data.result];
  952. colCache.value = _.uniqBy(colCache.value, "key");
  953. cb(res.data.result);
  954. }
  955. },
  956. 1000,
  957. { leading: true }
  958. );
  959. const selectLoadPort = (item) => {
  960. voyageForm.voyageForm.loadPortId = item.key;
  961. voyageForm.voyageForm.loadPort = item.value;
  962. };
  963. const selectDischargeProt = (item) => {
  964. voyageForm.voyageForm.dischargeProtId = item.key;
  965. voyageForm.voyageForm.dischargeProt = item.value;
  966. };
  967. function resetAddVoyageForm() {
  968. voyageAddDialogVisible.value = false;
  969. addVoyageForm.value.resetFields();
  970. }
  971. let sortradio = ref(0);
  972. async function downloadFYDI() {
  973. let res0 = await api.getFYFIDownloadUrl({
  974. loginAccountId: localStorage.loginAccountId,
  975. });
  976. if (res0.data.result == 1) {
  977. ElNotification({
  978. type: "info",
  979. title: "更新中",
  980. });
  981. } else {
  982. let url = res0.data.result;
  983. let a = document.createElement("a");
  984. a.setAttribute("href", url);
  985. a.click();
  986. }
  987. }
  988. let exportModalVisable = ref(false);
  989. let exportModalTitle = ref("");
  990. let currentMonth = ref("");
  991. function showExportModal(type) {
  992. exportModalVisable.value = true;
  993. exportModalTitle.value = type;
  994. }
  995. let isLoadingZip = ref(false);
  996. async function exportZip() {
  997. if (!currentMonth.value && exportModalTitle.value != "航次列表") return;
  998. isLoadingZip.value = true;
  999. let path = "";
  1000. let type = "";
  1001. let postData = {
  1002. loginAccountId: localStorage.loginAccountId,
  1003. };
  1004. let title = "";
  1005. switch (exportModalTitle.value) {
  1006. case "航次列表": {
  1007. if (localStorage.userId == 7) {
  1008. path = "/voyage/exportVoyageReportExcel";
  1009. postData.loginAccountId = localStorage.loginAccountId;
  1010. postData.date = currentMonth.value;
  1011. } else {
  1012. path = "/voyage/exportListExcel";
  1013. let arr = [];
  1014. for (let i of tableData.value) {
  1015. arr.push(i.id);
  1016. }
  1017. postData.voyageIds = arr.join(",");
  1018. }
  1019. type =
  1020. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
  1021. title = `${exportModalTitle.value}`;
  1022. break;
  1023. }
  1024. case "航次跟踪": {
  1025. path = "/voyage/exportMultExcel";
  1026. type = "application/zip";
  1027. postData.date = currentMonth.value;
  1028. title = `${exportModalTitle.value}${currentMonth.value}`;
  1029. break;
  1030. }
  1031. case "卸货记录": {
  1032. path = "/voyage/exportMultDischargeExcel";
  1033. type = "application/zip";
  1034. postData.date = currentMonth.value;
  1035. title = `${exportModalTitle.value}${currentMonth.value}`;
  1036. break;
  1037. }
  1038. }
  1039. try {
  1040. let res = await downloadBlobFile(
  1041. `${url.baseurl}${path}`,
  1042. postData,
  1043. title,
  1044. "post",
  1045. type
  1046. );
  1047. if (res.status == 0) {
  1048. ElNotification({
  1049. title: "导出成功!",
  1050. type: "success",
  1051. });
  1052. } else {
  1053. ElNotification({
  1054. title: "暂无数据",
  1055. });
  1056. }
  1057. } catch (error) {
  1058. ElNotification({
  1059. title: "暂无数据",
  1060. });
  1061. } finally {
  1062. isLoadingZip.value = false;
  1063. currentMonth.value = "";
  1064. exportModalVisable.value = false;
  1065. }
  1066. }
  1067. function rowStyle({ row }) {
  1068. let rowStyle = {};
  1069. if (row.daysInPort >= 20 && row.daysInPort < 25) {
  1070. rowStyle.color = "#f9ca24";
  1071. } else if (row.daysInPort >= 25 && row.daysInPort < 30) {
  1072. rowStyle.color = "#f0932b";
  1073. } else if (row.daysInPort >= 30) {
  1074. rowStyle.color = "#eb4d4b";
  1075. }
  1076. return rowStyle;
  1077. }
  1078. let loadPortFilterStr = ref("");
  1079. function selectLoadPortFilter(item) {
  1080. voyageListPostData.value.loadPortId = item.key;
  1081. getVoyageList(1);
  1082. }
  1083. let discPortFilterStr = ref("");
  1084. function selectDiscPortFilter(item) {
  1085. voyageListPostData.value.discPortId = item.key;
  1086. getVoyageList(1);
  1087. }
  1088. let cargoFilterStr = ref("");
  1089. function selectCargoFilter(item) {
  1090. voyageListPostData.value.cargo = item.key;
  1091. getVoyageList(1);
  1092. }
  1093. function resetFilter() {
  1094. loadPortFilterStr.value = "";
  1095. discPortFilterStr.value = "";
  1096. cargoFilterStr.value = "";
  1097. cacheList.value = [];
  1098. voyageListPostData.value = {};
  1099. getVoyageList(1);
  1100. }
  1101. let cargoOptions = ref([]);
  1102. async function getCargoSelect() {
  1103. if (cargoOptions.value.length) return;
  1104. let res = await api.getCargoSelect({
  1105. loginAccountId: loginAccountId.value,
  1106. status: 2,
  1107. term: "",
  1108. });
  1109. cargoOptions.value = res.data.result;
  1110. }
  1111. let portOptions = ref([]);
  1112. async function getPortSelect() {
  1113. if (portOptions.value.length) return;
  1114. let res = await api.getCol({
  1115. term: "",
  1116. });
  1117. portOptions.value = res.data.result;
  1118. }
  1119. let isDelete = ref(false);
  1120. let cacheList = ref([]);
  1121. function preSelect(e, row) {
  1122. console.log(e);
  1123. for (let i in cacheList.value) {
  1124. if (cacheList.value[i].id == row.id) {
  1125. cacheList.value.splice(i, 1);
  1126. break;
  1127. }
  1128. }
  1129. }
  1130. function preSelectAll(e) {
  1131. if (!e.length) {
  1132. let arr = tableData.value;
  1133. cacheList.value = cacheList.value.filter(
  1134. (x) => !arr.some((item) => x.id == item.id)
  1135. );
  1136. }
  1137. }
  1138. function preSelectionChange(e) {
  1139. cacheList.value = cacheList.value.concat(e);
  1140. cacheList.value = _.uniqWith(cacheList.value, _.isEqual);
  1141. }
  1142. let tableRef = ref(null);
  1143. function reSelect() {
  1144. for (let i of tableData.value) {
  1145. for (let j of cacheList.value) {
  1146. if (i.id == j.id) {
  1147. tableRef.value.toggleRowSelection(i, true);
  1148. continue;
  1149. }
  1150. }
  1151. }
  1152. }
  1153. let mergeVisable = ref(false);
  1154. let mergeFormRef = ref(null);
  1155. let mergeForm = ref({});
  1156. let mergeRules = ref({});
  1157. async function mergeVoyage() {
  1158. let arr = [];
  1159. for (let i of cacheList.value) {
  1160. arr.push(i.id);
  1161. }
  1162. let res = await api.mergeVoyage({
  1163. ...mergeForm.value,
  1164. voyageIds: arr.join(","),
  1165. });
  1166. if (res.data.status == 0) {
  1167. ElNotification({
  1168. title: res.data.msg,
  1169. type: "success",
  1170. });
  1171. }
  1172. cacheList.value = [];
  1173. clearMergeForm();
  1174. getVoyageList();
  1175. }
  1176. async function showUnmergeModal(voyageId) {
  1177. ElMessageBox.confirm("确认解除航次合并?", "解除合并", {
  1178. confirmButtonText: "确认",
  1179. cancelButtonText: "取消",
  1180. type: "warning",
  1181. }).then(async () => {
  1182. let res = await api.unmergeVoyage({
  1183. voyageId,
  1184. });
  1185. getVoyageList();
  1186. });
  1187. }
  1188. function clearMergeForm() {
  1189. mergeVisable.value = false;
  1190. mergeForm.value = {};
  1191. }
  1192. function showMergeModal() {
  1193. let arr = [];
  1194. for (let i of cacheList.value) {
  1195. if (!i.contractNum) {
  1196. arr.push(i);
  1197. }
  1198. }
  1199. if (arr.length > 1) {
  1200. cacheList.value = arr;
  1201. mergeVisable.value = true;
  1202. } else {
  1203. ElNotification({
  1204. title: "列表包含已合并航次",
  1205. type: "info",
  1206. });
  1207. }
  1208. }
  1209. function filterHandler(value, row, column) {
  1210. const property = column["property"];
  1211. return row[property] === value;
  1212. }
  1213. onMounted(() => {
  1214. getVoyageList(1);
  1215. loginAccountId.value = localStorage.loginAccountId;
  1216. });
  1217. </script>
  1218. <style scoped>
  1219. .search-btn {
  1220. display: inline-block;
  1221. width: 60px;
  1222. height: 32px;
  1223. background: #0094fe;
  1224. border-radius: 2px;
  1225. font-size: 14px;
  1226. font-family: PingFangSC-Regular, PingFang SC;
  1227. font-weight: 400;
  1228. color: #ffffff;
  1229. text-align: center;
  1230. line-height: 32px;
  1231. margin-left: 10px;
  1232. cursor: pointer;
  1233. }
  1234. .cargo-owner-add {
  1235. width: 80px;
  1236. height: 32px;
  1237. border-radius: 2px;
  1238. border: 1px solid #0094fe;
  1239. font-size: 14px;
  1240. font-family: PingFangSC-Regular, PingFang SC;
  1241. font-weight: 400;
  1242. color: #0094fe;
  1243. line-height: 32px;
  1244. text-align: center;
  1245. cursor: pointer;
  1246. }
  1247. :deep().el-dialog {
  1248. width: 560px;
  1249. padding: 20px 50px;
  1250. border-radius: 6px;
  1251. }
  1252. :deep() .el-dialog__title {
  1253. font-size: 18px;
  1254. font-family: PingFangSC-Regular, PingFang SC;
  1255. font-weight: 400;
  1256. color: #0094fe;
  1257. }
  1258. .normal-label {
  1259. font-size: 14px;
  1260. font-family: PingFangSC-Regular, PingFang SC;
  1261. font-weight: 400;
  1262. color: #353a42;
  1263. margin-right: 10px;
  1264. }
  1265. .show-input {
  1266. width: 280px;
  1267. height: 32px;
  1268. background: #ffffff;
  1269. border-radius: 2px;
  1270. border: 1px solid #dee0e3;
  1271. font-size: 14px;
  1272. font-family: PingFangSC-Regular, PingFang SC;
  1273. font-weight: 400;
  1274. color: #333333;
  1275. line-height: 32px;
  1276. padding-left: 12px;
  1277. margin-right: 40px;
  1278. }
  1279. .radio-btns {
  1280. height: 38px;
  1281. width: 70px;
  1282. border: 1px solid #1486f9;
  1283. line-height: 38px;
  1284. text-align: center;
  1285. font-size: 14px;
  1286. font-family: PingFangSC-Regular, PingFang SC;
  1287. font-weight: 400;
  1288. color: #0094fe;
  1289. cursor: pointer;
  1290. }
  1291. .left-radius {
  1292. border-top-left-radius: 19px;
  1293. border-bottom-left-radius: 19px;
  1294. width: 80px;
  1295. }
  1296. .right-radius {
  1297. border-top-right-radius: 19px;
  1298. border-bottom-right-radius: 19px;
  1299. width: 80px;
  1300. }
  1301. .currentbtn {
  1302. background: #1486f9;
  1303. color: #fff;
  1304. }
  1305. .search-btn {
  1306. display: inline-block;
  1307. width: 60px;
  1308. height: 38px;
  1309. background: #0094fe;
  1310. border-radius: 2px;
  1311. font-size: 14px;
  1312. font-family: PingFangSC-Regular, PingFang SC;
  1313. font-weight: 400;
  1314. color: #ffffff;
  1315. text-align: center;
  1316. line-height: 38px;
  1317. margin-left: 10px;
  1318. cursor: pointer;
  1319. }
  1320. .voyage-add {
  1321. width: 80px;
  1322. height: 36px;
  1323. border-radius: 2px;
  1324. border: 1px solid #0094fe;
  1325. font-size: 14px;
  1326. font-family: PingFangSC-Regular, PingFang SC;
  1327. font-weight: 400;
  1328. color: #0094fe;
  1329. line-height: 36px;
  1330. text-align: center;
  1331. cursor: pointer;
  1332. }
  1333. :deep() .el-dialog {
  1334. width: 800px;
  1335. }
  1336. :deep() .el-form-item {
  1337. margin-right: 22px;
  1338. width: 300px;
  1339. }
  1340. :deep() .el-autocomplete {
  1341. width: 220px;
  1342. }
  1343. .el-radio {
  1344. margin-right: 10px;
  1345. }
  1346. .el-radio:last-child {
  1347. margin-right: 20px;
  1348. }
  1349. .el-button {
  1350. margin-left: 0;
  1351. }
  1352. </style>