فهرست منبع

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

wzh 3 سال پیش
والد
کامیت
f5841d8ba3

+ 1 - 1
package.json

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

+ 3 - 3
src/apis/config.js

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

+ 80 - 10
src/apis/fetch.js

@@ -2,49 +2,119 @@ import { $http } from "./config";
 export default {
   // 货主登录
   staffLogin(data) {
-    return $http("post", "user/cargo/login", data);
+    return $http("user/cargo/login", data);
   },
   // 获取航次列表
   getVoyageList(data) {
-    return $http("post", "voyage/list", data);
+    return $http("voyage/list", data);
   },
 
   // 获取航次详情
   getVoyageDetail(data) {
-    return $http("post", "/voyage/detail", data);
+    return $http("/voyage/detail", data);
   },
 
   // 更新航次
   updateVoyage(data) {
-    return $http("post", "/voyage/backstage/update", data);
+    return $http("/voyage/backstage/update", data);
   },
 
   // 完成航次
   finishVoyage(data) {
-    return $http("post", "/voyage/backstage/finish", data);
+    return $http("/voyage/backstage/finish", data);
   },
 
   // 添加航次
   addVoyage(data) {
-    return $http("post", "voyage/backstage/add", data);
+    return $http("voyage/backstage/add", data);
   },
 
   // 获取媒体列表
   getMediaList(data) {
-    return $http("post", "/media/backstage/list", data);
+    return $http("/media/backstage/list", data);
   },
   // 导出excel
   exportExcel(data) {
-    return $http("post", "/voyage/exportExcel", data);
+    return $http("/voyage/exportExcel", data);
   },
 
   // 获取卸货列表
   getDischargeList(data) {
-    return $http("post", "/voyage/getDischargeList", data);
+    return $http("/voyage/getDischargeList", 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);
   },
 };

+ 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;
+  }
+};

+ 59 - 0
src/auth/menuData.js

