voyageList.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. <template>
  2. <div class="full-container-p24">
  3. <div style="display: flex; justify-content: space-between">
  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. @click="changeVoyageType(4)"
  38. :class="
  39. status == 4
  40. ? 'currentbtn radio-btns right-radius'
  41. : 'radio-btns right-radius '
  42. "
  43. style="margin-right: 40px; border-left: none"
  44. >
  45. 历史航次
  46. </div>
  47. <el-input
  48. placeholder="请输入货主名称/船名/MMSI"
  49. prefix-icon="el-icon-search"
  50. v-model="term"
  51. clearable
  52. style="width: 330px"
  53. ></el-input>
  54. <div class="search-btn" @click="getVoyageList()">查询</div>
  55. </div>
  56. <!-- <div class="cargo-owner-add" @click="voyageAddDialogVisible = true">
  57. 添加航次
  58. </div> -->
  59. </div>
  60. <el-dialog v-model="voyageAddDialogVisible" title="添加航次">
  61. <el-form
  62. :rules="rules"
  63. label-position="right"
  64. label-width="80px"
  65. ref="addVoyageForm"
  66. :model="voyageForm"
  67. :before-close="resetAddVoyageForm"
  68. >
  69. <div class="df ffw">
  70. <!-- <el-form-item prop="voyageName" label="航次名称">
  71. <el-input v-model="voyageForm.voyageName"></el-input>
  72. </el-form-item>
  73. <el-form-item label=""></el-form-item> -->
  74. <el-form-item prop="shipName" label="船舶">
  75. <!-- <el-input v-model="voyageForm.shipOwnerId"></el-input> -->
  76. <el-autocomplete
  77. v-model="voyageForm.shipName"
  78. :fetch-suggestions="searchShip"
  79. placeholder="选择船舶"
  80. @blur="clear('shipId')"
  81. @select="selectShip"
  82. />
  83. </el-form-item>
  84. <el-form-item prop="cargoOwnerName" label="货主">
  85. <el-autocomplete
  86. v-model="voyageForm.cargoOwnerName"
  87. :fetch-suggestions="searchCargoOwner"
  88. @blur="clear('cargoOwnerId')"
  89. placeholder="选择货主"
  90. @select="selectCargoOwner"
  91. />
  92. </el-form-item>
  93. <el-form-item prop="startTime" label="开始时间">
  94. <el-date-picker
  95. v-model="voyageForm.startTime"
  96. type="date"
  97. value-format="YYYY/MM/DD"
  98. placeholder="航次开始时间"
  99. ></el-date-picker>
  100. </el-form-item>
  101. <el-form-item prop="endTime" label="结束时间">
  102. <el-date-picker
  103. v-model="voyageForm.endTime"
  104. type="date"
  105. value-format="YYYY/MM/DD"
  106. placeholder="航次结束时间"
  107. disabled
  108. ></el-date-picker>
  109. </el-form-item>
  110. <el-form-item prop="loadPort" label="装货港">
  111. <el-autocomplete
  112. v-model="voyageForm.loadPort"
  113. :fetch-suggestions="getCol"
  114. @blur="clear('loadPort')"
  115. placeholder="选择装货港"
  116. @select="selectLoadPort"
  117. />
  118. </el-form-item>
  119. <el-form-item prop="dischargeProt" label="卸货港">
  120. <el-autocomplete
  121. v-model="voyageForm.dischargeProt"
  122. :fetch-suggestions="getCol"
  123. @blur="clear('dischargeProt')"
  124. placeholder="选择卸货港"
  125. @select="selectDischargeProt"
  126. />
  127. </el-form-item>
  128. <el-form-item prop="cargo" label="货种">
  129. <el-input v-model="voyageForm.cargo"></el-input>
  130. </el-form-item>
  131. <el-form-item prop="tons" label="吨位">
  132. <el-input v-model="voyageForm.tons"></el-input>
  133. </el-form-item>
  134. </div>
  135. </el-form>
  136. <template #footer>
  137. <span class="dialog-footer">
  138. <el-button @click="resetAddVoyageForm">取消</el-button>
  139. <el-button type="primary" @click="addVoyage">确定</el-button>
  140. </span>
  141. </template>
  142. </el-dialog>
  143. <el-table :data="tableData" stripe style="width: 100%; margin-top: 24px">
  144. <el-table-column
  145. type="index"
  146. label="序号"
  147. min-width="80"
  148. align="center"
  149. ></el-table-column>
  150. <el-table-column
  151. prop="voyageName"
  152. label="航次名称"
  153. min-width="140"
  154. align="center"
  155. ></el-table-column>
  156. <el-table-column
  157. prop="loadDiscPort"
  158. label="装货港-卸货港"
  159. min-width="200"
  160. align="center"
  161. ></el-table-column>
  162. <el-table-column
  163. prop="setSailTime"
  164. label="开航时间"
  165. min-width="180"
  166. align="center"
  167. ></el-table-column>
  168. <el-table-column
  169. prop="expectedArrivalTime"
  170. label="预计到港时间"
  171. min-width="180"
  172. align="center"
  173. ></el-table-column>
  174. <el-table-column
  175. prop="todayPhotoCount"
  176. label="今日照片"
  177. min-width="80"
  178. align="center"
  179. ></el-table-column>
  180. <el-table-column
  181. prop="cargo"
  182. label="货种"
  183. min-width="80"
  184. align="center"
  185. ></el-table-column>
  186. <el-table-column
  187. prop="tons"
  188. label="吨位(吨)"
  189. min-width="80"
  190. align="center"
  191. ></el-table-column>
  192. <el-table-column
  193. prop="waybillStatus"
  194. sortable
  195. label="签单状态"
  196. min-width="100"
  197. align="center"
  198. >
  199. <template v-slot="scope">
  200. {{
  201. scope.row.waybillStatus == 0
  202. ? ""
  203. : scope.row.waybillStatus == 1
  204. ? "未签单"
  205. : "已签单"
  206. }}
  207. </template>
  208. </el-table-column>
  209. <el-table-column
  210. prop="transStatus"
  211. label="船舶状态"
  212. min-width="100"
  213. align="center"
  214. ></el-table-column>
  215. <el-table-column
  216. sortable
  217. prop="createTime"
  218. label="创建时间"
  219. min-width="100"
  220. align="center"
  221. ></el-table-column>
  222. <el-table-column
  223. prop="remark"
  224. label="备注"
  225. min-width="100"
  226. align="center"
  227. ></el-table-column>
  228. <el-table-column label="操作" min-width="80" align="center">
  229. <template v-slot="scope">
  230. <el-button
  231. @click="voyageDetail(scope.row.id, tableData)"
  232. type="text"
  233. size="small"
  234. >
  235. 查看详情
  236. </el-button>
  237. </template>
  238. </el-table-column>
  239. </el-table>
  240. <div style="width: 100%; text-align: right; margin-top: 43px">
  241. <el-pagination
  242. background
  243. layout="prev, pager, next"
  244. :total="total"
  245. @current-change="pageChange"
  246. ></el-pagination>
  247. </div>
  248. </div>
  249. </template>
  250. <script>
  251. import { ref, h, reactive, toRefs, onMounted } from "vue";
  252. import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
  253. import store from "../../store";
  254. import router from "../../router";
  255. import md5 from "md5";
  256. import api from "../../apis/fetch";
  257. import _ from "lodash";
  258. export default {
  259. setup() {
  260. let currentPage = ref(1);
  261. let term = ref();
  262. let tableData = ref();
  263. let total = ref(0);
  264. let status = ref(0);
  265. async function getVoyageList() {
  266. tableData.value = [];
  267. let res = await api.getVoyageList({
  268. cargoOwnerId: localStorage.userId,
  269. shipId: 0,
  270. status: status.value,
  271. term: term.value,
  272. currentPage: currentPage.value,
  273. size: 10,
  274. });
  275. term.value = "";
  276. if (res.data.status == 0) {
  277. tableData.value = res.data.result;
  278. total.value = res.data.total;
  279. } else {
  280. ElNotification({
  281. type: "error",
  282. title: res.data.msg,
  283. });
  284. }
  285. }
  286. function changeVoyageType(s) {
  287. term.value = "";
  288. currentPage.value = 1;
  289. status.value = s;
  290. getVoyageList();
  291. }
  292. async function voyageDetail(id) {
  293. router.push({
  294. path: "/voyage/voyageDetail",
  295. query: {
  296. id,
  297. },
  298. });
  299. }
  300. function pageChange(e) {
  301. currentPage.value = e;
  302. getVoyageList();
  303. }
  304. function goToVoyageAdd() {
  305. router.push({
  306. path: "/voyage/voyageAdd",
  307. });
  308. }
  309. let voyageAddDialogVisible = ref(false);
  310. const rules = reactive({
  311. rules: {
  312. voyageName: [
  313. { required: false, message: "请填写航次名称", trigger: "blur" },
  314. ],
  315. shipName: [{ required: true, message: "请选择船舶", trigger: "blur" }],
  316. cargoOwnerName: [
  317. { required: true, message: "请选择货主", trigger: "blur" },
  318. ],
  319. startTime: [
  320. { required: true, message: "请填写开始时间", trigger: "blur" },
  321. ],
  322. loadPort: [
  323. { required: true, message: "请填写装货港", trigger: "blur" },
  324. ],
  325. dischargeProt: [
  326. { required: true, message: "请填写卸货港", trigger: "blur" },
  327. ],
  328. cargo: [{ required: true, message: "请填写货种", trigger: "blur" }],
  329. tons: [{ required: true, message: "请填写吨位", trigger: "blur" }],
  330. },
  331. });
  332. let voyageForm = reactive({
  333. voyageForm: {
  334. voyageName: "",
  335. cargoOwnerId: "",
  336. cargoOwnerName: "",
  337. startTime: "",
  338. endTime: "",
  339. loadPort: "",
  340. dischargeProt: "",
  341. cargo: "",
  342. tons: "",
  343. loadPortId: "",
  344. dischargeProtId: "",
  345. shipId: "",
  346. shipName: "",
  347. },
  348. });
  349. function clear(type) {
  350. setTimeout(() => {
  351. switch (type) {
  352. case "shipId": {
  353. let index = ref(-1);
  354. for (let i in shipsCache.value) {
  355. if (
  356. voyageForm.voyageForm.shipName == shipsCache.value[i].shipName
  357. ) {
  358. index.value = i;
  359. break;
  360. }
  361. }
  362. if (index.value != -1) {
  363. voyageForm.voyageForm.shipId =
  364. shipsCache.value[index.value].shipId;
  365. } else {
  366. let b = shipsCache.value.some((item) => {
  367. return (
  368. item.shipId == voyageForm.voyageForm.shipId &&
  369. item.shipName == voyageForm.voyageForm.shipName
  370. );
  371. });
  372. voyageForm.voyageForm["shipId"] = "";
  373. voyageForm.voyageForm["shipName"] = "";
  374. }
  375. break;
  376. }
  377. case "cargoOwnerId": {
  378. let index = ref(-1);
  379. for (let i in cargoOwnersCache.value) {
  380. if (
  381. voyageForm.voyageForm.cargoOwnerName ==
  382. cargoOwnersCache.value[i].userName
  383. ) {
  384. index.value = i;
  385. break;
  386. }
  387. }
  388. if (index.value != -1) {
  389. voyageForm.voyageForm.cargoOwnerId =
  390. cargoOwnersCache.value[index.value].userId;
  391. } else {
  392. let b = cargoOwnersCache.value.some((item) => {
  393. return (
  394. item.userId == voyageForm.voyageForm.cargoOwnerId &&
  395. item.userName == voyageForm.voyageForm.cargoOwnerName
  396. );
  397. });
  398. if (!b) {
  399. voyageForm.voyageForm["cargoOwnerId"] = "";
  400. voyageForm.voyageForm["cargoOwnerName"] = "";
  401. }
  402. }
  403. break;
  404. }
  405. case "loadPort": {
  406. let index = ref(-1);
  407. for (let i in colCache.value) {
  408. if (voyageForm.voyageForm.loadPort == colCache.value[i].value) {
  409. index.value = i;
  410. break;
  411. }
  412. }
  413. if (index.value != -1) {
  414. voyageForm.voyageForm.loadPortId =
  415. colCache.value[index.value].key;
  416. } else {
  417. let b = colCache.value.some((item) => {
  418. return (
  419. item.value == voyageForm.voyageForm.loadPort &&
  420. item.key == voyageForm.voyageForm.loadPortId
  421. );
  422. });
  423. if (!b) {
  424. voyageForm.voyageForm["loadPort"] = "";
  425. voyageForm.voyageForm["loadPortId"] = "";
  426. }
  427. }
  428. break;
  429. }
  430. case "dischargeProt": {
  431. let index = ref(-1);
  432. for (let i in colCache.value) {
  433. if (
  434. voyageForm.voyageForm.dischargeProt == colCache.value[i].value
  435. ) {
  436. index.value = i;
  437. break;
  438. }
  439. }
  440. if (index.value != -1) {
  441. voyageForm.voyageForm.dischargeProtId =
  442. colCache.value[index.value].key;
  443. } else {
  444. let b = colCache.value.some((item) => {
  445. return (
  446. item.value == voyageForm.voyageForm.dischargeProt &&
  447. item.key == voyageForm.voyageForm.dischargeProtId
  448. );
  449. });
  450. if (!b) {
  451. voyageForm.voyageForm["dischargeProt"] = "";
  452. voyageForm.voyageForm["dischargeProtId"] = "";
  453. }
  454. }
  455. break;
  456. }
  457. }
  458. }, 200);
  459. }
  460. let addVoyageForm = ref(null);
  461. async function addVoyage() {
  462. addVoyageForm.value.validate(async (valid) => {
  463. if (valid) {
  464. console.log("提交", voyageForm.voyageForm);
  465. let res = await api.addVoyage({
  466. ...voyageForm.voyageForm,
  467. });
  468. if (res.data.status == 0) {
  469. ElNotification({
  470. title: res.data.msg,
  471. type: "success",
  472. });
  473. resetAddVoyageForm();
  474. getVoyageList();
  475. } else {
  476. console.log(res);
  477. ElNotification({
  478. title: res.data.msg,
  479. type: "error",
  480. });
  481. }
  482. } else {
  483. console.log("未提交", voyageForm.voyageForm);
  484. }
  485. });
  486. }
  487. let shipsCache = ref([]);
  488. let colCache = ref([]);
  489. let cargoOwnersCache = ref([]);
  490. const searchShip = _.debounce(
  491. async (queryString, cb) => {
  492. if (!queryString) return;
  493. let res = await api.searchShip({
  494. term: queryString,
  495. });
  496. let ships = [];
  497. if (res.data.status == 0) {
  498. ships = res.data.result;
  499. for (let i of ships) {
  500. i.value = `${i.shipName}`;
  501. }
  502. shipsCache.value = ships;
  503. cb(ships);
  504. }
  505. },
  506. 1000,
  507. { leading: true }
  508. );
  509. const selectShip = (item) => {
  510. voyageForm.voyageForm.shipId = item.shipId;
  511. };
  512. const searchCargoOwner = _.debounce(
  513. async (queryString, cb) => {
  514. if (!queryString) return;
  515. let res = await api.searchUser({
  516. term: queryString,
  517. identity: 2,
  518. });
  519. let cargoOwners = [];
  520. if (res.data.status == 0) {
  521. cargoOwners = res.data.result;
  522. for (let i of cargoOwners) {
  523. i.value = `${i.userName}`;
  524. }
  525. cargoOwnersCache.value = cargoOwners;
  526. cb(cargoOwners);
  527. }
  528. },
  529. 1000,
  530. { leading: true }
  531. );
  532. const selectCargoOwner = (item) => {
  533. voyageForm.voyageForm.cargoOwnerId = item.userId;
  534. };
  535. const getCol = _.debounce(
  536. async (queryString, cb) => {
  537. if (!queryString) return;
  538. let res = await api.getCol({
  539. term: queryString,
  540. });
  541. if (res.data.status == 0) {
  542. colCache.value = [...colCache.value, ...res.data.result];
  543. colCache.value = _.uniqBy(colCache.value, "key");
  544. cb(res.data.result);
  545. }
  546. },
  547. 1000,
  548. { leading: true }
  549. );
  550. const selectLoadPort = (item) => {
  551. voyageForm.voyageForm.loadPortId = item.key;
  552. voyageForm.voyageForm.loadPort = item.value;
  553. };
  554. const selectDischargeProt = (item) => {
  555. voyageForm.voyageForm.dischargeProtId = item.key;
  556. voyageForm.voyageForm.dischargeProt = item.value;
  557. };
  558. function resetAddVoyageForm() {
  559. voyageAddDialogVisible.value = false;
  560. addVoyageForm.value.resetFields();
  561. }
  562. getVoyageList();
  563. onMounted(() => {});
  564. return {
  565. currentPage,
  566. term,
  567. tableData,
  568. total,
  569. status,
  570. changeVoyageType,
  571. getVoyageList,
  572. voyageDetail,
  573. pageChange,
  574. goToVoyageAdd,
  575. addVoyage,
  576. voyageAddDialogVisible,
  577. addVoyageForm,
  578. ...toRefs(rules),
  579. ...toRefs(voyageForm),
  580. searchShip,
  581. selectShip,
  582. searchCargoOwner,
  583. selectCargoOwner,
  584. resetAddVoyageForm,
  585. getCol,
  586. selectLoadPort,
  587. selectDischargeProt,
  588. clear,
  589. };
  590. },
  591. };
  592. </script>
  593. <style scoped>
  594. .search-btn {
  595. display: inline-block;
  596. width: 60px;
  597. height: 32px;
  598. background: #0094fe;
  599. border-radius: 2px;
  600. font-size: 14px;
  601. font-family: PingFangSC-Regular, PingFang SC;
  602. font-weight: 400;
  603. color: #ffffff;
  604. text-align: center;
  605. line-height: 32px;
  606. margin-left: 10px;
  607. cursor: pointer;
  608. }
  609. .cargo-owner-add {
  610. width: 80px;
  611. height: 32px;
  612. border-radius: 2px;
  613. border: 1px solid #0094fe;
  614. font-size: 14px;
  615. font-family: PingFangSC-Regular, PingFang SC;
  616. font-weight: 400;
  617. color: #0094fe;
  618. line-height: 32px;
  619. text-align: center;
  620. cursor: pointer;
  621. }
  622. :deep().el-dialog {
  623. width: 560px;
  624. padding: 20px 50px;
  625. border-radius: 6px;
  626. }
  627. :deep() .el-dialog__title {
  628. font-size: 18px;
  629. font-family: PingFangSC-Regular, PingFang SC;
  630. font-weight: 400;
  631. color: #0094fe;
  632. }
  633. .normal-label {
  634. font-size: 14px;
  635. font-family: PingFangSC-Regular, PingFang SC;
  636. font-weight: 400;
  637. color: #353a42;
  638. margin-right: 10px;
  639. }
  640. .show-input {
  641. width: 280px;
  642. height: 32px;
  643. background: #ffffff;
  644. border-radius: 2px;
  645. border: 1px solid #dee0e3;
  646. font-size: 14px;
  647. font-family: PingFangSC-Regular, PingFang SC;
  648. font-weight: 400;
  649. color: #333333;
  650. line-height: 32px;
  651. padding-left: 12px;
  652. margin-right: 40px;
  653. }
  654. .radio-btns {
  655. height: 38px;
  656. width: 70px;
  657. border: 1px solid #1486f9;
  658. line-height: 38px;
  659. text-align: center;
  660. font-size: 14px;
  661. font-family: PingFangSC-Regular, PingFang SC;
  662. font-weight: 400;
  663. color: #0094fe;
  664. cursor: pointer;
  665. }
  666. .left-radius {
  667. border-top-left-radius: 19px;
  668. border-bottom-left-radius: 19px;
  669. width: 80px;
  670. }
  671. .right-radius {
  672. border-top-right-radius: 19px;
  673. border-bottom-right-radius: 19px;
  674. width: 80px;
  675. }
  676. .currentbtn {
  677. background: #1486f9;
  678. color: #fff;
  679. }
  680. .search-btn {
  681. display: inline-block;
  682. width: 60px;
  683. height: 38px;
  684. background: #0094fe;
  685. border-radius: 2px;
  686. font-size: 14px;
  687. font-family: PingFangSC-Regular, PingFang SC;
  688. font-weight: 400;
  689. color: #ffffff;
  690. text-align: center;
  691. line-height: 38px;
  692. margin-left: 10px;
  693. cursor: pointer;
  694. }
  695. .voyage-add {
  696. width: 80px;
  697. height: 36px;
  698. border-radius: 2px;
  699. border: 1px solid #0094fe;
  700. font-size: 14px;
  701. font-family: PingFangSC-Regular, PingFang SC;
  702. font-weight: 400;
  703. color: #0094fe;
  704. line-height: 36px;
  705. text-align: center;
  706. cursor: pointer;
  707. }
  708. :deep() .el-dialog {
  709. width: 800px;
  710. }
  711. :deep() .el-form-item {
  712. margin-right: 22px;
  713. width: 300px;
  714. }
  715. :deep() .el-autocomplete {
  716. width: 220px;
  717. }
  718. </style>