Ver Fonte

Merge branch 'master' of http://git.huihenduo.com.cn:3000/wzh/JiangYunPhotosCargoOwner_WebApp into lydf

wzh há 3 anos atrás
pai
commit
656462fd9e

+ 1 - 1
src/App.vue

@@ -74,7 +74,7 @@ export default {
 .main-section {
   margin: 24px 0 0 24px;
   height: calc(100% - 76px);
-  overflow: scroll;
+  overflow-y: auto;
 }
 
 .line-container-p18 {

+ 14 - 0
src/apis/fetch.js

@@ -142,4 +142,18 @@ export default {
   getFYFIDownloadUrl(data) {
     return $http("/fydi/getLastest", data);
   },
+  // 获取提货单列表
+  getLabList(data) {
+    return $http("/voyage/getLabList", data);
+  },
+
+  // 获取港口天气列表
+  getPortWeatherList(data) {
+    return $http("/voyage/getPortWeatherList", data);
+  },
+
+  // 获取超期航次提醒
+  getLongDaysInPort(data) {
+    return $http("/voyage/cargo/longDaysInPort", data);
+  },
 };

+ 9 - 38
src/components/Header.vue

@@ -70,49 +70,20 @@ export default {
       store.commit("changeLogin", false);
       router.push({ path: "/login" });
     }
-    let vs = [
-      {
-        version: "1.2.13.0",
-        timer: "	2022/2/9",
-        remarks: [
-          "优化船舶轨迹测算算法",
-          "优化船舶预计到港时间测算算法",
-          "优化航次状态预测算法",
-        ],
-      },
-      {
-        version: "1.2.14.0",
-        timer: "	2022/2/11",
-        remarks: ["更新保险保单上传接口", "更新装卸单据展示方式"],
-      },
-      {
-        version: "1.3.0.0	",
-        timer: "2022/2/11",
-        remarks: [
-          "发布浙江物产货主版管理平台",
-          "发布货损数据采集接口",
-          "发布货损智能分析报告",
-        ],
-      },
-      {
-        version: "1.3.4.0	",
-        timer: "2022/2/28",
-        remarks: [
-          "发布汽车装货记录功能",
-          "发布单据识别算法功能",
-          "单据车牌数据、吨位数据拆分功能、生成表报",
-          "智能化数据自动匹配、自动修正算法",
-        ],
-      },
-    ];
     let timelineData = ref([]);
-    // timelineData.value = vs.reverse();
     onMounted(() => {
       cloudLogin();
     });
     async function getAbledVersions() {
-      let res = await v.where({ disabled: false, deleted: __.neq(true) }).get();
-      timelineData.value = res.data.reverse();
+      let res = await v
+        .aggregate()
+        .match({ deleted: __.neq(true) })
+        .sort({
+          createTime: -1,
+        })
+        .limit(10)
+        .end();
+      timelineData.value = res.data;
     }
 
     async function cloudLogin() {

+ 79 - 0
src/components/RemoteSearch.vue

@@ -0,0 +1,79 @@
+<template>
+  <el-autocomplete
+    v-model="value"
+    :fetch-suggestions="getSelectList"
+    :placeholder="placeholder"
+    @select="selectItem"
+    :size="size"
+    clearable
+    @clear="clear"
+    @input="handleInput"
+    style="width: 240px"
+  />
+</template>
+
+<script>
+import { onMounted, ref } from "vue";
+import api from "../apis/fetch";
+import _ from "lodash";
+export default {
+  props: {
+    placeholder: {
+      type: String,
+      default: "请填写",
+    },
+    size: {
+      type: String,
+      default: "small",
+    },
+    clearable: {
+      type: Boolean,
+      default: true,
+    },
+    api: String,
+    params: Object,
+    value: String,
+  },
+  emits: ["input", "selectItem"],
+  setup(props, { emit }) {
+    let selectStr = ref("");
+    const getSelectList = _.debounce(
+      async (queryString, cb) => {
+        if (!queryString) return;
+        let res = await api[props.api]({
+          ...props.params,
+          term: queryString,
+        });
+        if (res.data.status == 0) {
+          cb(res.data.result);
+        }
+      },
+      1000,
+      { leading: true }
+    );
+
+    const selectItem = (item) => {
+      emit("selectItem", item);
+    };
+
+    function clear() {
+      selectStr.value = "";
+    }
+
+    function handleInput(e) {
+      emit("input", e);
+    }
+
+    onMounted(() => {});
+    return {
+      selectStr,
+      getSelectList,
+      clear,
+      selectItem,
+      handleInput,
+    };
+  },
+};
+</script>
+
+<style></style>

+ 15 - 2
src/main.js

@@ -1,6 +1,7 @@
 import { createApp } from "vue";
 import ElementPlus from "element-plus";
 import "element-plus/dist/index.css";
+import zhCn from "element-plus/es/locale/lang/zh-cn";
 import App from "./App.vue";
 import router from "./router";
 import store from "./store";
@@ -8,8 +9,10 @@ import md5 from "md5";
 import "./styles/index.css";
 import Uploader from "./components/Uploader.vue";
 import Certs from "./components/Certs.vue";
+import RemoteSearch from "./components/RemoteSearch.vue";
 
 const app = createApp(App);
+app.component("RemoteSearch", RemoteSearch);
 app.component("Certs", Certs);
 app.component("Uploader", Uploader);
 
@@ -35,7 +38,11 @@ router.beforeEach(async (to, from, next) => {
         next();
       }
     } else {
-      next();
+      if (to.path == "/") {
+        next("/voyage/voyageList");
+      } else {
+        next();
+      }
     }
   } else {
     store.commit("changeLogin", false);
@@ -62,4 +69,10 @@ app.directive("auth", {
   },
 });
 
-app.use(router).use(ElementPlus).use(store).mount("#app");
+app
+  .use(router)
+  .use(ElementPlus, {
+    locale: zhCn,
+  })
+  .use(store)
+  .mount("#app");

+ 21 - 15
src/utils/downloadBlobFile.js

@@ -1,5 +1,5 @@
 import axios from "axios";
-function downloadBlobFile(url, data, name, type) {
+function downloadBlobFile(url, data, name, type, fileType) {
   return new Promise((resolve, reject) => {
     axios({
       method: type,
@@ -8,20 +8,26 @@ function downloadBlobFile(url, data, name, type) {
       data,
     })
       .then((res) => {
-        let blob = new Blob([res.data], {
-          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8",
-        });
-        let downloadElement = document.createElement("a");
-        let href = window.URL.createObjectURL(blob); // 创建下载的链接
-        downloadElement.href = href;
-        downloadElement.download = name; // 下载后文件名
-        document.body.appendChild(downloadElement);
-        downloadElement.click(); // 点击下载
-        document.body.removeChild(downloadElement); // 下载完成移除元素
-        window.URL.revokeObjectURL(href); // 释放掉blob对象
-        resolve({
-          status: 0,
-        });
+        if (res.data.size) {
+          let blob = new Blob([res.data], {
+            type: fileType,
+          });
+          let downloadElement = document.createElement("a");
+          let href = window.URL.createObjectURL(blob); // 创建下载的链接
+          downloadElement.href = href;
+          downloadElement.download = name; // 下载后文件名
+          document.body.appendChild(downloadElement);
+          downloadElement.click(); // 点击下载
+          document.body.removeChild(downloadElement); // 下载完成移除元素
+          window.URL.revokeObjectURL(href); // 释放掉blob对象
+          resolve({
+            status: 0,
+          });
+        } else {
+          resolve({
+            status: -1,
+          });
+        }
       })
       .catch((e) => {
         reject({

+ 8 - 2
src/utils/utils.js

@@ -1,6 +1,12 @@
-function subTimeStr(str) {
+function subTimeStr(str, i) {
   if (!str || typeof str != "string") return;
-  let index = str.indexOf(" ");
+  let index;
+  if (i) {
+    index = i;
+  } else {
+    index = str.indexOf(" ");
+  }
+
   return str.substring(0, index);
 }
 

+ 1 - 0
src/views/accountManage/subAccountList.vue

@@ -8,6 +8,7 @@
           v-model="term"
           clearable
           style="height: 32px; width: 330px; line-height: 32px"
+          @keyup.enter.native="getSubAccountList"
         ></el-input>
         <div class="seach-btn" @click="getSubAccountList">查询</div>
       </div>

+ 1 - 0
src/views/agencyManage/agencyCompanyList.vue

@@ -8,6 +8,7 @@
           v-model="term"
           clearable
           style="height: 32px; width: 330px; line-height: 32px"
+          @keyup.enter.native="getAgencyList"
         ></el-input>
         <div class="seach-btn" @click="getAgencyList">查询</div>
       </div>

+ 1 - 0
src/views/authManage/departmentList.vue

@@ -8,6 +8,7 @@
           v-model="term"
           clearable
           style="height: 32px; width: 330px; line-height: 32px"
+          @keyup.enter.native="getDepartmentList"
         ></el-input>
         <div class="seach-btn" @click="getDepartmentList">查询</div>
       </div>

+ 1 - 0
src/views/authManage/roleList.vue

@@ -8,6 +8,7 @@
           v-model="term"
           clearable
           style="height: 32px; width: 330px; line-height: 32px"
+          @keyup.enter.native="getRoleList"
         ></el-input>
         <div class="seach-btn" @click="getRoleList">查询</div>
       </div>

+ 1 - 0
src/views/cargoManage/cargoList.vue

@@ -8,6 +8,7 @@
           v-model="term"
           clearable
           style="height: 32px; width: 330px; line-height: 32px"
+          @keyup.enter.native="getCargoList"
         ></el-input>
         <div class="seach-btn" @click="getCargoList">查询</div>
       </div>

+ 456 - 120
src/views/voyage/voyageDetail.vue

@@ -10,7 +10,20 @@
   </div>
 
   <div class="container-title df aic jcsb">
-    <div>航次信息</div>
+    <div class="df aic">
+      <div class="mr30">航次信息</div>
+      <el-tooltip
+        v-if="blockchainInfo"
+        class="box-item"
+        effect="light"
+        :content="blockchainInfo.hash"
+        placement="top"
+      >
+        <div class="pointer" style="font-size: 14px; font-weight: normal">
+          汇很多科技区块链认证
+        </div>
+      </el-tooltip>
+    </div>
     <el-button
       v-auth="'DOWNLOADSHIPTRACK'"
       style="width: 220px; margin-right: 20px"
@@ -121,11 +134,13 @@
             disabled
           ></el-input>
         </div>
-        <div class="info-line">
-          <div class="info-line-title">卸货港</div>
+      </div>
+      <div class="line">
+        <div class="info-line" v-for="(item, index) in voyage.voyageDetails">
+          <div class="info-line-title">卸货港{{ " # " + (index + 1) }}</div>
           <el-input
             class="info-line-text"
-            v-model="voyage.dischargeProt"
+            v-model="item.portName"
             disabled
           ></el-input>
         </div>
@@ -153,7 +168,7 @@
           <el-input
             style="width: 80px !important"
             class="info-line-text"
-            v-model="voyage.Pieces"
+            v-model="voyage.pieces"
             disabled
           ></el-input>
           <span class="unit">件</span>
@@ -178,7 +193,7 @@
               class="info-line-text"
               v-model="voyage.arrivalLoadPortTime"
               type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
+              format="YYYY/MM/DD HH:mm"
               value-format="YYYY/MM/DD HH:mm:ss"
               placeholder="到达装货港时间"
               :disabled="disabledStatus"
@@ -211,7 +226,7 @@
               class="info-line-text"
               v-model="voyage.loadStartTime"
               type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
+              format="YYYY/MM/DD HH:mm"
               value-format="YYYY/MM/DD HH:mm:ss"
               placeholder="装货开始时间"
               :disabled="disabledStatus"
@@ -223,112 +238,134 @@
               class="info-line-text"
               v-model="voyage.loadEndTime"
               type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
+              format="YYYY/MM/DD HH:mm"
               value-format="YYYY/MM/DD HH:mm:ss"
               placeholder="装货开始时间"
               :disabled="disabledStatus"
             ></el-date-picker>
           </div>
         </div>
-        <div class="line">
-          <div class="info-line">
-            <div class="info-line-title">开航时间</div>
-            <el-date-picker
-              class="info-line-text"
-              v-model="voyage.setSailTime"
-              type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
-              value-format="YYYY/MM/DD HH:mm:ss"
-              placeholder="开航时间"
-              :disabled="disabledStatus"
-            ></el-date-picker>
-          </div>
-          <div class="info-line">
-            <div class="info-line-title">预计到港时间</div>
-            <el-date-picker
-              class="info-line-text"
-              v-model="voyage.expectedArrivalTime"
-              type="datetime"
-              format="YYYY/MM/DD"
-              value-format="YYYY/MM/DD HH:mm:ss"
-              placeholder="预计到港时间"
-              :disabled="disabledStatus"
-            ></el-date-picker>
-          </div>
-        </div>
-        <div class="line">
-          <div class="info-line">
-            <div class="info-line-title">实际到港时间</div>
-            <el-date-picker
-              class="info-line-text"
-              v-model="voyage.actualArrivalTime"
-              type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
-              value-format="YYYY/MM/DD HH:mm:ss"
-              placeholder="实际到港时间"
-              :disabled="disabledStatus"
-            ></el-date-picker>
-          </div>
-          <!-- <div class="info-line">
+        <el-tabs
+          v-model="currentPortId"
+          type="border-card"
+          class="demo-tabs mb20"
+        >
+          <el-tab-pane
+            v-for="(item, index) in voyage.voyageDetails"
+            :label="item.portName + ' # ' + (index + 1)"
+            :name="item.portId + ''"
+          >
+            <div class="line">
+              <div class="info-line">
+                <div class="info-line-title">开航时间</div>
+                <el-date-picker
+                  class="info-line-text"
+                  v-model="item.setSailTime"
+                  type="datetime"
+                  @change="calExpectedArrivalTime"
+                  format="YYYY/MM/DD HH:mm"
+                  value-format="YYYY/MM/DD HH:mm:ss"
+                  placeholder="开航时间"
+                  :disabled="disabledStatus"
+                ></el-date-picker>
+              </div>
+              <div class="info-line">
+                <div class="info-line-title">预计到港时间</div>
+                <el-date-picker
+                  class="info-line-text"
+                  v-model="item.expectedArrivalTime"
+                  type="datetime"
+                  format="YYYY/MM/DD"
+                  value-format="YYYY/MM/DD HH:mm:ss"
+                  placeholder="预计到港时间"
+                  :disabled="disabledStatus"
+                ></el-date-picker>
+              </div>
+            </div>
+            <div class="line">
+              <div class="info-line">
+                <div class="info-line-title">实际到港时间</div>
+                <el-date-picker
+                  class="info-line-text"
+                  v-model="item.actualArrivalTime"
+                  type="datetime"
+                  format="YYYY/MM/DD HH:mm"
+                  value-format="YYYY/MM/DD HH:mm:ss"
+                  placeholder="实际到港时间"
+                  :disabled="disabledStatus"
+                ></el-date-picker>
+              </div>
+              <!-- <div class="info-line">
         <div class="info-line-title">抵达目的地港时间</div>
         <el-date-picker
           class="info-line-text"
           v-model="voyage.arrivalPortTime"
           type="datetime"
-          format="YYYY/MM/DD HH:mm:ss"
+          format="YYYY/MM/DD HH:mm"
           value-format="YYYY/MM/DD HH:mm:ss"
           placeholder="抵达目的地港时间"
           :disabled="disabledStatus"
         ></el-date-picker>
       </div> -->
-        </div>
-        <div class="line">
-          <div class="info-line">
-            <div class="info-line-title">卸货开始时间</div>
-            <el-date-picker
-              class="info-line-text"
-              v-model="voyage.dischargeStartTime"
-              type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
-              value-format="YYYY/MM/DD HH:mm:ss"
-              placeholder="卸货开始时间"
-              :disabled="disabledStatus"
-            ></el-date-picker>
-          </div>
-          <div class="info-line">
-            <div class="info-line-title">卸货结束时间</div>
-            <el-date-picker
-              class="info-line-text"
-              v-model="voyage.dischargeEndTime"
-              type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
-              value-format="YYYY/MM/DD HH:mm:ss"
-              placeholder="卸货结束时间"
-              :disabled="disabledStatus"
-            ></el-date-picker>
-          </div>
-        </div>
-        <div class="line">
-          <div class="info-line">
-            <div class="info-line-title">实际卸货量</div>
-            <el-input
-              style="width: 100px !important"
-              class="info-line-text"
-              placeholder="实际卸货吨位"
-              v-model="voyage.actualDischargeTons"
-              :disabled="disabledStatus"
-            ></el-input>
-            <span class="unit">吨</span>
-            <el-input
-              style="width: 80px !important"
-              class="info-line-text"
-              placeholder="实际卸货件数"
-              v-model="voyage.actualDischargePieces"
-              :disabled="disabledStatus"
-            ></el-input>
-            <span class="unit">件</span>
-          </div>
-        </div>
+            </div>
+            <div class="line">
+              <div class="info-line">
+                <div class="info-line-title">卸货开始时间</div>
+                <el-date-picker
+                  class="info-line-text"
+                  v-model="item.dischargeStartTime"
+                  type="datetime"
+                  format="YYYY/MM/DD HH:mm"
+                  value-format="YYYY/MM/DD HH:mm:ss"
+                  placeholder="卸货开始时间"
+                  :disabled="disabledStatus"
+                ></el-date-picker>
+              </div>
+              <div class="info-line">
+                <div class="info-line-title">卸货结束时间</div>
+                <el-date-picker
+                  class="info-line-text"
+                  v-model="item.dischargeEndTime"
+                  type="datetime"
+                  format="YYYY/MM/DD HH:mm"
+                  value-format="YYYY/MM/DD HH:mm:ss"
+                  placeholder="卸货结束时间"
+                  :disabled="disabledStatus"
+                ></el-date-picker>
+              </div>
+            </div>
+            <div class="line">
+              <div class="info-line">
+                <div class="info-line-title">实际卸货量</div>
+                <el-input
+                  style="width: 100px !important"
+                  class="info-line-text"
+                  placeholder="实际卸货吨位"
+                  v-model="item.actualDischargeTons"
+                  :disabled="disabledStatus"
+                ></el-input>
+                <span class="unit">吨</span>
+                <el-input
+                  style="width: 80px !important"
+                  class="info-line-text"
+                  placeholder="实际卸货件数"
+                  v-model="item.actualDischargePieces"
+                  :disabled="disabledStatus"
+                ></el-input>
+                <span class="unit">件</span>
+              </div>
+              <!-- <div class="info-line">
+        <div class="info-line-title">是否购买保险</div>
+        <el-checkbox
+          v-model="voyage.hasInsurance"
+          :checked="voyage.hasInsurance == 1"
+          :disabled="disabledStatus"
+          label="购买保险"
+        ></el-checkbox>
+      </div> -->
+            </div>
+          </el-tab-pane>
+        </el-tabs>
         <div class="line">
           <div class="info-line">
             <div class="info-line-title">备注</div>
@@ -342,7 +379,169 @@
           </div>
         </div>
       </div>
-      <hr class="hr m30-0" />
+    </div>
+  </div>
+  <div class="container-title">卸货信息</div>
+  <div class="line-container-p24">
+    <el-tabs
+      v-model="currentDiscPortId"
+      type="card"
+      class="demo-tabs"
+      @tab-click="changeDiscPortTab"
+    >
+      <el-tab-pane
+        v-for="(item, index) in voyage.voyageDetails"
+        :label="item.portName + ' # ' + (index + 1)"
+        :name="item.portId + ''"
+      ></el-tab-pane>
+    </el-tabs>
+    <div>
+      <div class="container-second-title df aic jcsb">
+        <div>天气信息</div>
+      </div>
+      <el-table style="width: 1200px" :data="weatherTableData" stripe>
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="weather"
+          label="天气"
+          min-width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="temperature"
+          label="温度"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="winddirection"
+          label="风向"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="windpower"
+          label="风力"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="reporttime"
+          label="记录时间"
+          min-width="100"
+          align="center"
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.reporttime, 16) }}
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 1200px; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="weatherTotal"
+          @current-change="weatherPageChange"
+        ></el-pagination>
+      </div>
+      <div class="hr m30-0"></div>
+    </div>
+    <div v-auth="'LABDETAIL'">
+      <div class="container-second-title df aic jcsb">
+        <div>提单信息</div>
+      </div>
+      <el-table :data="labTableData" stripe style="width: 1200px">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="billingDate"
+          label="开单日期"
+          min-width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="billingNum"
+          label="开单数量"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+
+        <el-table-column label="单据" min-width="150" align="center">
+          <template v-slot="scope">
+            <el-button
+              @click="showLab(scope.row, scope.$index, '查看提单')"
+              type="primary"
+              size="small"
+            >
+              查看
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="text-align: right; margin-top: 43px; width: 1200px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="labTotal"
+          @current-change="labPageChange"
+        ></el-pagination>
+      </div>
+      <el-dialog
+        v-model="isAddLabVisable"
+        :title="labModalType"
+        width="780px"
+        center
+        @close="cancelUploadLab"
+      >
+        <el-form
+          :model="labForm"
+          inline
+          style="margin-bottom: 20px"
+          label-width="100px"
+        >
+          <el-form-item label="开单日期">
+            <el-date-picker
+              class="info-line-text"
+              v-model="labForm.billingDate"
+              type="date"
+              format="YYYY/MM/DD"
+              value-format="YYYY/MM/DD"
+              placeholder="开单日期"
+              disabled
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item label="开单数量">
+            <el-input
+              style="width: 240px"
+              v-model="labForm.billingNum"
+              placeholder="开单数量"
+              disabled
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="提单">
+            <Uploader
+              disabled
+              :actionUrl="store.state.wayBillUrl"
+              :uploaderId="'labLoad'"
+              :params="labParams"
+              @onSendFileList="getLabBillList"
+              :fileList="labBillList"
+              uploadText="上传提单"
+              :limit="1"
+            ></Uploader>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+      <div class="hr m30-0"></div>
     </div>
     <div v-auth="'SHIPDISCHARGE'">
       <div class="container-second-title df aic jcsb">
@@ -356,7 +555,7 @@
           >下载卸货信息</el-button
         >
       </div>
-      <el-table :data="dischargeList" stripe style="width: 800px">
+      <el-table :data="dischargeList" stripe style="width: 1200px">
         <el-table-column
           type="index"
           label="序号"
@@ -368,7 +567,11 @@
           label="卸货时间"
           min-width="120"
           align="center"
-        ></el-table-column>
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.dischargeTime, 16) }}
+          </template>
+        </el-table-column>
         <el-table-column
           prop="dischargeTons"
           label="卸货吨位"
@@ -394,7 +597,7 @@
           </template>
         </el-table-column>
       </el-table>
-      <div style="width: 100%; text-align: right; margin-top: 43px">
+      <div style="width: 1200px; text-align: right; margin-top: 43px">
         <el-pagination
           background
           layout="prev, pager, next"
@@ -432,7 +635,11 @@
           label="称重时间"
           min-width="120"
           align="center"
-        ></el-table-column>
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.weighTime, 16) }}
+          </template>
+        </el-table-column>
         <el-table-column
           prop="carNum"
           label="车号"
@@ -532,7 +739,7 @@
             class="info-line-text"
             v-model="truckRecordForm.weighTime"
             type="datetime"
-            format="YYYY/MM/DD HH:mm:ss"
+            format="YYYY/MM/DD HH:mm"
             value-format="YYYY/MM/DD HH:mm:ss"
             placeholder="称重时间"
           ></el-date-picker>
@@ -623,7 +830,18 @@
     </el-dialog>
   </div>
   <div v-auth="'BILLINFO'">
-    <div class="container-title">单据信息</div>
+    <div class="container-title df aic jcsb">
+      单据信息
+      <el-button
+        style="margin-right: 30px"
+        size="medium"
+        type="primary"
+        v-auth="'BILLDOWNLOAD'"
+        @click="downloadBillZip"
+        :loading="billZipLoading"
+        >下载单据</el-button
+      >
+    </div>
     <div class="line-container-p24" style="padding-left: 60px">
       <div class="df aic">
         <div class="info-line-title">保险单:</div>
@@ -666,8 +884,11 @@
                 {{ item.shipName }} 拍摄于
                 <br />
                 {{ item.createTime }}
+                <br />
+                天气 : {{ item.weather.weather }} - 气温 :
+                {{ item.weather.temperature }}℃
               </div>
-              <div class="medias-box" style="position: relative">
+              <div class="medias-box mb10" style="position: relative">
                 <el-image
                   v-if="item.mediaType == 1"
                   style="width: 100%; height: 100%"
@@ -744,7 +965,7 @@
               class="info-line-text"
               v-model="updateForm.dischargeTime"
               type="datetime"
-              format="YYYY/MM/DD HH:mm:ss"
+              format="YYYY/MM/DD HH:mm"
               value-format="YYYY/MM/DD HH:mm:ss"
               placeholder="卸货时间"
               disabled
@@ -782,6 +1003,7 @@ import store from "../../store";
 import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
 import downloadBlobFile from "../../utils/downloadBlobFile";
 import url from "../../apis/config";
+import { subTimeStr } from "utils/utils";
 
 const route = useRoute();
 let map = ref();
@@ -802,7 +1024,7 @@ function showCerts() {
   });
 }
 
-async function getVoyageDetail(type) {
+async function getVoyageDetail(isInit) {
   policyList.value = [];
   voyageBill.value = [];
   previewSrcList.value = [];
@@ -811,15 +1033,16 @@ async function getVoyageDetail(type) {
     voyageId: route.query.id,
   });
   if (res.data.status == 0) {
-    if (type) {
-      ElNotification({
-        type: "success",
-        title: res.data.msg,
-      });
-    }
-
+    ElNotification({
+      type: "success",
+      title: res.data.msg,
+    });
+    blockchainInfo.value = res.data.result.blockChain;
     coordinates.value = res.data.result.coordinates;
     voyage.value = res.data.result.voyage;
+    voyage.value.startTime = voyage.value.startTime.substring(0, 16);
+    currentPortId.value = voyage.value.voyageDetails[0].portId + "";
+    currentDiscPortId.value = voyage.value.voyageDetails[0].portId + "";
     medias.value = res.data.result.medias;
     shipAudits.value = res.data.result.shipAudits;
 
@@ -838,7 +1061,13 @@ async function getVoyageDetail(type) {
     for (let i of medias.value) {
       previewSrcList.value.push(i.downloadUrl);
     }
-    initMap();
+    if (isInit) {
+      getDischargeList();
+      getTruckLoadRecord();
+      getLabList();
+      getPortWeatherList();
+      initMap();
+    }
   } else {
     console.log(res);
     ElNotification({
@@ -891,6 +1120,7 @@ let formInline = ref({});
 async function getDischargeList(type) {
   let res = await api.getDischargeList({
     voyageId: route.query.id,
+    portId: currentDiscPortId.value,
     currentPage: dischargeCurrentPage.value,
     size: 10,
   });
@@ -1198,7 +1428,8 @@ async function downloadExcel() {
     `${url.baseurl}/voyage/exportExcel`,
     { voyageId: route.query.id },
     "船舶跟踪表",
-    "post"
+    "post",
+    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
   );
   if (res) {
     isLoadingExcel.value = false;
@@ -1211,7 +1442,8 @@ async function exportDischargeExcel() {
     `${url.baseurl}/voyage/exportDischargeExcel`,
     { voyageId: route.query.id },
     "卸货记录表",
-    "post"
+    "post",
+    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
   );
   if (res) {
     isDischargeLoadingExcel.value = false;
@@ -1227,6 +1459,7 @@ let truckTotal = ref(0);
 async function getTruckLoadRecord() {
   let res = await api.getTruckLoadRecord({
     voyageId: route.query.id,
+    portId: currentDiscPortId.value,
     currentPage: truckCurrentPage.value,
     size: 10,
   });
@@ -1254,11 +1487,101 @@ function showTruckRecord(item, index, text) {
 
 let truckLoadParams = ref({});
 function getTruckLoadBillList() {}
+let blockchainInfo = ref("");
 
-onMounted(() => {
-  getVoyageDetail(1);
-  getDischargeList(1);
+let billZipLoading = ref(false);
+async function downloadBillZip() {
+  billZipLoading.value = true;
+  let res = await downloadBlobFile(
+    `${url.baseurl}/voyage/exportBillZip`,
+    { voyageId: route.query.id },
+    "单据文件",
+    "post",
+    "application/zip"
+  );
+
+  billZipLoading.value = false;
+}
+
+let weatherTableData = ref([]);
+let weatherCurrentPage = ref(1);
+let weatherTotal = ref(0);
+async function getPortWeatherList() {
+  let res = await api.getPortWeatherList({
+    voyageId: route.query.id,
+    portId: currentDiscPortId.value,
+    size: 10,
+    currentPage: weatherCurrentPage.value,
+  });
+  weatherTableData.value = res.data.result;
+  weatherTotal.value = res.data.total;
+}
+
+function weatherPageChange(e) {
+  weatherCurrentPage.value = e;
+  getPortWeatherList();
+}
+
+let labTableData = ref([]);
+let labTotal = ref(0);
+let labCurrentPage = ref(1);
+
+async function getLabList() {
+  let res = await api.getLabList({
+    voyageId: route.query.id,
+    portId: currentDiscPortId.value,
+    currentPage: labCurrentPage.value,
+    size: 10,
+  });
+  labTableData.value = res.data.result;
+  labTotal.value = res.data.total;
+}
+function labPageChange(e) {
+  labCurrentPage.value = e;
+  getLabList();
+}
+let isAddLabVisable = ref(false);
+let labBillList = ref([]);
+let labForm = ref({});
+function showLab(item) {
+  isAddLabVisable.value = true;
+  if (item.file) {
+    labBillList.value[0] = {
+      ...item.file,
+      url: item.file.viewUrl,
+    };
+  }
+
+  labForm.value = { ...item };
+}
+
+let currentPortId = ref("");
+let currentDiscPortId = ref("");
+let currentDiscPortIndex = ref(0);
+function changeDiscPortTab(e) {
+  currentDiscPortIndex.value = e.index;
+  currentDiscPortId.value =
+    voyage.value.voyageDetails[currentDiscPortIndex.value].portId + "";
+  weatherTableData.value = [];
+  weatherTotal.value = 0;
+  labTableData.value = [];
+  labTotal.value = 0;
+  dischargeList.value = [];
+  total.value = 0;
+  truckTableData.value = [];
+  truckTotal.value = 0;
+  labCurrentPage.value = 1;
+  dischargeCurrentPage.value = 1;
+  weatherCurrentPage.value = 1;
+  truckCurrentPage.value = 1;
+  getDischargeList();
   getTruckLoadRecord();
+  getLabList();
+  getPortWeatherList();
+}
+
+onMounted(() => {
+  getVoyageDetail(true);
 });
 </script>
 <style scoped>
@@ -1440,4 +1763,17 @@ onMounted(() => {
   line-height: 100%;
   margin: 0 10px;
 }
+
+.line {
+  flex-wrap: wrap;
+  margin: 0 20px;
+}
+
+.info-line {
+  margin-bottom: 20px;
+}
+
+.ml50 {
+  margin-left: 140px;
+}
 </style>

+ 205 - 36
src/views/voyage/voyageList.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="line-container-p24">
-    <div style="display: flex; justify-content: space-between">
+    <div class="df jcsb aic">
       <div class="df aic">
         <div
           @click="changeVoyageType(0)"
@@ -58,21 +58,68 @@
           prefix-icon="el-icon-search"
           v-model="term"
           clearable
-          style="width: 330px"
+          style="width: 240px"
+          @keyup.enter.native="getVoyageList()"
         ></el-input>
         <div class="search-btn" @click="getVoyageList()">查询</div>
       </div>
       <!-- <div class="cargo-owner-add" @click="voyageAddDialogVisible = true">
         添加航次
       </div> -->
-      <el-button
-        v-auth="'DOWNLOADFYDI'"
-        type="primary"
-        size="medium"
-        @click="downloadFYDI"
-        >下载FYDI指数</el-button
-      >
+      <div>
+        <el-button
+          type="primary"
+          size="small"
+          v-auth="'DOWNLOADVOYAGELIST'"
+          @click="showExportModal('航次列表')"
+          >导出航次列表</el-button
+        >
+        <el-button
+          type="primary"
+          size="small"
+          v-auth="'MULTDOWNLOADSHIPTRACK'"
+          @click="showExportModal('航次跟踪')"
+          >导出航次跟踪</el-button
+        >
+        <el-button
+          type="primary"
+          size="small"
+          @click="showExportModal('卸货记录')"
+          v-auth="'MULTDOWNLOADDISCHARGE'"
+          >导出卸货记录</el-button
+        >
+        <el-button
+          v-auth="'DOWNLOADFYDI'"
+          type="primary"
+          size="small"
+          @click="downloadFYDI"
+          >下载FYDI指数</el-button
+        >
+      </div>
     </div>
+    <el-dialog
+      v-model="exportModalVisable"
+      :title="exportModalTitle"
+      :close-on-click-modal="false"
+      width="200px"
+    >
+      <div class="df aic jcsb">
+        <div v-if="exportModalTitle != '航次列表'" class="df aic">
+          <div class="mr20">请选择月份:</div>
+          <el-date-picker
+            v-model="currentMonth"
+            type="month"
+            placeholder="请选择年月"
+            value-format="YYYYMM"
+            :disabled="isLoadingZip"
+          />
+        </div>
+        <div></div>
+        <el-button type="primary" @click="exportZip" :loading="isLoadingZip"
+          >导出{{ exportModalTitle }}</el-button
+        >
+      </div>
+    </el-dialog>
     <el-dialog v-model="voyageAddDialogVisible" title="添加航次">
       <el-form
         :rules="rules"
@@ -156,13 +203,18 @@
         </span>
       </template>
     </el-dialog>
-    <el-table :data="tableData" stripe style="width: 100%; margin-top: 24px">
-      <el-table-column
+    <el-table
+      :data="tableData"
+      stripe
+      style="width: 100%; margin-top: 24px"
+      :row-style="rowStyle"
+    >
+      <!-- <el-table-column
         type="index"
         label="序号"
         min-width="80"
         align="center"
-      ></el-table-column>
+      ></el-table-column> -->
       <el-table-column
         prop="voyageName"
         label="航次名称"
@@ -170,23 +222,34 @@
         align="center"
       ></el-table-column>
       <el-table-column
-        prop="loadDiscPort"
-        label="装货港-卸货港"
-        min-width="200"
+        prop="loadPort"
+        label="装货港"
+        min-width="90"
         align="center"
       ></el-table-column>
       <el-table-column
+        prop="dischargePort"
+        label="卸货港"
+        min-width="80"
+        align="center"
+      ></el-table-column>
+      <!-- <el-table-column
         prop="setSailTime"
         label="开航时间"
         min-width="100"
         align="center"
-      ></el-table-column>
+      ></el-table-column> -->
       <el-table-column
         prop="expectedArrivalTime"
         label="预计到港时间"
-        min-width="100"
+        sortable
+        min-width="140"
         align="center"
-      ></el-table-column>
+      >
+        <template v-slot="scope">
+          {{ scope.row.arrived ? "已到港" : scope.row.expectedArrivalTime }}
+        </template>
+      </el-table-column>
       <el-table-column
         prop="abnormalStatus"
         label="航次状态"
@@ -198,31 +261,43 @@
         </template>
       </el-table-column>
       <el-table-column
-        prop="daysInPort"
+        prop="daysInPortStr"
         label="在港天数"
-        min-width="80"
+        sortable
+        min-width="100"
         align="center"
       ></el-table-column>
       <el-table-column
         prop="todayPhotoCount"
         label="今日照片"
-        min-width="80"
+        min-width="70"
         align="center"
       ></el-table-column>
       <el-table-column
         prop="cargo"
         label="货种"
+        min-width="70"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="actualLoadTons"
+        label="装载吨位"
         min-width="80"
         align="center"
       ></el-table-column>
-
       <el-table-column
-        prop="tons"
-        label="吨位(吨)"
+        prop="unloadedtons"
+        label="已卸货吨位"
         min-width="80"
         align="center"
       ></el-table-column>
       <el-table-column
+        prop="remainTons"
+        label="剩余吨位"
+        min-width="80"
+        align="center"
+      ></el-table-column>
+      <!-- <el-table-column
         prop="waybillStatus"
         sortable
         label="签单状态"
@@ -238,7 +313,7 @@
               : "已签单"
           }}
         </template>
-      </el-table-column>
+      </el-table-column> -->
       <!-- <el-table-column
         prop="transStatus"
         label="船舶状态"
@@ -247,15 +322,15 @@
       ></el-table-column> -->
       <el-table-column
         prop="hasInsurance"
-        label="保险状态"
-        min-width="100"
+        label="保险状态"
+        min-width="70"
         align="center"
       >
         <template v-slot="scope">
           {{ scope.row.hasInsurance == 0 ? "未购买" : "已购买" }}
         </template>
       </el-table-column>
-      <el-table-column
+      <!-- <el-table-column
         sortable
         prop="createTime"
         label="创建时间"
@@ -271,12 +346,13 @@
         label="备注"
         min-width="100"
         align="center"
-      ></el-table-column>
+      ></el-table-column> -->
       <el-table-column
         v-auth="'VOYAGEDETAIL'"
         label="操作"
         min-width="80"
         align="center"
+        fixed="right"
       >
         <template v-slot="scope">
           <el-button
@@ -308,6 +384,8 @@ import md5 from "md5";
 import api from "../../apis/fetch";
 import _ from "lodash";
 import { subTimeStr } from "../../utils/utils";
+import downloadBlobFile from "../../utils/downloadBlobFile";
+import url from "../../apis/config";
 
 let currentPage = ref(1);
 let term = ref("");
@@ -326,7 +404,6 @@ async function getVoyageList() {
     currentPage: currentPage.value,
     size: 10,
   });
-  term.value = "";
   if (res.data.status == 0) {
     tableData.value = res.data.result;
     total.value = res.data.total;
@@ -338,7 +415,6 @@ async function getVoyageList() {
   }
 }
 function changeVoyageType(s) {
-  term.value = "";
   currentPage.value = 1;
   status.value = s;
   getVoyageList();
@@ -621,16 +697,109 @@ function resetAddVoyageForm() {
 }
 
 let sortradio = ref(0);
-let isLoadingExcel = ref(false);
 async function downloadFYDI() {
   let res0 = await api.getFYFIDownloadUrl({
     loginAccountId: localStorage.loginAccountId,
   });
 
-  let url = res0.data.result;
-  let a = document.createElement("a");
-  a.setAttribute("href", url);
-  a.click();
+  if (res0.data.result == 1) {
+    ElNotification({
+      type: "info",
+      title: "更新中",
+    });
+  } else {
+    let url = res0.data.result;
+    let a = document.createElement("a");
+    a.setAttribute("href", url);
+    a.click();
+  }
+}
+
+let exportModalVisable = ref(false);
+let exportModalTitle = ref("");
+let currentMonth = ref("");
+
+function showExportModal(type) {
+  exportModalVisable.value = true;
+  exportModalTitle.value = type;
+}
+
+let isLoadingZip = ref(false);
+
+async function exportZip() {
+  if (!currentMonth.value && exportModalTitle.value != "航次列表") return;
+
+  isLoadingZip.value = true;
+  let path = "";
+  let type = "";
+  let postData = {
+    loginAccountId: localStorage.loginAccountId,
+  };
+  let title = "";
+  switch (exportModalTitle.value) {
+    case "航次列表": {
+      path = "/voyage/exportListExcel";
+      type =
+        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
+      title = `${exportModalTitle.value}`;
+      let arr = [];
+      for (let i of tableData.value) {
+        arr.push(i.id);
+      }
+      postData.voyageIds = arr.join(",");
+      break;
+    }
+    case "航次跟踪": {
+      path = "/voyage/exportMultExcel";
+      type = "application/zip";
+      postData.date = currentMonth.value;
+      title = `${exportModalTitle.value}${currentMonth.value}`;
+
+      break;
+    }
+    case "卸货记录": {
+      path = "/voyage/exportMultDischargeExcel";
+      type = "application/zip";
+      postData.date = currentMonth.value;
+      title = `${exportModalTitle.value}${currentMonth.value}`;
+      break;
+    }
+  }
+  try {
+    let res = await downloadBlobFile(
+      `${url.baseurl}${path}`,
+      postData,
+      title,
+      "post",
+      type
+    );
+    if (res.status == 0) {
+      ElNotification({
+        title: "导出成功!",
+        type: "success",
+      });
+    } else {
+      ElNotification({
+        title: "暂无数据",
+      });
+    }
+  } catch (error) {
+    ElNotification({
+      title: "暂无数据",
+    });
+  } finally {
+    isLoadingZip.value = false;
+    currentMonth.value = "";
+    exportModalVisable.value = false;
+  }
+}
+
+function rowStyle({ row }) {
+  let rowStyle = {};
+  if (row.daysInPort >= 30) {
+    rowStyle.color = "red";
+    return rowStyle;
+  }
 }
 
 onMounted(() => {

+ 12 - 0
vite.config.js

@@ -1,10 +1,22 @@
 import { defineConfig } from "vite";
 import vue from "@vitejs/plugin-vue";
 import viteCompression from "vite-plugin-compression";
+import path from "path";
 
 // https://vitejs.dev/config/
 export default defineConfig({
   plugins: [vue(), viteCompression()],
+  resolve: {
+    alias: {
+      "@": path.resolve(__dirname, "src"),
+      comps: path.resolve(__dirname, "src/components"),
+      apis: path.resolve(__dirname, "src/apis"),
+      router: path.resolve(__dirname, "src/router"),
+      store: path.resolve(__dirname, "src/store"),
+      views: path.resolve(__dirname, "src/views"),
+      utils: path.resolve(__dirname, "src/utils"),
+    },
+  },
   server: {
     port: 6183,
   },