@@ -0,0 +1,59 @@
+let menuData = [
+  {
+    icon: "el-icon-s-data",
+    title: "航次",
+    items: [
+      {
+        path: "/voyage/voyageList",
+        name: "航次列表",
+        code: "VOYAGELIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "代理管理",
+    items: [
+      {
+        path: "/agencyManage/agencyCompanyList",
+        name: "代理公司列表",
+        code: "PROXYLIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "货种管理",
+    items: [
+      {
+        path: "/cargoManage/cargoList",
+        name: "货种列表",
+        code: "CARGOLIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "账户管理",
+    items: [
+      {
+        path: "/accountManage/subAccountList",
+        name: "子账户列表",
+        code: "ACCOUNTLIST",
+      },
+    ],
+  },
+  {
+    icon: "el-icon-s-data",
+    title: "权限管理",
+    items: [
+      {
+        path: "/authManage/roleList",
+        name: "角色列表",
+        code: "ROLELIST",
+      },
+    ],
+  },
+];
+
+export default menuData;

+ 7 - 35
src/components/Aside.vue

@@ -7,48 +7,20 @@
     active-text-color="#ffd04b"
     :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>
         <i :class="item.icon"></i>
         <span>{{ item.title }}</span>
       </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 }}
       </el-menu-item>
     </el-sub-menu>
   </el-menu>
 </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>

+ 20 - 5
src/main.js

@@ -12,14 +12,23 @@ import Certs from "./components/Certs.vue";
 const app = createApp(App);
 app.component("Certs", Certs);
 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) => {
-  let userId = localStorage.userId;
-  if (userId) {
+  if (localStorage.userId) {
     store.commit("changeLogin", true);
+    let rolePermission = localStorage.rolePermission?.split(",") || [];
     if (0 === to.matched.length) {
       next("/voyage/voyageList");
     } else if (to.path == "/login" || to.path == "/") {
       next("/voyage/voyageList");
+    } else if (rolePermission?.indexOf(to.meta.code) == -1) {
+      next("/voyage/voyageList");
     } else {
       next();
     }
@@ -38,8 +47,14 @@ router.afterEach((to, from) => {
   store.commit("setCurrentMenuItem", to.path);
   store.commit("changefirstTitle", title);
 });
-app.config.globalProperties.check = () => {
-  console.log("check");
-};
+
+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).use(store).mount("#app");

+ 95 - 9
src/router/index.js

@@ -23,32 +23,118 @@ const router = createRouter({
       },
       component: Login,
     },
-    {
-      path: "/voyage/voyageAdd",
-      name: "voyageAdd",
-      meta: {
-        title: "添加航次",
-      },
-      component: () => import("../views/voyage/voyageAdd.vue"),
-    },
     {
       path: "/voyage/voyageDetail",
       name: "voyageDetail",
       meta: {
         title: "航次详情",
+        code: "VOYAGEDETAIL",
       },
       component: () => import("../views/voyage/voyageDetail.vue"),
     },
-
     {
       path: "/voyage/voyageList",
       name: "voyageList",
       meta: {
         title: "航次列表",
+        code: "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/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 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;

+ 63 - 1
src/store/index.js

@@ -1,5 +1,8 @@
 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);
 let baseurl = import.meta.env.VITE_BASEURL;
 
@@ -10,7 +13,12 @@ const store = createStore({
     secondTitle: "",
     currentMenuItem: "/cargoOwnerManage/cargoOwnerList",
     baseurl,
+    basePermissionData: [],
+    userPermission: [],
+    menuData: [],
+    baseParentNodes: [],
   },
+  getters: {},
   mutations: {
     changefirstTitle(state, text) {
       state.firstTitle = text;
@@ -24,6 +32,60 @@ const store = createStore({
     setCurrentMenuItem(state, 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);
+        });
+      });
+    },
+    GetUserPermission({ commit }, loginAccountId) {
+      return new Promise((resolve, reject) => {
+        api.getPermissionByUserId({ loginAccountId }).then((e) => {
+          let res = e.data.result || [];
+          let arr = [...new Set([...res, "VOYAGELIST"])];
+          commit("setUserPermissionData", arr);
+          localStorage.setItem("rolePermission", arr);
+          let data = [];
+          for (let i of menuData) {
+            if (i.title == "航次") {
+              data.push(i);
+            } else {
+              if (arr.indexOf(i.items[0].code) != -1) {
+                data.push(i);
+              }
+            }
+          }
+          commit("setMenuData", data);
+        });
+      });
+    },
   },
 });
 

+ 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 };

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

@@ -0,0 +1,290 @@
+<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"
+        ></el-input>
+        <div class="seach-btn" @click="getSubAccountList">查询</div>
+      </div>
+      <el-button v-auth="'ADDACCOUNT'" type="primary" @click="visable = true"
+        >添加账号</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="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="role_name"
+          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)"
+                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 accountId = ref("");
+let ruleForm = ref({
+  name: "",
+  phone: "",
+  roleId: "",
+});
+const rules = reactive({
+  name: [
+    {
+      required: true,
+      message: "请填写名称",
+      trigger: "blur",
+    },
+  ],
+  phone: [
+    {
+      required: true,
+      message: "请填写手机号",
+      trigger: "blur",
+    },
+  ],
+  roleId: [
+    {
+      required: true,
+      message: "请选择角色",
+      trigger: "blur",
+    },
+  ],
+});
+
+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
+  );
+  let status = res.data.status == 0;
+  ElNotification({
+    title: status ? "成功" : "失败",
+    duration: 1500,
+    message: res.data.msg,
+    type: status ? "success" : "error",
+  });
+  resetForm();
+  getSubAccountList();
+}
+function resetForm() {
+  visable.value = false;
+  accountId.value = "";
+  form.value.resetFields();
+}
+
+function showUpdateModal(item) {
+  visable.value = true;
+  accountId.value = item.id;
+  ruleForm.value = {
+    name: item.userName,
+    phone: item.phone,
+    roleId: item.roleId,
+  };
+}
+
+let roleSelect = ref([]);
+
+async function getRoleSelect() {
+  let res = await api.getRoleSelect({
+    loginAccountId: loginAccountId.value,
+  });
+  roleSelect.value = res.data.result;
+}
+
+onMounted(() => {
+  loginAccountId.value = localStorage.loginAccountId;
+  getSubAccountList();
+  getRoleSelect();
+});
+</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>

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

@@ -0,0 +1,237 @@
+<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"
+        ></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>

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

@@ -0,0 +1,228 @@
+<template>
+  <el-card>
+    <el-form
+      :model="ruleForm"
+      :rules="rules"
+      ref="form"
+      label-width="110px"
+      label-position="left"
+    >
+      <el-form-item prop="roleCode" label="角色代码">
+        <el-input style="width: 280px" v-model="ruleForm.roleCode"></el-input>
+      </el-form-item>
+      <el-form-item prop="roleName" label="角色名称">
+        <el-input style="width: 280px" v-model="ruleForm.roleName"></el-input>
+      </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>
+
+  <!-- <el-card class="mt30">
+    <h4>权限设置</h4>
+    <div v-for="(item, index) in basePermissionData" :key="item" class="mt30">
+      <div class="df aic mb20">
+        <div class="mr20">{{ item.label }}</div>
+        <el-switch
+          v-model="item.status"
+          inline-prompt
+          active-text="启用"
+          :active-value="true"
+          :inactive-value="false"
+          inactive-text="禁用"
+          @change="change($event, item.code, index)"
+        />
+      </div>
+      <div class="ml30" v-if="index == 0">
+        <div class="mb10" v-for="(item1, index1) in item.children">
+          <div class="df aic mb10">
+            <div class="mr20">{{ item1.label }}</div>
+            <el-switch
+              v-model="item1.status"
+              inline-prompt
+              active-text="启用"
+              :active-value="true"
+              :inactive-value="false"
+              inactive-text="禁用"
+              @change="change($event, item1.code, index, index1)"
+            />
+          </div>
+          <div class="df aic">
+            <div class="mr20" v-for="(item2, index2) in item1.children">
+              {{ item2.label }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <div v-else class="df aic ml30">
+        <div class="mr20" v-for="(item1, index1) in item.children">
+          {{ item1.label }}
+        </div>
+      </div>
+    </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({
+  roleCode: "",
+  roleName: "",
+});
+const rules = reactive({
+  roleCode: [
+    {
+      required: true,
+      message: "请填写角色代码",
+      trigger: "blur",
+    },
+  ],
+  roleName: [
+    {
+      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, roleName, permission, cargoLimit, proxyLimit } =
+      res.data.result;
+    ruleForm.value = {
+      roleCode: code,
+      roleName,
+    };
+    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",
+    });
+    router.replace("/authManage/roleList");
+  } else {
+    console.log(res);
+    ElNotification({
+      title: "失败",
+      duration: 1500,
+      message: res.data.msg,
+      type: "error",
+    });
+  }
+}
+
+onMounted(() => {
+  loginAccountId.value = 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>

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

@@ -0,0 +1,146 @@
+<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"
+        ></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="code"
+          label="角色代码"
+          min-width="100"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="roleName"
+          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>

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

@@ -0,0 +1,163 @@
+<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"
+        ></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>

+ 17 - 1
src/views/index/Login.vue

@@ -123,13 +123,29 @@ export default {
               message: res.data.msg,
               type: "success",
             });
-            let { userId, userName, phone, contactName } = res.data.result;
+            let {
+              userId,
+              userName,
+              phone,
+              contactName,
+              loginAccountId,
+              rolePermission,
+            } = res.data.result;
             localStorage.setItem("userId", userId);
             localStorage.setItem("userName", userName);
             localStorage.setItem("phone", phone);
             localStorage.setItem("contactName", contactName);
             localStorage.setItem("userType", 1);
+            localStorage.setItem("loginAccountId", loginAccountId);
+            rolePermission = rolePermission || [];
+            let arr = [...new Set([...rolePermission, "VOYAGELIST"])];
+            localStorage.setItem("rolePermission", arr);
             store.commit("changeLogin", true);
+            store.dispatch(
+              "GetBasePermissionData",
+              localStorage.loginAccountId
+            );
+            store.dispatch("GetUserPermission", localStorage.loginAccountId);
             router.replace({ path: "/voyage/voyageList" });
           } else {
             ElNotification.error({

+ 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>

+ 1 - 0
src/views/voyage/voyageDetail.vue

@@ -338,6 +338,7 @@
     <div class="container-second-title df aic jcsb">
       <div>卸货记录</div>
       <el-button
+        v-auth="'DOWNLOADDISCHARGE'"
         @click="exportDischargeExcel"
         style="width: 220px; margin-right: 20px"
         type="primary"

+ 304 - 334
src/views/voyage/voyageList.vue

@@ -244,14 +244,23 @@
         label="创建时间"
         min-width="100"
         align="center"
-      ></el-table-column>
+      >
+        <template v-slot="scope">
+          {{ subTimeStr(scope.row.createTime) }}
+        </template>
+      </el-table-column>
       <el-table-column
         prop="remark"
         label="备注"
         min-width="100"
         align="center"
       ></el-table-column>
-      <el-table-column label="操作" min-width="80" align="center">
+      <el-table-column
+        v-auth="'VOYAGEDETAIL'"
+        label="操作"
+        min-width="80"
+        align="center"
+      >
         <template v-slot="scope">
           <el-button
             @click="voyageDetail(scope.row.id, tableData)"
@@ -273,7 +282,7 @@
     </div>
   </div>
 </template>
-<script>
+<script setup>
 import { ref, h, reactive, toRefs, onMounted } from "vue";
 import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
 import store from "../../store";
@@ -281,363 +290,324 @@ import router from "../../router";
 import md5 from "md5";
 import api from "../../apis/fetch";
 import _ from "lodash";
-
-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();
-    }
-
-    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: "",
-      },
+import { subTimeStr } from "../../utils/utils";
+
+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({
+    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"] = "";
-            }
+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;
           }
-          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"] = "";
-              }
-            }
-
+        }
+        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;
           }
-          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"] = "";
-              }
-            }
-
-            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;
           }
         }
-      }, 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;
-    };
-
-    const getCol = _.debounce(
-      async (queryString, cb) => {
-        if (!queryString) return;
-        let res = await api.getCol({
-          term: queryString,
+        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 (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 }
-    );
-
-    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 shipsCache = ref([]);
+let colCache = ref([]);
+let cargoOwnersCache = ref([]);
+
+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 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}`;
+      }
+      cargoOwnersCache.value = cargoOwners;
+      cb(cargoOwners);
     }
+  },
+  1000,
+  { leading: true }
+);
 
-    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 selectCargoOwner = (item) => {
+  voyageForm.voyageForm.cargoOwnerId = item.userId;
+};
+
+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);
+
+onMounted(() => {
+  getVoyageList();
+});
 </script>
 <style scoped>
 .search-btn {