Ver Fonte

同步 wuchan

wzh há 3 anos atrás
pai
commit
bdb450c7a2

+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
 {
-  "name": "jiangyunphotosagent",
+  "name": "jiangyun-cargo-owner",
   "version": "0.0.0",
   "version": "0.0.0",
   "scripts": {
   "scripts": {
     "dev": "vite --mode dev",
     "dev": "vite --mode dev",

+ 3 - 3
src/App.vue

@@ -10,7 +10,7 @@
         <div class="main-section"><router-view></router-view></div>
         <div class="main-section"><router-view></router-view></div>
       </div>
       </div>
     </div>
     </div>
-    <FooterVue></FooterVue>
+    <!-- <FooterVue></FooterVue> -->
   </div>
   </div>
   <router-view v-else></router-view>
   <router-view v-else></router-view>
 </template>
 </template>
@@ -48,7 +48,7 @@ export default {
 }
 }
 .main-app {
 .main-app {
   width: 100%;
   width: 100%;
-  height: calc(100% - 120px);
+  height: calc(100% - 60px);
   display: flex;
   display: flex;
 }
 }
 
 
@@ -74,7 +74,7 @@ export default {
 .main-section {
 .main-section {
   margin: 24px 0 0 24px;
   margin: 24px 0 0 24px;
   height: calc(100% - 76px);
   height: calc(100% - 76px);
-  overflow: scroll;
+  overflow-y: auto;
 }
 }
 
 
 .line-container-p18 {
 .line-container-p18 {

+ 3 - 3
src/apis/config.js

@@ -1,7 +1,7 @@
 import store from "../store/index";
 import store from "../store/index";
 import axios from "axios";
 import axios from "axios";
 
 
-let baseurl = store.state.baseurl;
+let baseurl = import.meta.env.VITE_BASEURL;
 const uploadUrl = `${baseurl}cos/upload`;
 const uploadUrl = `${baseurl}cos/upload`;
 
 
 axios.interceptors.response.use(
 axios.interceptors.response.use(
@@ -12,9 +12,9 @@ axios.interceptors.response.use(
     return Promise.reject(error);
     return Promise.reject(error);
   }
   }
 );
 );
-export const $http = function (method, url, data) {
+export const $http = function (url, data) {
   return axios({
   return axios({
-    method,
+    method: data ? "post" : "get",
     url: baseurl + url,
     url: baseurl + url,
     data,
     data,
     withCredentials: true,
     withCredentials: true,

+ 144 - 10
src/apis/fetch.js

@@ -2,49 +2,183 @@ import { $http } from "./config";
 export default {
 export default {
   // 货主登录
   // 货主登录
   staffLogin(data) {
   staffLogin(data) {
-    return $http("post", "user/cargo/login", data);
+    return $http("user/cargo/login", data);
   },
   },
   // 获取航次列表
   // 获取航次列表
   getVoyageList(data) {
   getVoyageList(data) {
-    return $http("post", "voyage/list", data);
+    return $http("voyage/list", data);
   },
   },
 
 
   // 获取航次详情
   // 获取航次详情
   getVoyageDetail(data) {
   getVoyageDetail(data) {
-    return $http("post", "/voyage/detail", data);
+    return $http("/voyage/detail", data);
   },
   },
 
 
   // 更新航次
   // 更新航次
   updateVoyage(data) {
   updateVoyage(data) {
-    return $http("post", "/voyage/backstage/update", data);
+    return $http("/voyage/backstage/update", data);
   },
   },
 
 
   // 完成航次
   // 完成航次
   finishVoyage(data) {
   finishVoyage(data) {
-    return $http("post", "/voyage/backstage/finish", data);
+    return $http("/voyage/backstage/finish", data);
   },
   },
 
 
   // 添加航次
   // 添加航次
   addVoyage(data) {
   addVoyage(data) {
-    return $http("post", "voyage/backstage/add", data);
+    return $http("voyage/backstage/add", data);
   },
   },
 
 
   // 获取媒体列表
   // 获取媒体列表
   getMediaList(data) {
   getMediaList(data) {
-    return $http("post", "/media/backstage/list", data);
+    return $http("/media/backstage/list", data);
   },
   },
   // 导出excel
   // 导出excel
   exportExcel(data) {
   exportExcel(data) {
-    return $http("post", "/voyage/exportExcel", data);
+    return $http("/voyage/exportExcel", data);
   },
   },
 
 
   // 获取卸货列表
   // 获取卸货列表
   getDischargeList(data) {
   getDischargeList(data) {
-    return $http("post", "/voyage/getDischargeList", data);
+    return $http("/voyage/getDischargeList", data);
   },
   },
 
 
   // 获取汽车装货记录
   // 获取汽车装货记录
   getTruckLoadRecord(data) {
   getTruckLoadRecord(data) {
-    return $http("post", "/voyage/getCarLoadRecordList", data);
+    return $http("/voyage/getCarLoadRecordList", data);
+  },
+
+  // 添加职位
+  addRole(data) {
+    return $http("/role/permission/addRole", data);
+  },
+
+  // 获取权限数据
+  getPermisiionData(data) {
+    return $http("/role/permission/getPermisiionData", data);
+  },
+
+  // 获取角色列表
+  getRoleList(data) {
+    return $http("/role/permission/list", data);
+  },
+
+  // 获取角色下拉列表
+  getRoleSelect(data) {
+    return $http("/role/permission/getRoleSelect", data);
+  },
+
+  // 修改角色信息
+  updateRole(data) {
+    return $http("/role/permission/updateRole", data);
+  },
+
+  // 添加货种
+  addCargo(data) {
+    return $http("/cargo/add", data);
+  },
+
+  // 货种列表
+  getCargoList(data) {
+    return $http("/cargo/list", data);
+  },
+
+  // 获取代理列表
+  getAgencyList(data) {
+    return $http("/user/cargo/proxy/list", data);
+  },
+
+  // 添加代理
+  addAgency(data) {
+    return $http("/user/cargo/add/proxy", data);
+  },
+
+  // 获取货主子账户列表
+  getSubAccountList(data) {
+    return $http("/user/cargo/account/list", data);
+  },
+
+  // 添加货主子账户
+  addSubAccount(data) {
+    return $http("/user/cargo/add/loginAccount", data);
+  },
+
+  // 修改货主子账户
+  updateSubAccount(data) {
+    return $http("/user/cargo/update/loginAccount", data);
+  },
+
+  // 获取角色详情
+  getRoleDetail(data) {
+    return $http("/role/permission/getRoleDetail", data);
+  },
+
+  // 获取登录人权限
+  getPermissionByUserId(data) {
+    return $http("/role/permission/getPermissionByUserId", data);
+  },
+
+  // 添加部门
+  addDepartment(data) {
+    return $http("/department/add", data);
+  },
+
+  // 获取部门列表
+  getDepartmentList(data) {
+    return $http("/department/list", data);
+  },
+
+  // 获取部门下拉
+  getDepartmentSelect(data) {
+    return $http("/department/select", data);
+  },
+
+  // 修改部门
+  updateDepartment(data) {
+    return $http("/department/update", data);
+  },
+
+  // 获取FYDI指数下载链接
+  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);
+  },
+
+  // 获取客户列表
+  getClientList(data) {
+    return $http("/client/account/list", data);
+  },
+
+  // 添加客户
+  addClient(data) {
+    return $http("/client/account/add", data);
+  },
+
+  // 修改客户
+  updateClient(data) {
+    return $http("/client/account/update", data);
+  },
+
+  // 获取港口列表
+  getCol(data) {
+    return $http("/port/backstage/getCol", data);
+  },
+
+  // 获取货种下拉
+  getCargoSelect(data) {
+    return $http("/cargo/select", data);
   },
   },
 };
 };

+ 7 - 0
src/auth/hasPermission.js

@@ -0,0 +1,7 @@
+export const hasPermission = (roles, route) => {
+  if (route.meta && route.meta.roles) {
+    return roles.some((role) => route.meta.roles.indexOf(role) >= 0);
+  } else {
+    return true;
+  }
+};

+ 74 - 0
src/auth/menuData.js

@@ -0,0 +1,74 @@
+let menuData = [
+  {
+    icon: "el-icon-s-data",
+    title: "航次管理",
+    code: "VOYAGEMANAGE",
+    items: [
+      {
+        path: "/voyage/voyageList",
+        name: "航次列表",
+        code: "VOYAGELIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "代理管理",
+    code: "PROXYMANAGE",
+    items: [
+      {
+        path: "/agencyManage/agencyCompanyList",
+        name: "代理公司列表",
+        code: "PROXYLIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "货种管理",
+    code: "CARGOMANAGE",
+    items: [
+      {
+        path: "/cargoManage/cargoList",
+        name: "货种列表",
+        code: "CARGOLIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "账户管理",
+    code: "ACCOUNTMANAGE",
+    items: [
+      {
+        path: "/accountManage/subAccountList",
+        name: "员工列表",
+        code: "ACCOUNTLIST",
+      },
+      {
+        path: "/accountManage/clientList",
+        name: "客户列表",
+        code: "CLIENTLIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "权限管理",
+    code: "PERMISSIONMANAGE",
+    items: [
+      {
+        path: "/authManage/departmentList",
+        name: "部门列表",
+        code: "DEPTMANAGE",
+      },
+      {
+        path: "/authManage/roleList",
+        name: "职位列表",
+        code: "ROLELIST",
+      },
+    ],
+  },
+];
+
+export default menuData;

+ 7 - 35
src/components/Aside.vue

@@ -7,48 +7,20 @@
     active-text-color="#ffd04b"
     active-text-color="#ffd04b"
     :router="true"
     :router="true"
   >
   >
-    <el-sub-menu v-for="(item, index) in menu" :key="item" :index="`${index}`">
+    <el-sub-menu
+      v-for="(item, index) in this.$store.state.menuData"
+      :key="item"
+      :index="`${index}`"
+    >
       <template v-slot:title>
       <template v-slot:title>
         <i :class="item.icon"></i>
         <i :class="item.icon"></i>
         <span>{{ item.title }}</span>
         <span>{{ item.title }}</span>
       </template>
       </template>
-      <el-menu-item
-        v-for="son in item.items"
-        :index="son.path"
-        :key="son"
-        @click="changeIndex(son.path)"
-      >
+      <el-menu-item v-for="son in item.items" :index="son.path" :key="son">
         {{ son.name }}
         {{ son.name }}
       </el-menu-item>
       </el-menu-item>
     </el-sub-menu>
     </el-sub-menu>
   </el-menu>
   </el-menu>
 </template>
 </template>
-<script>
-import { ref } from "vue";
-export default {
-  setup() {
-    let defaultActive = ref();
-    function changeIndex(path) {
-      defaultActive.value = path;
-    }
-    let menu = [
-      {
-        icon: "el-icon-s-data",
-        title: "航次",
-        items: [
-          {
-            path: "/voyage/voyageList",
-            name: "航次列表",
-          },
-        ],
-      },
-    ];
-
-    return {
-      changeIndex,
-      defaultActive,
-      menu,
-    };
-  },
-};
+<script setup>
 </script>
 </script>

+ 36 - 58
src/components/Header.vue

@@ -14,25 +14,35 @@
     <div class="right">
     <div class="right">
       <img class="user-icon" src="../assets/user.png" alt="" />
       <img class="user-icon" src="../assets/user.png" alt="" />
       <div class="user">{{ contactName }}</div>
       <div class="user">{{ contactName }}</div>
-      <el-popover placement="bottom" trigger="hover" :width="240">
-        <el-timeline>
-          <el-timeline-item
-            v-for="item in timelineData"
-            center
-            :timestamp="item.timer"
-            placement="top"
-          >
-            <div class="log-card">
-              <p style="margin-bottom: 10px">Version: {{ item.version }}</p>
-              <div
-                style="margin-bottom: 5px; font-size: 12px"
-                v-for="(item1, index) in item.remarks"
-              >
-                {{ index + 1 }}. {{ item1.text }}
+      <el-popover placement="bottom" trigger="hover" :width="280">
+        <div
+          style="
+            width: 100%;
+            height: 60vh;
+            overflow-y: auto;
+            padding-right: 10px;
+            margin-right: 10px;
+          "
+        >
+          <el-timeline>
+            <el-timeline-item
+              v-for="item in timelineData"
+              center
+              :timestamp="item.timer"
+              placement="top"
+            >
+              <div class="log-card">
+                <p style="margin-bottom: 10px">Version: {{ item.version }}</p>
+                <div
+                  style="margin-bottom: 5px; font-size: 12px"
+                  v-for="(item1, index) in item.remarks"
+                >
+                  {{ index + 1 }}. {{ item1.text }}
+                </div>
               </div>
               </div>
-            </div>
-          </el-timeline-item>
-        </el-timeline>
+            </el-timeline-item>
+          </el-timeline>
+        </div>
         <template #reference>
         <template #reference>
           <el-badge value="new">
           <el-badge value="new">
             <div class="log">新功能日志</div>
             <div class="log">新功能日志</div>
@@ -60,52 +70,20 @@ export default {
       store.commit("changeLogin", false);
       store.commit("changeLogin", false);
       router.push({ path: "/login" });
       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([]);
     let timelineData = ref([]);
-    // timelineData.value = vs.reverse();
     onMounted(() => {
     onMounted(() => {
       cloudLogin();
       cloudLogin();
     });
     });
     async function getAbledVersions() {
     async function getAbledVersions() {
       let res = await v
       let res = await v
-        .where({ disabled: false, deleted: __.neq(true) })
-        .limit(6)
-        .get();
-      timelineData.value = res.data.reverse();
+        .aggregate()
+        .match({ deleted: __.neq(true) })
+        .sort({
+          createTime: -1,
+        })
+        .limit(10)
+        .end();
+      timelineData.value = res.data;
     }
     }
 
 
     async function cloudLogin() {
     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>

+ 44 - 11
src/main.js

@@ -1,6 +1,7 @@
 import { createApp } from "vue";
 import { createApp } from "vue";
 import ElementPlus from "element-plus";
 import ElementPlus from "element-plus";
 import "element-plus/dist/index.css";
 import "element-plus/dist/index.css";
+import zhCn from "element-plus/es/locale/lang/zh-cn";
 import App from "./App.vue";
 import App from "./App.vue";
 import router from "./router";
 import router from "./router";
 import store from "./store";
 import store from "./store";
@@ -8,20 +9,40 @@ import md5 from "md5";
 import "./styles/index.css";
 import "./styles/index.css";
 import Uploader from "./components/Uploader.vue";
 import Uploader from "./components/Uploader.vue";
 import Certs from "./components/Certs.vue";
 import Certs from "./components/Certs.vue";
+import RemoteSearch from "./components/RemoteSearch.vue";
 
 
 const app = createApp(App);
 const app = createApp(App);
+app.component("RemoteSearch", RemoteSearch);
 app.component("Certs", Certs);
 app.component("Certs", Certs);
 app.component("Uploader", Uploader);
 app.component("Uploader", Uploader);
+
+let userId = localStorage.userId;
+if (userId) {
+  store.dispatch("GetBasePermissionData", localStorage.loginAccountId);
+  store.dispatch("GetUserPermission", localStorage.loginAccountId);
+}
+
 router.beforeEach(async (to, from, next) => {
 router.beforeEach(async (to, from, next) => {
-  let userId = localStorage.userId;
-  if (userId) {
+  if (localStorage.userId) {
     store.commit("changeLogin", true);
     store.commit("changeLogin", true);
-    if (0 === to.matched.length) {
-      next("/voyage/voyageList");
-    } else if (to.path == "/login" || to.path == "/") {
-      next("/voyage/voyageList");
+    let rolePermission = localStorage.rolePermission?.split(",") || [];
+    if (store.state.menuData.length) {
+      let path = store.state?.menuData[0]?.items[0].path;
+      if (0 === to.matched.length) {
+        next(path);
+      } else if (to.path == "/login" || to.path == "/") {
+        next(path);
+      } else if (rolePermission?.indexOf(to.meta.code) == -1) {
+        next(path);
+      } else {
+        next();
+      }
     } else {
     } else {
-      next();
+      if (to.path == "/") {
+        next("/voyage/voyageList");
+      } else {
+        next();
+      }
     }
     }
   } else {
   } else {
     store.commit("changeLogin", false);
     store.commit("changeLogin", false);
@@ -38,8 +59,20 @@ router.afterEach((to, from) => {
   store.commit("setCurrentMenuItem", to.path);
   store.commit("setCurrentMenuItem", to.path);
   store.commit("changefirstTitle", title);
   store.commit("changefirstTitle", title);
 });
 });
-app.config.globalProperties.check = () => {
-  console.log("check");
-};
 
 
-app.use(router).use(ElementPlus).use(store).mount("#app");
+app.directive("auth", {
+  mounted(el, bind) {
+    let permissions = localStorage.rolePermission?.split(",") || [];
+    if (permissions.indexOf(bind.value) == -1) {
+      el.parentNode.removeChild(el);
+    }
+  },
+});
+
+app
+  .use(router)
+  .use(ElementPlus, {
+    locale: zhCn,
+  })
+  .use(store)
+  .mount("#app");

+ 113 - 14
src/router/index.js

@@ -10,11 +10,6 @@ import VoyageList from "../views/voyage/voyageList.vue";
 const router = createRouter({
 const router = createRouter({
   history: createWebHashHistory(),
   history: createWebHashHistory(),
   routes: [
   routes: [
-    {
-      path: "/",
-      name: "index",
-      component: () => import("../views/index/Index.vue"),
-    },
     {
     {
       path: "/login",
       path: "/login",
       name: "login",
       name: "login",
@@ -23,32 +18,136 @@ const router = createRouter({
       },
       },
       component: Login,
       component: Login,
     },
     },
-    {
-      path: "/voyage/voyageAdd",
-      name: "voyageAdd",
-      meta: {
-        title: "添加航次",
-      },
-      component: () => import("../views/voyage/voyageAdd.vue"),
-    },
     {
     {
       path: "/voyage/voyageDetail",
       path: "/voyage/voyageDetail",
       name: "voyageDetail",
       name: "voyageDetail",
       meta: {
       meta: {
         title: "航次详情",
         title: "航次详情",
+        code: "VOYAGEDETAIL",
       },
       },
       component: () => import("../views/voyage/voyageDetail.vue"),
       component: () => import("../views/voyage/voyageDetail.vue"),
     },
     },
-
     {
     {
       path: "/voyage/voyageList",
       path: "/voyage/voyageList",
       name: "voyageList",
       name: "voyageList",
       meta: {
       meta: {
         title: "航次列表",
         title: "航次列表",
+        code: "VOYAGELIST",
       },
       },
       component: VoyageList,
       component: VoyageList,
     },
     },
+    {
+      path: "/accountManage/subAccountList",
+      name: "subAccountList",
+      meta: {
+        title: "员工列表",
+        code: "ACCOUNTLIST",
+      },
+      component: () => import("../views/accountManage/subAccountList.vue"),
+    },
+    {
+      path: "/agencyManage/agencyCompanyList",
+      name: "agencyCompanyList",
+      meta: {
+        title: "代理公司列表",
+        code: "PROXYLIST",
+      },
+      component: () => import("../views/agencyManage/agencyCompanyList.vue"),
+    },
+    {
+      path: "/authManage/roleList",
+      name: "roleList",
+      meta: {
+        title: "职位列表",
+        code: "ROLELIST",
+      },
+      component: () => import("../views/authManage/roleList.vue"),
+    },
+    {
+      path: "/authManage/departmentList",
+      name: "departmentList",
+      meta: {
+        title: "部门列表",
+        code: "DEPTLIST",
+      },
+      component: () => import("../views/authManage/departmentList.vue"),
+    },
+    {
+      path: "/authManage/addRole",
+      name: "addRole",
+      meta: {
+        title: "新增职位",
+        code: "ADDUPDATEROLE",
+      },
+      component: () => import("../views/authManage/addRole.vue"),
+    },
+    {
+      path: "/cargoManage/cargoList",
+      name: "cargoList",
+      meta: {
+        title: "货种列表",
+        code: "CARGOLIST",
+      },
+      component: () => import("../views/cargoManage/cargoList.vue"),
+    },
+    {
+      path: "/accountManage/clientList",
+      name: "clientList",
+      meta: {
+        title: "客户列表",
+        code: "CLIENTLIST",
+      },
+      component: () => import("../views/accountManage/clientList.vue"),
+    },
   ],
   ],
 });
 });
 
 
+export const asyncRouterList = [
+  {
+    path: "/accountManage/subAccountList",
+    name: "subAccountList",
+    meta: {
+      title: "员工列表",
+      code: "ACCOUNTLIST",
+    },
+    component: () => import("../views/accountManage/subAccountList.vue"),
+  },
+  {
+    path: "/agencyManage/agencyCompanyList",
+    name: "agencyCompanyList",
+    meta: {
+      title: "代理公司列表",
+      code: "PROXYLIST",
+    },
+    component: () => import("../views/agencyManage/agencyCompanyList.vue"),
+  },
+  {
+    path: "/authManage/roleList",
+    name: "roleList",
+    meta: {
+      title: "职位列表",
+      code: "ROLELIST",
+    },
+    component: () => import("../views/authManage/roleList.vue"),
+  },
+  {
+    path: "/authManage/addRole",
+    name: "addRole",
+    meta: {
+      title: "新增职位",
+      code: "ADDUPDATEROLE",
+    },
+    component: () => import("../views/authManage/addRole.vue"),
+  },
+  {
+    path: "/cargoManage/cargoList",
+    name: "cargoList",
+    meta: {
+      title: "货种列表",
+      code: "CARGOLIST",
+    },
+    component: () => import("../views/cargoManage/cargoList.vue"),
+  },
+];
+
 export default router;
 export default router;

+ 75 - 1
src/store/index.js

@@ -1,5 +1,8 @@
 import { createStore } from "vuex";
 import { createStore } from "vuex";
-
+import api from "../apis/fetch";
+import router from "../router";
+import { asyncRouterList } from "../router";
+import menuData from "../auth/menuData";
 console.log(import.meta.env.VITE_PROJECT_ENV);
 console.log(import.meta.env.VITE_PROJECT_ENV);
 let baseurl = import.meta.env.VITE_BASEURL;
 let baseurl = import.meta.env.VITE_BASEURL;
 
 
@@ -10,7 +13,12 @@ const store = createStore({
     secondTitle: "",
     secondTitle: "",
     currentMenuItem: "/cargoOwnerManage/cargoOwnerList",
     currentMenuItem: "/cargoOwnerManage/cargoOwnerList",
     baseurl,
     baseurl,
+    basePermissionData: [],
+    userPermission: [],
+    menuData: [],
+    baseParentNodes: [],
   },
   },
+  getters: {},
   mutations: {
   mutations: {
     changefirstTitle(state, text) {
     changefirstTitle(state, text) {
       state.firstTitle = text;
       state.firstTitle = text;
@@ -24,6 +32,72 @@ const store = createStore({
     setCurrentMenuItem(state, index) {
     setCurrentMenuItem(state, index) {
       state.currentMenuItem = index;
       state.currentMenuItem = index;
     },
     },
+    setBasePermissionData(state, data) {
+      state.basePermissionData = data;
+    },
+    setUserPermissionData(state, data) {
+      state.userPermission = data;
+    },
+    setMenuData(state, data) {
+      state.menuData = data;
+    },
+    setBaseParentNodes(state, data) {
+      state.baseParentNodes = data;
+    },
+  },
+  actions: {
+    GetBasePermissionData({ commit }, loginAccountId) {
+      return new Promise((resolve, reject) => {
+        api.getPermisiionData({ loginAccountId }).then((e) => {
+          let data = e.data.result;
+          let arr = [];
+          function getParentNodes(data) {
+            for (let i of data) {
+              if (i.children.length) {
+                arr.push(i.code);
+                getParentNodes(i.children);
+              }
+            }
+          }
+          getParentNodes(data);
+          commit("setBaseParentNodes", arr);
+          commit("setBasePermissionData", data);
+          resolve(1);
+        });
+      });
+    },
+    GetUserPermission({ commit }, loginAccountId) {
+      return new Promise((resolve, reject) => {
+        api.getPermissionByUserId({ loginAccountId }).then((e) => {
+          let res = e.data.result || [];
+          let arr = [...new Set([...res])];
+          commit("setUserPermissionData", arr);
+          localStorage.setItem("rolePermission", arr);
+          let data = [];
+          for (let i in menuData) {
+            // if (menuData[i].title == "航次管理") {
+            //   data[i] = menuData[i];
+            // } else {
+            if (arr.indexOf(menuData[i].code) != -1) {
+              data[i] = menuData[i];
+              let arr0 = [];
+              for (let j in data[i].items) {
+                if (arr.indexOf(data[i].items[j].code) != -1) {
+                  arr0.push(data[i].items[j]);
+                }
+              }
+              data[i].items = arr0;
+            }
+            // }
+          }
+          data = data.filter((item) => {
+            return item;
+          });
+          commit("setMenuData", data);
+          resolve(1);
+        });
+      });
+    },
   },
   },
 });
 });
 
 

+ 21 - 15
src/utils/downloadBlobFile.js

@@ -1,5 +1,5 @@
 import axios from "axios";
 import axios from "axios";
-function downloadBlobFile(url, data, name, type) {
+function downloadBlobFile(url, data, name, type, fileType) {
   return new Promise((resolve, reject) => {
   return new Promise((resolve, reject) => {
     axios({
     axios({
       method: type,
       method: type,
@@ -8,20 +8,26 @@ function downloadBlobFile(url, data, name, type) {
       data,
       data,
     })
     })
       .then((res) => {
       .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) => {
       .catch((e) => {
         reject({
         reject({

+ 7 - 0
src/utils/utils.js

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

+ 353 - 0
src/views/accountManage/clientList.vue

@@ -0,0 +1,353 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <el-input
+          placeholder="请输入姓名/手机号"
+          prefix-icon="el-icon-search"
+          v-model="term"
+          clearable
+          style="height: 32px; width: 330px; line-height: 32px"
+          @keyup.enter.native="getClientList"
+        ></el-input>
+        <div class="seach-btn" @click="getClientList">查询</div>
+      </div>
+      <el-button v-auth="'CLIENTACCOUNT'" type="primary" @click="showAddModal"
+        >添加客户</el-button
+      >
+    </div>
+
+    <el-dialog
+      v-model="visable"
+      :title="accountId ? '修改客户' : '添加客户'"
+      width="550px"
+      @close="resetForm()"
+    >
+      <template v-slot:default>
+        <div class="df jcc">
+          <el-form
+            :model="ruleForm"
+            :rules="rules"
+            ref="form"
+            label-width="110px"
+            label-position="left"
+          >
+            <el-form-item prop="name" label="姓名">
+              <el-input style="width: 280px" v-model="ruleForm.name"></el-input>
+            </el-form-item>
+            <el-form-item prop="phone" label="手机号">
+              <el-input
+                style="width: 280px"
+                v-model="ruleForm.phone"
+              ></el-input>
+            </el-form-item>
+
+            <el-form-item prop="sharePermission" label="查看分享">
+              <el-radio
+                v-model="ruleForm.sharePermission"
+                :label="1"
+                size="large"
+                >有权限</el-radio
+              >
+              <el-radio
+                v-model="ruleForm.sharePermission"
+                :label="0"
+                size="large"
+                >无权限</el-radio
+              >
+            </el-form-item>
+          </el-form>
+        </div>
+      </template>
+      <template v-slot:footer>
+        <div class="dialog-footer">
+          <el-button @click="resetForm">取 消</el-button>
+          <el-button type="primary" @click="addClient(ruleForm)">
+            确 定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <div style="margin-top: 24px">
+      <el-table :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="40"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="userName"
+          label="姓名"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="phone"
+          label="手机号"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <!-- <el-table-column
+          prop="accountStatus"
+          label="状态"
+          min-width="80"
+          align="center"
+        >
+          <template v-slot="scope">
+            <el-switch
+              v-model="scope.row.accountStatus"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+              active-text="启用"
+              inactive-text="禁用"
+              :active-value="1"
+              :inactive-value="0"
+            /> </template
+        ></el-table-column> -->
+        <el-table-column
+          prop="sharePermission"
+          label="分享权限"
+          min-width="80"
+          align="center"
+        >
+          <template v-slot="scope">
+            <el-switch
+              v-model="scope.row.sharePermission"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+              active-text="启用"
+              inactive-text="禁用"
+              :active-value="1"
+              :inactive-value="0"
+              @change="changeSharePermission($event, scope.row)"
+            /> </template
+        ></el-table-column>
+
+        <el-table-column
+          v-auth="'ADDACCOUNT'"
+          label="操作"
+          min-width="120"
+          align="center"
+        >
+          <template v-slot="scope">
+            <div class="df aic jcsa">
+              <!-- <el-switch
+                v-model="scope.row.accountStatus"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+                active-text="启用"
+                inactive-text="禁用"
+                :active-value="1"
+                :inactive-value="0"
+              />
+
+              <div
+                style="
+                  color: red;
+                  margin-left: 10px;
+                  font-size: 14px;
+                  text-decoration: underline;
+                  cursor: pointer;
+                "
+              >
+                删除
+              </div> -->
+              <el-button
+                @click="showUpdateModal(scope.row, 1)"
+                size="small"
+                type="primary"
+                >修改客户权限</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 100%; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import { ref, onMounted, reactive } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+import store from "../../store/index";
+
+let tableData = ref([]);
+let currentPage = ref(1);
+let total = ref(0);
+let term = ref("");
+let loginAccountId = ref(0);
+let accountId = ref("");
+let ruleForm = ref({
+  name: "",
+  phone: "",
+  roleId: "",
+  deptId: "",
+});
+const rules = reactive({
+  name: [
+    {
+      required: true,
+      message: "请填写姓名",
+      trigger: "blur",
+    },
+  ],
+  phone: [
+    {
+      required: true,
+      message: "请填写手机号",
+      trigger: "blur",
+    },
+  ],
+  sharePermission: [
+    {
+      required: true,
+      message: "请选择权限",
+      trigger: "blur",
+    },
+  ],
+});
+
+function showAddModal() {
+  visable.value = true;
+  ruleForm.value = {
+    name: "",
+    phone: "",
+    sharePermission: "",
+  };
+}
+
+async function getClientList() {
+  let res = await api.getClientList({
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+    loginAccountId: loginAccountId.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getClientList();
+}
+let visable = ref(false);
+let form = ref(null);
+async function addClient() {
+  form.value.validate(async (valid) => {
+    if (valid) {
+      let postData = {
+        ...ruleForm.value,
+        loginAccountId: loginAccountId.value,
+      };
+      if (accountId.value) {
+        postData.accountId = accountId.value;
+      }
+      let res = await api[accountId.value ? "updateClient" : "addClient"](
+        postData
+      );
+      if (res.data.status == 0) {
+        ElNotification({
+          title: "成功",
+          duration: 1500,
+          message: res.data.msg,
+          type: "success",
+        });
+        resetForm();
+        getClientList();
+      } else {
+        ElNotification({
+          title: "失败",
+          duration: 1500,
+          message: res.data.msg,
+          type: "error",
+        });
+      }
+    }
+  });
+}
+function resetForm() {
+  visable.value = false;
+  accountId.value = "";
+  form.value.resetFields();
+}
+
+function showUpdateModal(item, type) {
+  visable.value = true;
+  accountId.value = item.id;
+  console.log(item);
+  ruleForm.value = {
+    name: item.userName,
+    phone: item.phone,
+    sharePermission: item.sharePermission,
+  };
+}
+
+async function changeSharePermission(sharePermission, item) {
+  let postData = {
+    accountId: item.id,
+    name: item.userName,
+    phone: item.phone,
+    sharePermission,
+    loginAccountId: loginAccountId.value,
+  };
+
+  let res = await api["updateClient"](postData);
+  if (res.data.status == 0) {
+    ElNotification({
+      title: "成功",
+      duration: 1500,
+      message: res.data.msg,
+      type: "success",
+    });
+    resetForm();
+    getClientList();
+  } else {
+    ElNotification({
+      title: "失败",
+      duration: 1500,
+      message: res.data.msg,
+      type: "error",
+    });
+  }
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getClientList();
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

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

@@ -0,0 +1,353 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <el-input
+          placeholder="请输入姓名/手机号"
+          prefix-icon="el-icon-search"
+          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>
+      <el-button v-auth="'ADDACCOUNT'" type="primary" @click="showAddModal"
+        >添加员工</el-button
+      >
+    </div>
+
+    <el-dialog
+      v-model="visable"
+      :title="accountId ? '修改员工' : '添加员工'"
+      width="550px"
+      @close="resetForm()"
+    >
+      <template v-slot:default>
+        <div class="df jcc">
+          <el-form
+            :model="ruleForm"
+            :rules="rules"
+            ref="form"
+            label-width="110px"
+            label-position="left"
+          >
+            <el-form-item prop="name" label="姓名">
+              <el-input style="width: 280px" v-model="ruleForm.name"></el-input>
+            </el-form-item>
+            <el-form-item prop="phone" label="手机号">
+              <el-input
+                style="width: 280px"
+                v-model="ruleForm.phone"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="deptId" label="部门">
+              <el-select
+                style="width: 280px"
+                v-model="ruleForm.deptId"
+                placeholder="请选择部门"
+                @change="getRoleSelect"
+              >
+                <el-option
+                  v-for="item in departmentSelect"
+                  :key="item"
+                  :label="item.value"
+                  :value="item.key"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item v-if="ruleForm.deptId" prop="roleId" label="职位">
+              <el-select
+                style="width: 280px"
+                v-model="ruleForm.roleId"
+                placeholder="请选择职位"
+              >
+                <el-option
+                  v-for="item in roleSelect"
+                  :key="item"
+                  :label="item.value"
+                  :value="item.key"
+                />
+              </el-select>
+            </el-form-item>
+          </el-form>
+        </div>
+      </template>
+      <template v-slot:footer>
+        <div class="dialog-footer">
+          <el-button @click="resetForm">取 消</el-button>
+          <el-button type="primary" @click="addSubAccount(ruleForm)">
+            确 定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <div style="margin-top: 24px">
+      <el-table :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="40"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="userName"
+          label="姓名"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="phone"
+          label="手机号"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="password"
+          label="密码"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="deptName"
+          label="部门"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="roleName"
+          label="职位"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          v-auth="'ADDACCOUNT'"
+          label="操作"
+          min-width="120"
+          align="center"
+        >
+          <template v-slot="scope">
+            <div class="df aic jcsa">
+              <!-- <el-switch
+                v-model="scope.row.accountStatus"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+                active-text="启用"
+                inactive-text="禁用"
+                :active-value="1"
+                :inactive-value="0"
+              />
+
+              <div
+                style="
+                  color: red;
+                  margin-left: 10px;
+                  font-size: 14px;
+                  text-decoration: underline;
+                  cursor: pointer;
+                "
+              >
+                删除
+              </div> -->
+              <el-button
+                @click="showUpdateModal(scope.row, 1)"
+                size="small"
+                type="primary"
+                >修改员工权限</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 100%; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import { ref, onMounted, reactive } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+import store from "../../store/index";
+
+let tableData = ref([]);
+let currentPage = ref(1);
+let total = ref(0);
+let term = ref("");
+let loginAccountId = ref(0);
+let accountId = ref("");
+let ruleForm = ref({
+  name: "",
+  phone: "",
+  roleId: "",
+  deptId: "",
+});
+const rules = reactive({
+  name: [
+    {
+      required: true,
+      message: "请填写名称",
+      trigger: "blur",
+    },
+  ],
+  phone: [
+    {
+      required: true,
+      message: "请填写手机号",
+      trigger: "blur",
+    },
+  ],
+  roleId: [
+    {
+      required: true,
+      message: "请选择职位",
+      trigger: "blur",
+    },
+  ],
+  deptId: [
+    {
+      required: true,
+      message: "请选择部门",
+      trigger: "blur",
+    },
+  ],
+});
+
+function showAddModal() {
+  visable.value = true;
+  ruleForm.value = {
+    name: "",
+    phone: "",
+    roleId: "",
+    deptId: "",
+  };
+}
+
+async function getSubAccountList() {
+  let res = await api.getSubAccountList({
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+    loginAccountId: loginAccountId.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    for (let i of tableData.value) {
+      if (i.roleId == 0) {
+        i.roleId = "";
+      }
+    }
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getSubAccountList();
+}
+let visable = ref(false);
+let form = ref(null);
+async function addSubAccount() {
+  let postData = {
+    ...ruleForm.value,
+    loginAccountId: loginAccountId.value,
+  };
+  if (accountId.value) {
+    postData.accountId = accountId.value;
+  }
+  let res = await api[accountId.value ? "updateSubAccount" : "addSubAccount"](
+    postData
+  );
+  if (res.data.status == 0) {
+    ElNotification({
+      title: "成功",
+      duration: 1500,
+      message: res.data.msg,
+      type: "success",
+    });
+    store.dispatch("GetBasePermissionData", localStorage.loginAccountId);
+    store.dispatch("GetUserPermission", localStorage.loginAccountId);
+    resetForm();
+    getSubAccountList();
+  } else {
+    ElNotification({
+      title: "失败",
+      duration: 1500,
+      message: res.data.msg,
+      type: "error",
+    });
+  }
+}
+function resetForm() {
+  visable.value = false;
+  accountId.value = "";
+  form.value.resetFields();
+}
+
+function showUpdateModal(item, type) {
+  visable.value = true;
+  accountId.value = item.id;
+  ruleForm.value = {
+    name: item.userName,
+    phone: item.phone,
+    deptId: item.deptId,
+    roleId: item.roleId,
+  };
+  getRoleSelect(type);
+}
+
+let roleSelect = ref([]);
+
+async function getRoleSelect(type) {
+  roleSelect.value = [];
+  if (!ruleForm.value.deptId) return;
+  if (type != 1) ruleForm.value.roleId = "";
+  let res = await api.getRoleSelect({
+    loginAccountId: loginAccountId.value,
+    deptId: ruleForm.value.deptId,
+  });
+  roleSelect.value = res.data.result;
+}
+let departmentSelect = ref([]);
+async function getDepartmentSelect() {
+  let res = await api.getDepartmentSelect({
+    loginAccountId: loginAccountId.value,
+  });
+  departmentSelect.value = res.data.result;
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getSubAccountList();
+  getDepartmentSelect();
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

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

@@ -0,0 +1,238 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <el-input
+          placeholder="请输入名称/联系人/手机号"
+          prefix-icon="el-icon-search"
+          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>
+      <el-button v-auth="'ADDPROXY'" type="primary" @click="visable = true"
+        >添加代理</el-button
+      >
+    </div>
+
+    <el-dialog v-model="visable" title="添加代理" width="550px">
+      <template v-slot:default>
+        <div class="df jcc">
+          <el-form
+            :model="ruleForm"
+            :rules="rules"
+            ref="form"
+            label-width="110px"
+            label-position="left"
+          >
+            <el-form-item prop="proxyName" label="代理名称">
+              <el-input
+                style="width: 280px"
+                v-model="ruleForm.proxyName"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="contactName" label="联系人">
+              <el-input
+                style="width: 280px"
+                v-model="ruleForm.contactName"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="contactPhone" label="联系人手机号">
+              <el-input
+                style="width: 280px"
+                v-model="ruleForm.contactPhone"
+              ></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </template>
+      <template v-slot:footer>
+        <div class="dialog-footer">
+          <el-button @click="resetForm">取 消</el-button>
+          <el-button type="primary" @click="addAgency(ruleForm)">
+            确 定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <div style="margin-top: 24px">
+      <el-table :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="userName"
+          label="代理公司"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="contactName"
+          label="联系人"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="phone"
+          label="手机号"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="入驻时间"
+          min-width="160"
+          align="center"
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" min-width="120" align="center">
+          <template v-slot="scope">
+            <div class="df aic jcsa">
+              <!-- <el-switch
+                v-model="scope.row.accountStatus"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+                active-text="启用"
+                inactive-text="禁用"
+                :active-value="1"
+                :inactive-value="0"
+              /> -->
+              <el-button disabled size="small" type="danger">删除</el-button>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 100%; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import { ref, onMounted, reactive } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+import { subTimeStr } from "../../utils/utils";
+
+let tableData = ref([]);
+let currentPage = ref(1);
+let total = ref(0);
+let term = ref("");
+let loginAccountId = ref(0);
+let ruleForm = ref({
+  proxyName: "",
+  contactName: "",
+  contactPhone: "",
+});
+const rules = reactive({
+  proxyName: [
+    {
+      required: true,
+      message: "请填写代理名称",
+      trigger: "blur",
+    },
+  ],
+  contactPhone: [
+    {
+      required: true,
+      message: "请填联系人手机号",
+      trigger: "blur",
+    },
+  ],
+  contactName: [
+    {
+      required: true,
+      message: "请填写联系人名称",
+      trigger: "blur",
+    },
+  ],
+});
+
+async function getAgencyList() {
+  let res = await api.getAgencyList({
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+    loginAccountId: loginAccountId.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getAgencyList();
+}
+let visable = ref(false);
+let form = ref(null);
+async function addAgency() {
+  console.log(ruleForm.value);
+  let res = await api.addAgency({
+    ...ruleForm.value,
+    loginAccountId: loginAccountId.value,
+  });
+  console.log(res);
+  let status = res.data.status == 0;
+  ElNotification({
+    title: status ? "成功" : "失败",
+    duration: 1500,
+    message: res.data.msg,
+    type: status ? "success" : "error",
+  });
+  resetForm();
+  getAgencyList();
+}
+function resetForm() {
+  visable.value = false;
+  form.value.resetFields();
+}
+
+function closeModal() {
+  proxyName.value = "";
+  contactPhone.value = "";
+  contactName.value = "";
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getAgencyList();
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

+ 208 - 0
src/views/authManage/addRole.vue

@@ -0,0 +1,208 @@
+<template>
+  <el-card>
+    <el-form
+      :model="ruleForm"
+      :rules="rules"
+      ref="form"
+      label-width="110px"
+      label-position="left"
+    >
+      <el-form-item prop="roleName" label="职位名称">
+        <el-input style="width: 280px" v-model="ruleForm.roleName"></el-input>
+      </el-form-item>
+      <el-form-item prop="deptId" label="部门">
+        <el-select
+          style="width: 280px"
+          v-model="ruleForm.deptId"
+          placeholder="请选择部门"
+        >
+          <el-option
+            v-for="item in departmentSelect"
+            :key="item"
+            :label="item.value"
+            :value="item.key"
+          />
+        </el-select>
+      </el-form-item>
+    </el-form>
+  </el-card>
+  <el-card class="mt30 p30">
+    <h4 class="mb30">权限设置</h4>
+    <el-tree
+      ref="treeRef"
+      :data="basePermissionData"
+      show-checkbox
+      default-expand-all
+      node-key="code"
+      highlight-current
+      :props="defaultProps"
+      :default-checked-keys="checkedNodes"
+      style="display: flex; justify-content: space-between"
+    />
+    <div class="df aic jcfe mt50">
+      <el-button type="primary" @click="addRole">{{
+        roleId ? "修改员工" : "添加职位"
+      }}</el-button>
+    </div>
+  </el-card>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import store from "../../store";
+import router from "../../router";
+import { ref, onMounted, reactive, computed } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+import { mapGetters } from "vuex";
+import { useRoute } from "vue-router";
+
+const route = useRoute();
+
+let loginAccountId = ref(0);
+let form = ref(null);
+let ruleForm = ref({
+  roleName: "",
+  deptId: "",
+});
+const rules = reactive({
+  roleName: [
+    {
+      required: true,
+      message: "请填角色名称",
+      trigger: "blur",
+    },
+  ],
+  deptId: [
+    {
+      required: true,
+      message: "请选择部门",
+      trigger: "blur",
+    },
+  ],
+});
+let roleId = ref("");
+let treeRef = ref(null);
+const defaultProps = {
+  children: "children",
+  label: "label",
+};
+
+let basePermissionData = computed(() => store.state.basePermissionData);
+let baseParentNodes = computed(() => store.state.baseParentNodes);
+let checkedNodes = ref([]);
+
+async function getRoleDetail(roleId) {
+  let res = await api.getRoleDetail({
+    loginAccountId: loginAccountId.value,
+    roleId,
+  });
+  if (res.data.status == 0) {
+    let { code, departmentId, roleName, permission, cargoLimit, proxyLimit } =
+      res.data.result;
+    ruleForm.value = {
+      roleName,
+      deptId: departmentId,
+    };
+    let c = cargoLimit.split(",");
+    let cargoLimitArr = [];
+    for (let i of c) {
+      cargoLimitArr.push(`CARGO_${i}`);
+    }
+    let p = proxyLimit.split(",");
+    let proxyLimitArr = [];
+    for (let i of p) {
+      proxyLimitArr.push(`PROXY_${i}`);
+    }
+    let arr = permission.split(",");
+    baseParentNodes.value.forEach((item) => {
+      for (let i of arr) {
+        if (i == item) {
+          let index = arr.indexOf(i);
+          arr.splice(index, 1);
+        }
+      }
+    });
+    checkedNodes.value = [...arr, ...cargoLimitArr, ...proxyLimitArr];
+  }
+}
+
+async function addRole() {
+  let fullChecked = treeRef.value.getCheckedNodes();
+  let halfCHecked = treeRef.value.getHalfCheckedNodes();
+  let permissionCodes = [];
+  for (let i of fullChecked) {
+    permissionCodes.push(i.code);
+  }
+  for (let i of halfCHecked) {
+    permissionCodes.push(i.code);
+  }
+
+  let postData = {
+    loginAccountId: loginAccountId.value,
+    ...ruleForm.value,
+    permissionCodes: permissionCodes.join(","),
+  };
+  if (roleId.value) {
+    postData.roleId = roleId.value;
+  }
+  let res = await api[roleId.value ? "updateRole" : "addRole"](postData);
+  if (res.data.status == 0) {
+    ElNotification({
+      title: "成功",
+      duration: 1500,
+      message: res.data.msg,
+      type: "success",
+    });
+    store.dispatch("GetBasePermissionData", localStorage.loginAccountId);
+    store.dispatch("GetUserPermission", localStorage.loginAccountId);
+    router.replace("/authManage/roleList");
+  } else {
+    console.log(res);
+    ElNotification({
+      title: "失败",
+      duration: 1500,
+      message: res.data.msg,
+      type: "error",
+    });
+  }
+}
+
+let departmentSelect = ref([]);
+async function getDepartmentSelect() {
+  let res = await api.getDepartmentSelect({
+    loginAccountId: loginAccountId.value,
+  });
+  departmentSelect.value = res.data.result;
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getDepartmentSelect();
+  store.dispatch("GetBasePermissionData", localStorage.loginAccountId);
+
+  let id = route.query.roleId;
+  if (id) {
+    getRoleDetail(id);
+    roleId.value = id;
+    store.commit("changefirstTitle", "修改员工");
+  }
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

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

@@ -0,0 +1,236 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <el-input
+          placeholder="请输入部门"
+          prefix-icon="el-icon-search"
+          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>
+      <el-button v-auth="'ADDUPDATEDEPT'" type="primary" @click="visable = true"
+        >添加部门</el-button
+      >
+    </div>
+
+    <el-dialog
+      v-model="visable"
+      :title="deptId ? '修改部门' : '添加部门'"
+      width="550px"
+      @close="resetForm()"
+    >
+      <template v-slot:default>
+        <div class="df jcc">
+          <el-form
+            :model="ruleForm"
+            :rules="rules"
+            ref="form"
+            label-width="110px"
+            label-position="left"
+          >
+            <el-form-item prop="name" label="部门名称">
+              <el-input
+                style="width: 280px"
+                v-model="ruleForm.deptName"
+              ></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </template>
+      <template v-slot:footer>
+        <div class="dialog-footer">
+          <el-button @click="resetForm">取 消</el-button>
+          <el-button type="primary" @click="addDepartment(ruleForm)">
+            确 定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <div style="margin-top: 24px">
+      <el-table :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="40"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="departmentName"
+          label="部门名称"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="创建时间"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          v-auth="'ADDUPDATEDEPT'"
+          label="操作"
+          min-width="120"
+          align="center"
+        >
+          <template v-slot="scope">
+            <div class="df aic jcsa">
+              <!-- <el-switch
+                v-model="scope.row.accountStatus"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+                active-text="启用"
+                inactive-text="禁用"
+                :active-value="1"
+                :inactive-value="0"
+              />
+
+              <div
+                style="
+                  color: red;
+                  margin-left: 10px;
+                  font-size: 14px;
+                  text-decoration: underline;
+                  cursor: pointer;
+                "
+              >
+                删除
+              </div> -->
+              <el-button
+                @click="showUpdateModal(scope.row)"
+                size="small"
+                type="primary"
+                >修改部门</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 100%; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import { ref, onMounted, reactive } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+
+let tableData = ref([]);
+let currentPage = ref(1);
+let total = ref(0);
+let term = ref("");
+let loginAccountId = ref(0);
+let deptId = ref("");
+let ruleForm = ref({
+  deptName: "",
+});
+const rules = reactive({
+  deptName: [
+    {
+      required: true,
+      message: "请填写部门名称",
+      trigger: "blur",
+    },
+  ],
+});
+
+async function getDepartmentList() {
+  let res = await api.getDepartmentList({
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+    loginAccountId: loginAccountId.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    for (let i of tableData.value) {
+      if (i.roleId == 0) {
+        i.roleId = "";
+      }
+    }
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getDepartmentList();
+}
+let visable = ref(false);
+let form = ref(null);
+async function addDepartment() {
+  let postData = {
+    ...ruleForm.value,
+    loginAccountId: loginAccountId.value,
+  };
+  if (deptId.value) {
+    postData.deptId = deptId.value;
+  }
+  let res = await api[deptId.value ? "updateDepartment" : "addDepartment"](
+    postData
+  );
+  let status = res.data.status == 0;
+  ElNotification({
+    title: status ? "成功" : "失败",
+    duration: 1500,
+    message: res.data.msg,
+    type: status ? "success" : "error",
+  });
+  resetForm();
+  getDepartmentList();
+}
+function resetForm() {
+  visable.value = false;
+  deptId.value = "";
+  ruleForm.value = {
+    deptName: "",
+  };
+  form.value.resetFields();
+}
+
+function showUpdateModal(item) {
+  visable.value = true;
+  deptId.value = item.id;
+  ruleForm.value = {
+    deptName: item.departmentName,
+  };
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getDepartmentList();
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

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

@@ -0,0 +1,147 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <el-input
+          placeholder="请输入部门/职位名称"
+          prefix-icon="el-icon-search"
+          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>
+      <el-button v-auth="'ADDUPDATEROLE'" type="primary" @click="addRole"
+        >添加职位</el-button
+      >
+    </div>
+    <div style="margin-top: 24px">
+      <el-table :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="roleName"
+          label="职位名称"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="departmentName"
+          label="部门"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="创建时间"
+          min-width="160"
+          align="center"
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          v-auth="'ADDUPDATEROLE'"
+          label="操作"
+          min-width="120"
+          align="center"
+        >
+          <template v-slot="scope">
+            <el-button
+              @click="roleDetail(scope.row.id)"
+              size="small"
+              type="primary"
+              >详情</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 100%; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import { ref, onMounted, reactive } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+import router from "../../router";
+import store from "../../store";
+import { subTimeStr } from "../../utils/utils";
+
+let tableData = ref([]);
+let currentPage = ref(1);
+let total = ref(0);
+let term = ref("");
+let loginAccountId = ref(0);
+
+async function getRoleList() {
+  let res = await api.getRoleList({
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+    loginAccountId: loginAccountId.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getRoleList();
+}
+
+function addRole() {
+  router.push("/authManage/addRole");
+}
+
+function roleDetail(roleId) {
+  router.push({
+    path: "/authManage/addRole",
+    query: {
+      roleId,
+    },
+  });
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getRoleList();
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

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

@@ -0,0 +1,164 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <el-input
+          placeholder="请输入货种名称"
+          prefix-icon="el-icon-search"
+          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>
+      <el-button v-auth="'ADDCARGO'" type="primary" @click="addCargo"
+        >添加货种</el-button
+      >
+    </div>
+
+    <div style="margin-top: 24px">
+      <el-table :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="cargo"
+          label="货种名称"
+          min-width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="添加时间"
+          min-width="160"
+          align="center"
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" min-width="80" align="center">
+          <template v-slot="scope">
+            <div class="df aic jcsa">
+              <!-- <el-switch
+                v-model="scope.row.status"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+                active-text="启用"
+                inactive-text="禁用"
+                :active-value="1"
+                :inactive-value="0"
+              /> -->
+              <el-button disabled size="small" type="danger">删除</el-button>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="width: 100%; text-align: right; margin-top: 43px">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import api from "../../apis/fetch";
+import { ref, onMounted } from "vue";
+import { ElNotification, ElMessageBox } from "element-plus";
+import { subTimeStr } from "../../utils/utils";
+
+let tableData = ref([]);
+let currentPage = ref(1);
+let total = ref(0);
+let term = ref("");
+let loginAccountId = ref(0);
+let cargo = ref("");
+
+async function getCargoList() {
+  let res = await api.getCargoList({
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+    loginAccountId: loginAccountId.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getCargoList();
+}
+function addCargo() {
+  ElMessageBox.prompt("请输入货种名称", "添加货种", {
+    confirmButtonText: "添加",
+    cancelButtonText: "取消",
+    inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/,
+    inputErrorMessage: "请输入货种名称",
+  })
+    .then(async ({ value }) => {
+      let res = await api.addCargo({
+        loginAccountId: loginAccountId.value,
+        cargo: value,
+      });
+      if (res.data.status == 0) {
+        ElNotification({
+          title: "成功",
+          duration: 1500,
+          message: res.data.msg,
+          type: "success",
+        });
+        getCargoList();
+      } else {
+        ElNotification({
+          title: "失败",
+          duration: 1500,
+          message: res.data.msg,
+          type: "error",
+        });
+      }
+    })
+    .catch(() => {});
+}
+
+function closeModal() {
+  cargo.value = "";
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getCargoList();
+});
+</script>
+
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 38px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 38px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+</style>

+ 2 - 16
src/views/index/Index.vue

@@ -1,17 +1,3 @@
-<template>
-  index
-  <div v-for="i in arr" :key="i">
-    <p>{{ i }}</p>
-  </div>
-</template>
-<script>
-export default {
-  data() {
-    return {
-      arr: ["a", "b", "c"],
-    };
-  },
-  methods: {},
-  created() {},
-};
+<template>主页</template>
+<script setup>
 </script>
 </script>

+ 32 - 3
src/views/index/Login.vue

@@ -73,7 +73,7 @@
   </div>
   </div>
 </template>
 </template>
 <script>
 <script>
-import { ref, reactive, toRefs } from "vue";
+import { ref, reactive, toRefs, computed } from "vue";
 import { ElNotification } from "element-plus";
 import { ElNotification } from "element-plus";
 import store from "../../store";
 import store from "../../store";
 import router from "../../router";
 import router from "../../router";
@@ -117,20 +117,49 @@ export default {
             cargoOwnerId: 25,
             cargoOwnerId: 25,
           });
           });
           if (res.data.status == 0) {
           if (res.data.status == 0) {
+            let {
+              userId,
+              userName,
+              phone,
+              contactName,
+              loginAccountId,
+              rolePermission,
+            } = res.data.result;
+            if (!rolePermission.length) {
+              ElNotification.error({
+                title: "失败",
+                duration: 2000,
+                message: "暂无权限,请联系管理员",
+                type: "error",
+              });
+              return;
+            }
             ElNotification.success({
             ElNotification.success({
               title: "成功",
               title: "成功",
               duration: 2000,
               duration: 2000,
               message: res.data.msg,
               message: res.data.msg,
               type: "success",
               type: "success",
             });
             });
-            let { userId, userName, phone, contactName } = res.data.result;
             localStorage.setItem("userId", userId);
             localStorage.setItem("userId", userId);
             localStorage.setItem("userName", userName);
             localStorage.setItem("userName", userName);
             localStorage.setItem("phone", phone);
             localStorage.setItem("phone", phone);
             localStorage.setItem("contactName", contactName);
             localStorage.setItem("contactName", contactName);
             localStorage.setItem("userType", 1);
             localStorage.setItem("userType", 1);
+            localStorage.setItem("loginAccountId", loginAccountId);
+            rolePermission = rolePermission || [];
+            let arr = [...new Set([...rolePermission])];
+            localStorage.setItem("rolePermission", arr);
+            store.dispatch(
+              "GetBasePermissionData",
+              localStorage.loginAccountId
+            );
+            let res1 = await store.dispatch(
+              "GetUserPermission",
+              localStorage.loginAccountId
+            );
+
+            router.replace({ path: store.state.menuData[0]?.items[0].path });
             store.commit("changeLogin", true);
             store.commit("changeLogin", true);
-            router.replace({ path: "/voyage/voyageList" });
           } else {
           } else {
             ElNotification.error({
             ElNotification.error({
               title: "错误",
               title: "错误",

+ 0 - 117
src/views/voyage/voyageAdd.vue

@@ -1,117 +0,0 @@
-<template>
-  <div class="line-container-p18">
-    <i class="el-icon-arrow-left"></i>
-    <div class="dib go-back ml8">返回航次列表</div>
-  </div>
-  <div id="map-container" class="map-container"></div>
-
-  <div class="line-container-p24">
-    <el-form
-      :rules="rules"
-      label-position="right"
-      label-width="80px"
-      :model="voyageForm"
-    >
-      <div class="line1 df" style="flex-wrap: wrap">
-        <el-form-item prop="voyageName" label="航次名称">
-          <el-input v-model="voyageForm.voyageName"></el-input>
-        </el-form-item>
-        <el-form-item label="货主">
-          <el-input v-model="voyageForm.cargoOwnerId"></el-input>
-        </el-form-item>
-        <el-form-item label="开始时间">
-          <el-input v-model="voyageForm.startTime"></el-input>
-        </el-form-item>
-        <el-form-item label="结束时间">
-          <el-input v-model="voyageForm.endTime"></el-input>
-        </el-form-item>
-        <el-form-item label="装货港">
-          <el-input v-model="voyageForm.loadPort"></el-input>
-        </el-form-item>
-        <el-form-item label="卸货港">
-          <el-input v-model="voyageForm.dischargPort"></el-input>
-        </el-form-item>
-        <el-form-item label="货种">
-          <el-input v-model="voyageForm.cargo"></el-input>
-        </el-form-item>
-        <el-form-item label="吨位">
-          <el-input v-model="voyageForm.tons"></el-input>
-        </el-form-item>
-      </div>
-    </el-form>
-  </div>
-</template>
-<script>
-import { onMounted, ref, toRefs, reactive } from "vue";
-export default {
-  setup() {
-    let map = ref();
-    const rules = reactive({
-      rules: {
-        voyageName: [{ required: true, message: "", trigger: "blur" }],
-        cargoOwnerId: [{ required: true, message: "", trigger: "blur" }],
-        startTime: [{ required: true, message: "", trigger: "blur" }],
-        endTime: [{ required: true, message: "", trigger: "blur" }],
-        loadPort: [{ required: true, message: "", trigger: "blur" }],
-        dischargPort: [{ required: true, message: "", trigger: "blur" }],
-        cargo: [{ required: true, message: "", trigger: "blur" }],
-        tons: [{ required: true, message: "", trigger: "blur" }],
-      },
-    });
-    let voyageForm = reactive({
-      voyageForm: {
-        voyageName: "",
-        cargoOwnerId: "",
-        startTime: "",
-        endTime: "",
-        loadPort: "",
-        dischargPort: "",
-        cargo: "",
-        tons: "",
-      },
-    });
-
-    function initMap() {
-      var center = new TMap.LatLng(31.228721, 121.524761);
-      //初始化地图
-      map.value = new TMap.Map("map-container", {
-        zoom: 12, //设置地图缩放级别
-        center: center, //设置地图中心点坐标
-      });
-    }
-
-    onMounted(() => {
-      initMap();
-    });
-
-    return {
-      ...toRefs(voyageForm),
-      ...toRefs(rules),
-    };
-  },
-};
-</script>
-<style scoped>
-.go-back {
-  font-size: 16px;
-  font-family: PingFangSC-Medium, PingFang SC;
-  font-weight: 500;
-  color: #333d43;
-  line-height: 100%;
-  cursor: pointer;
-}
-
-.map-container {
-  width: 100%;
-  height: 658px;
-  margin: 20px 0;
-}
-
-:deep() .el-form-item {
-  margin-right: 22px;
-  width: 280px;
-}
-:deep() .el-input__inner {
-  text-align: center;
-}
-</style>

Diff do ficheiro suprimidas por serem muito extensas
+ 766 - 432
src/views/voyage/voyageDetail.vue


+ 814 - 351
src/views/voyage/voyageList.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <div class="line-container-p24">
   <div class="line-container-p24">
-    <div style="display: flex; justify-content: space-between">
+    <div class="df jcsb aic">
       <div class="df aic">
       <div class="df aic">
         <div
         <div
           @click="changeVoyageType(0)"
           @click="changeVoyageType(0)"
@@ -58,14 +58,69 @@
           prefix-icon="el-icon-search"
           prefix-icon="el-icon-search"
           v-model="term"
           v-model="term"
           clearable
           clearable
-          style="width: 330px"
+          style="width: 240px"
+          @keyup.enter.native="getVoyageList()"
         ></el-input>
         ></el-input>
         <div class="search-btn" @click="getVoyageList()">查询</div>
         <div class="search-btn" @click="getVoyageList()">查询</div>
       </div>
       </div>
       <!-- <div class="cargo-owner-add" @click="voyageAddDialogVisible = true">
       <!-- <div class="cargo-owner-add" @click="voyageAddDialogVisible = true">
         添加航次
         添加航次
       </div> -->
       </div> -->
+      <div>
+        <el-button
+          type="primary"
+          size="small"
+          v-auth="'DOWNLOADVOYAGELIST'"
+          @click="showExportModal('航次列表')"
+          style="margin-left: 10px; margin-bottom: 10px"
+          >导出航次列表</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>
     </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-dialog v-model="voyageAddDialogVisible" title="添加航次">
       <el-form
       <el-form
         :rules="rules"
         :rules="rules"
@@ -149,63 +204,306 @@
         </span>
         </span>
       </template>
       </template>
     </el-dialog>
     </el-dialog>
-    <el-table :data="tableData" stripe style="width: 100%; margin-top: 24px">
-      <el-table-column
+    <!-- <div class="mt20">
+      <p class="mr20 df aic" style="font-size: 14px; color: #333">
+        列表筛选:
+        <el-checkbox
+          class="ml20"
+          v-model="selectAllVisable"
+          label="全选"
+          size="default"
+          @change="selectAll"
+        />
+      </p>
+      <el-checkbox
+        v-model="voyageNameVisable"
+        label="航次名称"
+        size="default"
+        @change="selectSingle"
+        disabled
+      />
+      <el-checkbox
+        v-model="loadPortVisable"
+        label="装货港"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="dischargePortVisable"
+        label="卸货港"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="expectedArrivalTimeVisable"
+        label="预计到港时间"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="abnormalStatusVisable"
+        label="航次状态"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="daysInPortVisable"
+        label="在港天数"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="todayPhotoCountVisable"
+        label="今日照片"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="cargoVisable"
+        label="货种"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="actualLoadTonsVisable"
+        label="装载吨位"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="unloadedtonsVisable"
+        label="已卸货吨位"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="remainTonsVisable"
+        label="剩余吨位"
+        size="default"
+        @change="selectSingle"
+      />
+      <el-checkbox
+        v-model="hasInsuranceVisable"
+        label="保险状态"
+        size="default"
+        @change="selectSingle"
+      />
+    </div> -->
+    <div
+      class="df aic jcfs mt20"
+      style="
+        font-size: 14px;
+        color: #333;
+        width: calc(100vw - 300px);
+        flex-wrap: wrap;
+      "
+    >
+      <div class="df jcsb aic mb10 mr20">
+        <div class="mr10">装货港:</div>
+        <el-select
+          style="width: 120px"
+          v-model="voyageListPostData.loadPortId"
+          placeholder="装货港"
+          size="small"
+          @change="getVoyageList"
+          @focus="getPortSelect"
+          filterable
+        >
+          <el-option
+            v-for="item in portOptions"
+            :key="item"
+            :label="item.value"
+            :value="item.key"
+          />
+        </el-select>
+      </div>
+      <div class="df jcsb aic mb10 mr20">
+        <div class="mr10">卸货港:</div>
+        <el-select
+          style="width: 120px"
+          v-model="voyageListPostData.discPortId"
+          placeholder="卸货港"
+          size="small"
+          @change="getVoyageList"
+          @focus="getPortSelect"
+          filterable
+        >
+          <el-option
+            v-for="item in portOptions"
+            :key="item"
+            :label="item.value"
+            :value="item.key"
+          />
+        </el-select>
+      </div>
+      <div class="df jcsb aic mb10 mr20">
+        <div class="mr10">预计到港时间:</div>
+        <el-select
+          style="width: 120px"
+          v-model="voyageListPostData.isArrived"
+          placeholder="到港状态"
+          size="small"
+          @change="getVoyageList"
+        >
+          <el-option label="已到港" :value="0" />
+          <el-option label="未到港" :value="1" />
+        </el-select>
+      </div>
+      <div class="df jcsb aic mb10 mr20">
+        <div class="mr10">航次状态:</div>
+        <el-select
+          style="width: 120px"
+          v-model="voyageListPostData.abnormalStatus"
+          placeholder="航次状态"
+          size="small"
+          @change="getVoyageList"
+        >
+          <el-option label="正常" :value="0" />
+          <el-option label="异常" :value="1" />
+        </el-select>
+      </div>
+      <div class="df jcsb aic mb10 mr20">
+        <div class="mr10">货种:</div>
+        <el-select
+          style="width: 100px"
+          v-model="voyageListPostData.cargo"
+          placeholder="货种"
+          size="small"
+          @change="getVoyageList"
+          @focus="getCargoSelect"
+          filterable
+        >
+          <el-option
+            v-for="item in cargoOptions"
+            :key="item"
+            :label="item.key"
+            :value="item.value"
+          />
+        </el-select>
+      </div>
+      <div class="df jcsb aic mb10 mr20">
+        <div class="mr10">保险状态:</div>
+        <el-select
+          style="width: 120px"
+          v-model="voyageListPostData.hasInsurance"
+          placeholder="保险状态"
+          size="small"
+          @change="getVoyageList"
+        >
+          <el-option label="未购买" :value="0" />
+          <el-option label="已购买" :value="1" />
+        </el-select>
+      </div>
+      <el-button @click="resetFilter" class="mb10" size="small" type="primary"
+        >重置</el-button
+      >
+    </div>
+    <el-table
+      :data="tableData"
+      stripe
+      style="width: 100%; margin-top: 12px"
+      :row-style="rowStyle"
+    >
+      <!-- <el-table-column
         type="index"
         type="index"
         label="序号"
         label="序号"
         min-width="80"
         min-width="80"
         align="center"
         align="center"
-      ></el-table-column>
+      ></el-table-column> -->
       <el-table-column
       <el-table-column
         prop="voyageName"
         prop="voyageName"
         label="航次名称"
         label="航次名称"
+        v-if="voyageNameVisable"
         min-width="140"
         min-width="140"
         align="center"
         align="center"
       ></el-table-column>
       ></el-table-column>
       <el-table-column
       <el-table-column
-        prop="loadDiscPort"
-        label="装货港-卸货港"
-        min-width="200"
+        prop="loadPort"
+        label="装货港"
+        v-if="loadPortVisable"
+        min-width="90"
         align="center"
         align="center"
       ></el-table-column>
       ></el-table-column>
       <el-table-column
       <el-table-column
+        prop="dischargePort"
+        label="卸货港"
+        v-if="dischargePortVisable"
+        min-width="80"
+        align="center"
+      ></el-table-column>
+      <!-- <el-table-column
         prop="setSailTime"
         prop="setSailTime"
         label="开航时间"
         label="开航时间"
-        min-width="180"
+        min-width="100"
         align="center"
         align="center"
-      ></el-table-column>
+      ></el-table-column> -->
       <el-table-column
       <el-table-column
         prop="expectedArrivalTime"
         prop="expectedArrivalTime"
         label="预计到港时间"
         label="预计到港时间"
-        min-width="180"
+        v-if="expectedArrivalTimeVisable"
+        sortable
+        min-width="140"
         align="center"
         align="center"
-      ></el-table-column>
+      >
+        <template v-slot="scope">
+          {{ scope.row.arrived ? "已到港" : scope.row.expectedArrivalTime }}
+        </template>
+      </el-table-column>
       <el-table-column
       <el-table-column
-        prop="daysInPort"
-        label="在港天数"
+        prop="abnormalStatus"
+        label="航次状态"
+        v-if="abnormalStatusVisable"
         min-width="80"
         min-width="80"
         align="center"
         align="center"
+      >
+        <template v-slot="scope">
+          {{ scope.row.abnormalStatus == 0 ? "正常" : "异常" }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="daysInPortStr"
+        label="在港天数"
+        v-if="daysInPortVisable"
+        sortable
+        min-width="100"
+        align="center"
       ></el-table-column>
       ></el-table-column>
       <el-table-column
       <el-table-column
         prop="todayPhotoCount"
         prop="todayPhotoCount"
         label="今日照片"
         label="今日照片"
-        min-width="80"
+        v-if="todayPhotoCountVisable"
+        min-width="100"
         align="center"
         align="center"
       ></el-table-column>
       ></el-table-column>
       <el-table-column
       <el-table-column
         prop="cargo"
         prop="cargo"
+        v-if="cargoVisable"
         label="货种"
         label="货种"
-        min-width="80"
+        min-width="70"
         align="center"
         align="center"
       ></el-table-column>
       ></el-table-column>
-
       <el-table-column
       <el-table-column
-        prop="tons"
-        label="吨位(吨)"
+        prop="actualLoadTons"
+        label="装载吨位"
+        v-if="actualLoadTonsVisable"
         min-width="80"
         min-width="80"
         align="center"
         align="center"
       ></el-table-column>
       ></el-table-column>
       <el-table-column
       <el-table-column
+        prop="unloadedtons"
+        label="已卸货吨位"
+        v-if="unloadedtonsVisable"
+        min-width="100"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="remainTons"
+        label="剩余吨位"
+        v-if="remainTonsVisable"
+        min-width="80"
+        align="center"
+      ></el-table-column>
+      <!-- <el-table-column
         prop="waybillStatus"
         prop="waybillStatus"
         sortable
         sortable
         label="签单状态"
         label="签单状态"
@@ -221,7 +519,7 @@
               : "已签单"
               : "已签单"
           }}
           }}
         </template>
         </template>
-      </el-table-column>
+      </el-table-column> -->
       <!-- <el-table-column
       <!-- <el-table-column
         prop="transStatus"
         prop="transStatus"
         label="船舶状态"
         label="船舶状态"
@@ -230,7 +528,8 @@
       ></el-table-column> -->
       ></el-table-column> -->
       <el-table-column
       <el-table-column
         prop="hasInsurance"
         prop="hasInsurance"
-        label="保险单状态"
+        label="保险状态"
+        v-if="hasInsuranceVisable"
         min-width="100"
         min-width="100"
         align="center"
         align="center"
       >
       >
@@ -238,20 +537,30 @@
           {{ scope.row.hasInsurance == 0 ? "未购买" : "已购买" }}
           {{ scope.row.hasInsurance == 0 ? "未购买" : "已购买" }}
         </template>
         </template>
       </el-table-column>
       </el-table-column>
-      <el-table-column
+      <!-- <el-table-column
         sortable
         sortable
         prop="createTime"
         prop="createTime"
         label="创建时间"
         label="创建时间"
         min-width="100"
         min-width="100"
         align="center"
         align="center"
-      ></el-table-column>
+      >
+        <template v-slot="scope">
+          {{ subTimeStr(scope.row.createTime) }}
+        </template>
+      </el-table-column>
       <el-table-column
       <el-table-column
         prop="remark"
         prop="remark"
         label="备注"
         label="备注"
         min-width="100"
         min-width="100"
         align="center"
         align="center"
-      ></el-table-column>
-      <el-table-column label="操作" min-width="80" align="center">
+      ></el-table-column> -->
+      <el-table-column
+        v-auth="'VOYAGEDETAIL'"
+        label="操作"
+        min-width="80"
+        align="center"
+        fixed="right"
+      >
         <template v-slot="scope">
         <template v-slot="scope">
           <el-button
           <el-button
             @click="voyageDetail(scope.row.id, tableData)"
             @click="voyageDetail(scope.row.id, tableData)"
@@ -273,7 +582,7 @@
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
-<script>
+<script setup>
 import { ref, h, reactive, toRefs, onMounted } from "vue";
 import { ref, h, reactive, toRefs, onMounted } from "vue";
 import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
 import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
 import store from "../../store";
 import store from "../../store";
@@ -281,363 +590,517 @@ import router from "../../router";
 import md5 from "md5";
 import md5 from "md5";
 import api from "../../apis/fetch";
 import api from "../../apis/fetch";
 import _ from "lodash";
 import _ from "lodash";
+import { subTimeStr } from "../../utils/utils";
+import downloadBlobFile from "../../utils/downloadBlobFile";
+import url from "../../apis/config";
 
 
-export default {
-  setup() {
-    let currentPage = ref(1);
-    let term = ref("");
-    let tableData = ref([]);
-    let total = ref(0);
-    let status = ref(0);
-    async function getVoyageList() {
-      tableData.value = [];
-
-      let res = await api.getVoyageList({
-        cargoOwnerId: localStorage.userId,
-        shipId: 0,
-        status: status.value,
-        term: term.value,
-        currentPage: currentPage.value,
-        size: 10,
-      });
-      term.value = "";
-      if (res.data.status == 0) {
-        tableData.value = res.data.result;
-        total.value = res.data.total;
-      } else {
-        ElNotification({
-          type: "error",
-          title: res.data.msg,
-        });
-      }
-    }
-    function changeVoyageType(s) {
-      term.value = "";
-      currentPage.value = 1;
-      status.value = s;
-      getVoyageList();
-    }
-    async function voyageDetail(id) {
-      router.push({
-        path: "/voyage/voyageDetail",
-        query: {
-          id,
-        },
-      });
-    }
-    function pageChange(e) {
-      currentPage.value = e;
-      getVoyageList();
-    }
+let currentPage = ref(1);
+let term = ref("");
+let tableData = ref([]);
+let total = ref(0);
+let status = ref(0);
+let loginAccountId = ref("");
+let voyageListPostData = ref({});
+async function getVoyageList() {
+  tableData.value = [];
 
 
-    function goToVoyageAdd() {
-      router.push({
-        path: "/voyage/voyageAdd",
-      });
-    }
-    let voyageAddDialogVisible = ref(false);
-    const rules = reactive({
-      rules: {
-        voyageName: [
-          { required: false, message: "请填写航次名称", trigger: "blur" },
-        ],
-        shipName: [{ required: true, message: "请选择船舶", trigger: "blur" }],
-        cargoOwnerName: [
-          { required: true, message: "请选择货主", trigger: "blur" },
-        ],
-        startTime: [
-          { required: true, message: "请填写开始时间", trigger: "blur" },
-        ],
-        loadPort: [
-          { required: true, message: "请填写装货港", trigger: "blur" },
-        ],
-        dischargeProt: [
-          { required: true, message: "请填写卸货港", trigger: "blur" },
-        ],
-        cargo: [{ required: true, message: "请填写货种", trigger: "blur" }],
-        tons: [{ required: true, message: "请填写吨位", trigger: "blur" }],
-      },
-    });
-    let voyageForm = reactive({
-      voyageForm: {
-        voyageName: "",
-        cargoOwnerId: "",
-        cargoOwnerName: "",
-        startTime: "",
-        endTime: "",
-        loadPort: "",
-        dischargeProt: "",
-        cargo: "",
-        tons: "",
-        loadPortId: "",
-        dischargeProtId: "",
-        shipId: "",
-        shipName: "",
-      },
+  let res = await api.getVoyageList({
+    ...voyageListPostData.value,
+    loginAccountId: localStorage.loginAccountId,
+    cargoOwnerId: localStorage.userId,
+    shipId: 0,
+    status: status.value,
+    term: term.value,
+    currentPage: currentPage.value,
+    size: 10,
+  });
+  term.value = "";
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    total.value = res.data.total;
+  } else {
+    ElNotification({
+      type: "error",
+      title: res.data.msg,
     });
     });
+  }
+}
+function changeVoyageType(s) {
+  term.value = "";
+  currentPage.value = 1;
+  status.value = s;
+  getVoyageList();
+}
+async function voyageDetail(id) {
+  router.push({
+    path: "/voyage/voyageDetail",
+    query: {
+      id,
+    },
+  });
+}
+function pageChange(e) {
+  currentPage.value = e;
+  getVoyageList();
+}
 
 
-    function clear(type) {
-      setTimeout(() => {
-        switch (type) {
-          case "shipId": {
-            let index = ref(-1);
-            for (let i in shipsCache.value) {
-              if (
-                voyageForm.voyageForm.shipName == shipsCache.value[i].shipName
-              ) {
-                index.value = i;
-                break;
-              }
-            }
-            if (index.value != -1) {
-              voyageForm.voyageForm.shipId =
-                shipsCache.value[index.value].shipId;
-            } else {
-              let b = shipsCache.value.some((item) => {
-                return (
-                  item.shipId == voyageForm.voyageForm.shipId &&
-                  item.shipName == voyageForm.voyageForm.shipName
-                );
-              });
-              voyageForm.voyageForm["shipId"] = "";
-              voyageForm.voyageForm["shipName"] = "";
-            }
-            break;
-          }
-          case "cargoOwnerId": {
-            let index = ref(-1);
-            for (let i in cargoOwnersCache.value) {
-              if (
-                voyageForm.voyageForm.cargoOwnerName ==
-                cargoOwnersCache.value[i].userName
-              ) {
-                index.value = i;
-                break;
-              }
-            }
-            if (index.value != -1) {
-              voyageForm.voyageForm.cargoOwnerId =
-                cargoOwnersCache.value[index.value].userId;
-            } else {
-              let b = cargoOwnersCache.value.some((item) => {
-                return (
-                  item.userId == voyageForm.voyageForm.cargoOwnerId &&
-                  item.userName == voyageForm.voyageForm.cargoOwnerName
-                );
-              });
-              if (!b) {
-                voyageForm.voyageForm["cargoOwnerId"] = "";
-                voyageForm.voyageForm["cargoOwnerName"] = "";
-              }
-            }
+function goToVoyageAdd() {
+  router.push({
+    path: "/voyage/voyageAdd",
+  });
+}
+let voyageAddDialogVisible = ref(false);
+const rules = reactive({
+  rules: {
+    voyageName: [
+      { required: false, message: "请填写航次名称", trigger: "blur" },
+    ],
+    shipName: [{ required: true, message: "请选择船舶", trigger: "blur" }],
+    cargoOwnerName: [
+      { required: true, message: "请选择货主", trigger: "blur" },
+    ],
+    startTime: [{ required: true, message: "请填写开始时间", trigger: "blur" }],
+    loadPort: [{ required: true, message: "请填写装货港", trigger: "blur" }],
+    dischargeProt: [
+      { required: true, message: "请填写卸货港", trigger: "blur" },
+    ],
+    cargo: [{ required: true, message: "请填写货种", trigger: "blur" }],
+    tons: [{ required: true, message: "请填写吨位", trigger: "blur" }],
+  },
+});
+let voyageForm = reactive({
+  voyageForm: {
+    voyageName: "",
+    cargoOwnerId: "",
+    cargoOwnerName: "",
+    startTime: "",
+    endTime: "",
+    loadPort: "",
+    dischargeProt: "",
+    cargo: "",
+    tons: "",
+    loadPortId: "",
+    dischargeProtId: "",
+    shipId: "",
+    shipName: "",
+  },
+});
 
 
+function clear(type) {
+  setTimeout(() => {
+    switch (type) {
+      case "shipId": {
+        let index = ref(-1);
+        for (let i in shipsCache.value) {
+          if (voyageForm.voyageForm.shipName == shipsCache.value[i].shipName) {
+            index.value = i;
             break;
             break;
           }
           }
-          case "loadPort": {
-            let index = ref(-1);
-            for (let i in colCache.value) {
-              if (voyageForm.voyageForm.loadPort == colCache.value[i].value) {
-                index.value = i;
-                break;
-              }
-            }
-            if (index.value != -1) {
-              voyageForm.voyageForm.loadPortId =
-                colCache.value[index.value].key;
-            } else {
-              let b = colCache.value.some((item) => {
-                return (
-                  item.value == voyageForm.voyageForm.loadPort &&
-                  item.key == voyageForm.voyageForm.loadPortId
-                );
-              });
-              if (!b) {
-                voyageForm.voyageForm["loadPort"] = "";
-                voyageForm.voyageForm["loadPortId"] = "";
-              }
-            }
-
+        }
+        if (index.value != -1) {
+          voyageForm.voyageForm.shipId = shipsCache.value[index.value].shipId;
+        } else {
+          let b = shipsCache.value.some((item) => {
+            return (
+              item.shipId == voyageForm.voyageForm.shipId &&
+              item.shipName == voyageForm.voyageForm.shipName
+            );
+          });
+          voyageForm.voyageForm["shipId"] = "";
+          voyageForm.voyageForm["shipName"] = "";
+        }
+        break;
+      }
+      case "cargoOwnerId": {
+        let index = ref(-1);
+        for (let i in cargoOwnersCache.value) {
+          if (
+            voyageForm.voyageForm.cargoOwnerName ==
+            cargoOwnersCache.value[i].userName
+          ) {
+            index.value = i;
             break;
             break;
           }
           }
+        }
+        if (index.value != -1) {
+          voyageForm.voyageForm.cargoOwnerId =
+            cargoOwnersCache.value[index.value].userId;
+        } else {
+          let b = cargoOwnersCache.value.some((item) => {
+            return (
+              item.userId == voyageForm.voyageForm.cargoOwnerId &&
+              item.userName == voyageForm.voyageForm.cargoOwnerName
+            );
+          });
+          if (!b) {
+            voyageForm.voyageForm["cargoOwnerId"] = "";
+            voyageForm.voyageForm["cargoOwnerName"] = "";
+          }
+        }
 
 
-          case "dischargeProt": {
-            let index = ref(-1);
-            for (let i in colCache.value) {
-              if (
-                voyageForm.voyageForm.dischargeProt == colCache.value[i].value
-              ) {
-                index.value = i;
-                break;
-              }
-            }
-            if (index.value != -1) {
-              voyageForm.voyageForm.dischargeProtId =
-                colCache.value[index.value].key;
-            } else {
-              let b = colCache.value.some((item) => {
-                return (
-                  item.value == voyageForm.voyageForm.dischargeProt &&
-                  item.key == voyageForm.voyageForm.dischargeProtId
-                );
-              });
-              if (!b) {
-                voyageForm.voyageForm["dischargeProt"] = "";
-                voyageForm.voyageForm["dischargeProtId"] = "";
-              }
-            }
+        break;
+      }
+      case "loadPort": {
+        let index = ref(-1);
+        for (let i in colCache.value) {
+          if (voyageForm.voyageForm.loadPort == colCache.value[i].value) {
+            index.value = i;
             break;
             break;
           }
           }
         }
         }
-      }, 200);
-    }
-    let addVoyageForm = ref(null);
-
-    async function addVoyage() {
-      addVoyageForm.value.validate(async (valid) => {
-        if (valid) {
-          console.log("提交", voyageForm.voyageForm);
-          let res = await api.addVoyage({
-            ...voyageForm.voyageForm,
+        if (index.value != -1) {
+          voyageForm.voyageForm.loadPortId = colCache.value[index.value].key;
+        } else {
+          let b = colCache.value.some((item) => {
+            return (
+              item.value == voyageForm.voyageForm.loadPort &&
+              item.key == voyageForm.voyageForm.loadPortId
+            );
           });
           });
-          if (res.data.status == 0) {
-            ElNotification({
-              title: res.data.msg,
-              type: "success",
-            });
-            resetAddVoyageForm();
-            getVoyageList();
-          } else {
-            console.log(res);
-            ElNotification({
-              title: res.data.msg,
-              type: "error",
-            });
+          if (!b) {
+            voyageForm.voyageForm["loadPort"] = "";
+            voyageForm.voyageForm["loadPortId"] = "";
           }
           }
-        } else {
-          console.log("未提交", voyageForm.voyageForm);
         }
         }
-      });
-    }
 
 
-    let shipsCache = ref([]);
-    let colCache = ref([]);
-    let cargoOwnersCache = ref([]);
+        break;
+      }
 
 
-    const searchShip = _.debounce(
-      async (queryString, cb) => {
-        if (!queryString) return;
-        let res = await api.searchShip({
-          term: queryString,
-        });
-        let ships = [];
-        if (res.data.status == 0) {
-          ships = res.data.result;
-          for (let i of ships) {
-            i.value = `${i.shipName}`;
+      case "dischargeProt": {
+        let index = ref(-1);
+        for (let i in colCache.value) {
+          if (voyageForm.voyageForm.dischargeProt == colCache.value[i].value) {
+            index.value = i;
+            break;
           }
           }
-          shipsCache.value = ships;
-          cb(ships);
         }
         }
-      },
-      1000,
-      { leading: true }
-    );
-
-    const selectShip = (item) => {
-      voyageForm.voyageForm.shipId = item.shipId;
-    };
-
-    const searchCargoOwner = _.debounce(
-      async (queryString, cb) => {
-        if (!queryString) return;
-        let res = await api.searchUser({
-          term: queryString,
-          identity: 2,
-        });
-        let cargoOwners = [];
-        if (res.data.status == 0) {
-          cargoOwners = res.data.result;
-          for (let i of cargoOwners) {
-            i.value = `${i.userName}`;
+        if (index.value != -1) {
+          voyageForm.voyageForm.dischargeProtId =
+            colCache.value[index.value].key;
+        } else {
+          let b = colCache.value.some((item) => {
+            return (
+              item.value == voyageForm.voyageForm.dischargeProt &&
+              item.key == voyageForm.voyageForm.dischargeProtId
+            );
+          });
+          if (!b) {
+            voyageForm.voyageForm["dischargeProt"] = "";
+            voyageForm.voyageForm["dischargeProtId"] = "";
           }
           }
-          cargoOwnersCache.value = cargoOwners;
-          cb(cargoOwners);
         }
         }
-      },
-      1000,
-      { leading: true }
-    );
-
-    const selectCargoOwner = (item) => {
-      voyageForm.voyageForm.cargoOwnerId = item.userId;
-    };
+        break;
+      }
+    }
+  }, 200);
+}
+let addVoyageForm = ref(null);
 
 
-    const getCol = _.debounce(
-      async (queryString, cb) => {
-        if (!queryString) return;
-        let res = await api.getCol({
-          term: queryString,
+async function addVoyage() {
+  addVoyageForm.value.validate(async (valid) => {
+    if (valid) {
+      console.log("提交", voyageForm.voyageForm);
+      let res = await api.addVoyage({
+        ...voyageForm.voyageForm,
+      });
+      if (res.data.status == 0) {
+        ElNotification({
+          title: res.data.msg,
+          type: "success",
+        });
+        resetAddVoyageForm();
+        getVoyageList();
+      } else {
+        console.log(res);
+        ElNotification({
+          title: res.data.msg,
+          type: "error",
         });
         });
-        if (res.data.status == 0) {
-          colCache.value = [...colCache.value, ...res.data.result];
-          colCache.value = _.uniqBy(colCache.value, "key");
+      }
+    } else {
+      console.log("未提交", voyageForm.voyageForm);
+    }
+  });
+}
 
 
-          cb(res.data.result);
-        }
-      },
-      1000,
-      { leading: true }
-    );
+let shipsCache = ref([]);
+let colCache = ref([]);
+let cargoOwnersCache = ref([]);
 
 
-    const selectLoadPort = (item) => {
-      voyageForm.voyageForm.loadPortId = item.key;
-      voyageForm.voyageForm.loadPort = item.value;
-    };
+const searchShip = _.debounce(
+  async (queryString, cb) => {
+    if (!queryString) return;
+    let res = await api.searchShip({
+      term: queryString,
+    });
+    let ships = [];
+    if (res.data.status == 0) {
+      ships = res.data.result;
+      for (let i of ships) {
+        i.value = `${i.shipName}`;
+      }
+      shipsCache.value = ships;
+      cb(ships);
+    }
+  },
+  1000,
+  { leading: true }
+);
 
 
-    const selectDischargeProt = (item) => {
-      voyageForm.voyageForm.dischargeProtId = item.key;
-      voyageForm.voyageForm.dischargeProt = item.value;
-    };
+const selectShip = (item) => {
+  voyageForm.voyageForm.shipId = item.shipId;
+};
 
 
-    function resetAddVoyageForm() {
-      voyageAddDialogVisible.value = false;
-      addVoyageForm.value.resetFields();
+const searchCargoOwner = _.debounce(
+  async (queryString, cb) => {
+    if (!queryString) return;
+    let res = await api.searchUser({
+      term: queryString,
+      identity: 2,
+    });
+    let cargoOwners = [];
+    if (res.data.status == 0) {
+      cargoOwners = res.data.result;
+      for (let i of cargoOwners) {
+        i.value = `${i.userName}`;
+      }
+      cargoOwnersCache.value = cargoOwners;
+      cb(cargoOwners);
     }
     }
+  },
+  1000,
+  { leading: true }
+);
+
+const selectCargoOwner = (item) => {
+  voyageForm.voyageForm.cargoOwnerId = item.userId;
+};
 
 
-    let sortradio = ref(0);
-
-    getVoyageList();
-    onMounted(() => {});
-
-    return {
-      currentPage,
-      term,
-      tableData,
-      total,
-      status,
-      changeVoyageType,
-      getVoyageList,
-      voyageDetail,
-      pageChange,
-      goToVoyageAdd,
-      addVoyage,
-      voyageAddDialogVisible,
-      addVoyageForm,
-      ...toRefs(rules),
-      ...toRefs(voyageForm),
-      searchShip,
-      selectShip,
-      searchCargoOwner,
-      selectCargoOwner,
-      resetAddVoyageForm,
-      getCol,
-      selectLoadPort,
-      selectDischargeProt,
-      clear,
-      sortradio,
-    };
+const getCol = _.debounce(
+  async (queryString, cb) => {
+    if (!queryString) return;
+    let res = await api.getCol({
+      term: queryString,
+    });
+    if (res.data.status == 0) {
+      colCache.value = [...colCache.value, ...res.data.result];
+      colCache.value = _.uniqBy(colCache.value, "key");
+
+      cb(res.data.result);
+    }
   },
   },
+  1000,
+  { leading: true }
+);
+
+const selectLoadPort = (item) => {
+  voyageForm.voyageForm.loadPortId = item.key;
+  voyageForm.voyageForm.loadPort = item.value;
+};
+
+const selectDischargeProt = (item) => {
+  voyageForm.voyageForm.dischargeProtId = item.key;
+  voyageForm.voyageForm.dischargeProt = item.value;
 };
 };
+
+function resetAddVoyageForm() {
+  voyageAddDialogVisible.value = false;
+  addVoyageForm.value.resetFields();
+}
+
+let sortradio = ref(0);
+async function downloadFYDI() {
+  let res0 = await api.getFYFIDownloadUrl({
+    loginAccountId: localStorage.loginAccountId,
+  });
+
+  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 >= 20 && row.daysInPort < 25) {
+    rowStyle.color = "#f9ca24";
+  } else if (row.daysInPort >= 25 && row.daysInPort < 30) {
+    rowStyle.color = "#f0932b";
+  } else if (row.daysInPort >= 30) {
+    rowStyle.color = "#eb4d4b";
+  }
+
+  return rowStyle;
+}
+
+let voyageNameVisable = ref(true);
+let loadPortVisable = ref(true);
+let dischargePortVisable = ref(true);
+let expectedArrivalTimeVisable = ref(true);
+let abnormalStatusVisable = ref(true);
+let daysInPortVisable = ref(true);
+let todayPhotoCountVisable = ref(true);
+let cargoVisable = ref(true);
+let actualLoadTonsVisable = ref(true);
+let unloadedtonsVisable = ref(true);
+let remainTonsVisable = ref(true);
+let hasInsuranceVisable = ref(true);
+
+let selectAllVisable = ref(true);
+function selectAll(e) {
+  loadPortVisable.value = e;
+  dischargePortVisable.value = e;
+  expectedArrivalTimeVisable.value = e;
+  abnormalStatusVisable.value = e;
+  daysInPortVisable.value = e;
+  todayPhotoCountVisable.value = e;
+  cargoVisable.value = e;
+  actualLoadTonsVisable.value = e;
+  unloadedtonsVisable.value = e;
+  remainTonsVisable.value = e;
+  hasInsuranceVisable.value = e;
+}
+
+function selectSingle(e) {
+  if (!e) {
+    selectAllVisable.value = e;
+  }
+}
+
+let loadPortFilterStr = ref("");
+function selectLoadPortFilter(item) {
+  voyageListPostData.value.loadPortId = item.key;
+  getVoyageList();
+}
+let discPortFilterStr = ref("");
+function selectDiscPortFilter(item) {
+  voyageListPostData.value.discPortId = item.key;
+  getVoyageList();
+}
+let cargoFilterStr = ref("");
+function selectCargoFilter(item) {
+  voyageListPostData.value.cargo = item.key;
+  getVoyageList();
+}
+
+function resetFilter() {
+  loadPortFilterStr.value = "";
+  discPortFilterStr.value = "";
+  cargoFilterStr.value = "";
+  voyageListPostData.value = {};
+  getVoyageList();
+}
+let cargoOptions = ref([]);
+async function getCargoSelect() {
+  if (cargoOptions.value.length) return;
+  let res = await api.getCargoSelect({
+    loginAccountId: loginAccountId.value,
+    status: 2,
+    term: "",
+  });
+  cargoOptions.value = res.data.result;
+}
+
+let portOptions = ref([]);
+async function getPortSelect() {
+  if (portOptions.value.length) return;
+  let res = await api.getCol({
+    term: "",
+  });
+  portOptions.value = res.data.result;
+}
+
+onMounted(() => {
+  getVoyageList();
+  loginAccountId.value = localStorage.loginAccountId;
+});
 </script>
 </script>
 <style scoped>
 <style scoped>
 .search-btn {
 .search-btn {

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff