Bladeren bron

初始化 项目

wangzhihui 4 jaren geleden
bovenliggende
commit
18e4d46f3d

+ 6 - 3
README.md

@@ -1,4 +1,7 @@
-# JiangYunPhotosCargoOwner_WebApp
+# Vue 3 + Vite
 
-江运随手拍-货主前端
-
+This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+## Recommended IDE Setup
+
+- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)

+ 33 - 0
index.html

@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/src/assets/logo.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <script
+      charset="utf-8"
+      src="https://map.qq.com/api/gljs?v=1.exp&key=Y2BBZ-IHRKU-V42VO-BFQEE-K7252-ZBBSF"
+    ></script>
+    <title>Huihenduo App</title>
+    <style>
+      * {
+        margin: 0;
+        padding: 0;
+      }
+      html,
+      body {
+        height: 100%;
+        width: 100%;
+      }
+
+      #app {
+        height: 100%;
+        width: 100%;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 24 - 0
package.json

@@ -0,0 +1,24 @@
+{
+  "name": "jiangyunphotosagent",
+  "version": "0.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "serve": "vite preview"
+  },
+  "dependencies": {
+    "axios": "^0.21.1",
+    "element-plus": "^1.1.0-beta.24",
+    "element3": "^0.0.40",
+    "lodash": "^4.17.21",
+    "md5": "^2.3.0",
+    "vite-plugin-compression": "^0.3.5",
+    "vue": "^3.2.16",
+    "vue-router": "^4.0.4",
+    "vuex": "^4.0.2"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^1.9.3",
+    "vite": "^2.6.4"
+  }
+}

BIN
public/favicon.ico


+ 174 - 0
src/App.vue

@@ -0,0 +1,174 @@
+<template>
+  <div v-if="this.$store.state.isLogin" class="main-container">
+    <HeaderVue class="header"></HeaderVue>
+    <div class="main-app">
+      <div class="aside"><AsideVue></AsideVue></div>
+      <div class="section">
+        <div class="first-title">
+          {{ this.$store.state.firstTitle }}
+        </div>
+        <div class="main-section"><router-view></router-view></div>
+      </div>
+    </div>
+    <FooterVue></FooterVue>
+  </div>
+  <router-view v-else></router-view>
+</template>
+
+<script>
+import HeaderVue from "./components/Header.vue";
+import AsideVue from "./components/Aside.vue";
+import FooterVue from "./components/Footer.vue";
+export default {
+  components: {
+    HeaderVue,
+    AsideVue,
+    FooterVue,
+  },
+  data() {
+    return {};
+  },
+};
+</script>
+<style>
+.main-container {
+  height: 100%;
+  width: 100%;
+  min-height: 800px;
+  min-width: 1200px;
+}
+
+.aside {
+  width: 220px;
+}
+
+.footer {
+  text-align: center;
+}
+.main-app {
+  width: 100%;
+  height: calc(100% - 120px);
+  display: flex;
+}
+
+.section {
+  width: 100%;
+  background: #f4f5f6;
+  overflow: auto;
+}
+
+.first-title {
+  height: 52px;
+  line-height: 52px;
+  width: 100%;
+  box-sizing: border-box;
+  background: #fff;
+  font-size: 18px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #333d43;
+  padding-left: 20px;
+}
+
+.main-section {
+  margin: 24px 0 0 24px;
+  height: calc(100% - 76px);
+  overflow: scroll;
+}
+
+.line-container-p18 {
+  padding: 18px;
+  background: #fff;
+}
+.line-container-p24 {
+  padding: 24px;
+  background: #fff;
+}
+
+.full-container-p24 {
+  padding: 24px;
+  height: calc(100% - 48px);
+  background: #fff;
+}
+
+.df {
+  display: flex;
+}
+
+.ffw {
+  flex-flow: wrap;
+}
+
+.jcsa {
+  justify-content: space-around;
+}
+.jcsb {
+  justify-content: space-between;
+}
+
+.aic {
+  align-items: center;
+}
+
+.dib {
+  display: inline-block;
+}
+
+.ml8 {
+  margin-left: 8px;
+}
+
+.jcfe {
+  justify-content: flex-end;
+}
+
+.mt20 {
+  margin-top: 20px;
+}
+
+.container-title {
+  font-size: 18px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #0094fe;
+  margin: 15px 0 15px 0;
+}
+
+.line {
+  display: flex;
+  align-items: center;
+  align-content: flex-start;
+  margin: 20px;
+}
+
+.info-line {
+  display: flex;
+  align-items: center;
+  align-content: flex-start;
+  margin-right: 20px;
+}
+
+.info-line-title {
+  width: 100px;
+  height: 100%;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #353a42;
+  line-height: 100%;
+  text-align: right;
+  padding-right: 20px;
+}
+
+.info-line-text {
+  width: 240px !important;
+  height: 100%;
+  line-height: 100%;
+}
+
+.info-line-textarea {
+  width: 620px;
+  height: 100%;
+  line-height: 100%;
+}
+</style>

+ 21 - 0
src/apis/config.js

@@ -0,0 +1,21 @@
+const baseurl = "http://49.234.214.168:8080/hhd-pat/";
+import axios from "axios";
+
+axios.interceptors.response.use(
+  function (response) {
+    return response;
+  },
+  function (error) {
+    return Promise.reject(error);
+  }
+);
+export const $http = function (method, url, data) {
+  return axios({
+    method,
+    url: baseurl + url,
+    data,
+    withCredentials: true,
+  });
+};
+
+export default { baseurl, $http };

+ 36 - 0
src/apis/fetch.js

@@ -0,0 +1,36 @@
+import { $http } from "./config";
+export default {
+  // 货主登录
+  staffLogin(data) {
+    return $http("post", "user/cargo/login", data);
+  },
+  // 获取航次列表
+  getVoyageList(data) {
+    return $http("post", "voyage/list", data);
+  },
+
+  // 获取航次详情
+  getVoyageDetail(data) {
+    return $http("post", "/voyage/detail", data);
+  },
+
+  // 更新航次
+  updateVoyage(data) {
+    return $http("post", "/voyage/backstage/update", data);
+  },
+
+  // 完成航次
+  finishVoyage(data) {
+    return $http("post", "/voyage/backstage/finish", data);
+  },
+
+  // 添加航次
+  addVoyage(data) {
+    return $http("post", "voyage/backstage/add", data);
+  },
+
+  // 获取媒体列表
+  getMediaList(data) {
+    return $http("post", "/media/backstage/list", data);
+  },
+};

BIN
src/assets/icon-player.png


BIN
src/assets/login-back.png


BIN
src/assets/login-modal.png


BIN
src/assets/logo.png


BIN
src/assets/ship-red-icon.png


BIN
src/assets/three.png


BIN
src/assets/user.png


BIN
src/assets/white-logo.png


+ 54 - 0
src/components/Aside.vue

@@ -0,0 +1,54 @@
+<template>
+  <el-menu
+    :default-active="this.$store.state.currentMenuItem"
+    style="width: 220px; height: 100%"
+    background-color="#141B29"
+    text-color="#fff"
+    active-text-color="#ffd04b"
+    :router="true"
+  >
+    <el-sub-menu v-for="(item, index) in menu" :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)"
+      >
+        {{ 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>

+ 37 - 0
src/components/Footer.vue

@@ -0,0 +1,37 @@
+<template>
+  <div class="copyright">
+    <div class="in" @click="goBeian">
+      Copyright © 2021 河南省汇很多科技有限公司 豫ICP备 2021029101 号
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  setup() {
+    function goBeian() {
+      window.open("https://beian.miit.gov.cn/");
+    }
+    return {
+      goBeian,
+    };
+  },
+};
+</script>
+<style scoped>
+.copyright {
+  width: 100%;
+  height: 60px;
+  background: #b3c0d1;
+}
+
+.in {
+  width: 520px;
+  margin: 0 auto;
+  line-height: 60px;
+  font-family: PingFang SC;
+  font-weight: 400;
+  color: #333333;
+  opacity: 0.8;
+  cursor: pointer;
+}
+</style>

+ 92 - 0
src/components/Header.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="header">
+    <div class="left">
+      <img class="first" src="../assets/three.png" alt="" />
+      <div class="shu"></div>
+      <img class="logo" src="../assets/white-logo.png" alt="" />
+      <div class="title">江运随手拍</div>
+    </div>
+    <div class="right">
+      <img class="user-icon" src="../assets/user.png" alt="" />
+      <div class="user">{{ userName }}</div>
+      <div class="quit" @click="quit">[退出]</div>
+    </div>
+  </div>
+</template>
+<script>
+import store from "../store";
+import router from "../router";
+
+export default {
+  setup() {
+    let userName = localStorage.userName;
+    function quit() {
+      localStorage.clear();
+      store.commit("changeLogin", false);
+      router.push({ path: "/login" });
+    }
+    return {
+      quit,
+      userName,
+    };
+  },
+};
+</script>
+<style scoped>
+.header {
+  width: 100%;
+  height: 60px;
+  background: #212029;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.left,
+.right {
+  display: flex;
+  align-items: center;
+}
+
+.first {
+  width: 22px;
+  height: 20px;
+  margin: 0 22px;
+}
+
+.shu {
+  width: 1px;
+  height: 60px;
+  background: #101015;
+  margin-right: 22px;
+}
+
+.logo {
+  width: 120px;
+  height: 40px;
+}
+
+.title {
+  font-size: 21px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  margin-left: 26px;
+}
+
+.user-icon {
+  width: 18px;
+  height: 18px;
+  margin-right: 16px;
+}
+
+.user,
+.quit {
+  font-size: 14px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #ffffff;
+  cursor: pointer;
+  margin-right: 16px;
+}
+</style>

+ 1 - 0
src/components/Table.vue

@@ -0,0 +1 @@
+<template></template>

BIN
src/images/顺发999.png


+ 41 - 0
src/main.js

@@ -0,0 +1,41 @@
+import { createApp } from "vue";
+import ElementPlus from "element-plus";
+import "element-plus/dist/index.css";
+import App from "./App.vue";
+import router from "./router";
+import store from "./store";
+import md5 from "md5";
+
+const app = createApp(App);
+
+router.beforeEach(async (to, from, next) => {
+  let id = localStorage.id;
+  if (id) {
+    store.commit("changeLogin", true);
+    if (0 === to.matched.length) {
+      next("/voyage/voyageList");
+    } else if (to.path == "/login" || to.path == "/") {
+      next("/voyage/voyageList");
+    } else {
+      next();
+    }
+  } else {
+    store.commit("changeLogin", false);
+    if (to.path == "/login") {
+      next();
+    } else {
+      next("/login");
+    }
+  }
+});
+router.afterEach((to, from) => {
+  let { title } = to.meta;
+  document.title = title;
+  store.commit("setCurrentMenuItem", to.path);
+  store.commit("changefirstTitle", title);
+});
+app.config.globalProperties.check = () => {
+  console.log("check");
+};
+
+app.use(router).use(ElementPlus).use(store).mount("#app");

+ 52 - 0
src/router/index.js

@@ -0,0 +1,52 @@
+import {
+  createWebHistory,
+  createWebHashHistory,
+  createMemoryHistory,
+  createRouter,
+} from "vue-router";
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes: [
+    {
+      path: "/",
+      name: "index",
+      component: () => import("../views/index/Index.vue"),
+    },
+    {
+      path: "/login",
+      name: "login",
+      meta: {
+        title: "登录",
+      },
+      component: () => import("../views/index/Login.vue"),
+    },
+    {
+      path: "/voyage/voyageAdd",
+      name: "voyageAdd",
+      meta: {
+        title: "添加航次",
+      },
+      component: () => import("../views/voyage/voyageAdd.vue"),
+    },
+    {
+      path: "/voyage/voyageDetail",
+      name: "voyageDetail",
+      meta: {
+        title: "航次详情",
+      },
+      component: () => import("../views/voyage/voyageDetail.vue"),
+    },
+
+    {
+      path: "/voyage/voyageList",
+      name: "voyageList",
+      meta: {
+        title: "航次列表",
+      },
+      component: () => import("../views/voyage/voyageList.vue"),
+    },
+  ],
+});
+
+export default router;

+ 26 - 0
src/store/index.js

@@ -0,0 +1,26 @@
+import { createStore } from "vuex";
+
+const store = createStore({
+  state: {
+    isLogin: false,
+    firstTitle: "",
+    secondTitle: "",
+    currentMenuItem: "/cargoOwnerManage/cargoOwnerList",
+  },
+  mutations: {
+    changefirstTitle(state, text) {
+      state.firstTitle = text;
+    },
+    changeTitleSecond(state, text) {
+      state.secondTitle = text;
+    },
+    changeLogin(state, b) {
+      state.isLogin = b;
+    },
+    setCurrentMenuItem(state, index) {
+      state.currentMenuItem = index;
+    },
+  },
+});
+
+export default store;

+ 17 - 0
src/views/index/Index.vue

@@ -0,0 +1,17 @@
+<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() {},
+};
+</script>

+ 228 - 0
src/views/index/Login.vue

@@ -0,0 +1,228 @@
+<template>
+  <div class="container">
+    <div class="login-box">
+      <div class="left">
+        <div class="left-up-icon"></div>
+      </div>
+      <div class="right">
+        <div class="title">
+          <div class="title-left"></div>
+          <div class="title-mid">丨</div>
+          <div class="title-right">智慧运力运维平台</div>
+        </div>
+        <div class="form-container">
+          <el-form :model="ruleForm" :rules="rules" ref="form">
+            <el-form-item prop="phone">
+              <el-input placeholder="请输入手机号" v-model="ruleForm.phone">
+                <template v-slot:prepend>
+                  <el-button icon="el-icon-mobile-phone"></el-button>
+                </template>
+              </el-input>
+            </el-form-item>
+            <el-form-item prop="password">
+              <el-input
+                type="password"
+                placeholder="请输入密码"
+                v-model="ruleForm.password"
+              >
+                <template v-slot:prepend>
+                  <el-button icon="el-icon-lock"></el-button>
+                </template>
+              </el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button
+                style="
+                  width: 384px;
+                  height: 48px;
+                  border-radius: 2px;
+                  margin-top: 40px;
+                "
+                type="primary"
+                @click="login('ruleForm')"
+              >
+                登录
+              </el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+    </div>
+
+    <div @click="goBeian" class="copyright">
+      Copyright © 2021 河南省汇很多科技有限公司 豫ICP备 2021029101 号
+    </div>
+  </div>
+</template>
+<script>
+import { ref, reactive, toRefs } from "vue";
+import { ElNotification } from "element-plus";
+import store from "../../store";
+import router from "../../router";
+
+import md5 from "md5";
+import api from "../../apis/fetch";
+
+export default {
+  setup() {
+    const form = ref(null);
+    const ruleForm = reactive({
+      ruleForm: {
+        phone: "",
+        password: "",
+      },
+    });
+
+    const rules = reactive({
+      rules: {
+        phone: [
+          { required: true, message: "请输入手机号", trigger: "blur" },
+          { min: 11, max: 11, message: "请正确输入手机号", trigger: "blur" },
+        ],
+        password: [
+          { required: true, message: "请输入密码", trigger: "blur" },
+          { min: 6, max: 20, message: "请正确输入手机号", trigger: "blur" },
+        ],
+      },
+    });
+    function check() {
+      // form.value.validate((valid) => {});
+    }
+    function login() {
+      form.value.validate(async (valid) => {
+        if (valid) {
+          let { phone, password } = ruleForm.ruleForm;
+          let res = await api.staffLogin({
+            phone,
+            password: md5(password).toUpperCase(),
+          });
+          if (res.data.status == 0) {
+            ElNotification.success({
+              title: "成功",
+              duration: 2000,
+              message: res.data.msg,
+              type: "success",
+            });
+            let { userId, userName, phone, contactName } = res.data.result;
+            localStorage.setItem("userId", userId);
+            localStorage.setItem("userName", userName);
+            localStorage.setItem("phone", phone);
+            localStorage.setItem("contactName", contactName);
+            store.commit("changeLogin", true);
+            router.replace({ path: "/cargoOwnerManage/cargoOwnerList" });
+          } else {
+            ElNotification.error({
+              title: "错误",
+              duration: 3000,
+              message: res.data.msg,
+            });
+          }
+        } else {
+          console.log("error submit!!");
+          return false;
+        }
+      });
+    }
+
+    function goBeian() {
+      window.open("https://beian.miit.gov.cn/");
+    }
+
+    return {
+      form,
+      ...toRefs(ruleForm),
+      ...toRefs(rules),
+      login,
+      goBeian,
+    };
+  },
+};
+</script>
+<style scoped>
+.container {
+  width: 100%;
+  height: 100%;
+  background-image: url(../../assets/login-back.png);
+  background-size: cover;
+}
+
+.login-box {
+  position: relative;
+  display: flex;
+  top: calc(50% - 255px);
+  left: calc(50% - 478px);
+  width: 966px;
+  height: 508px;
+  border-radius: 10px;
+  overflow: hidden;
+}
+
+.left {
+  height: 100%;
+  width: 450px;
+  background-image: url(../../assets/login-modal.png);
+}
+
+.left-up-icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  margin: 24px;
+  background-image: url(../../assets/logo.png);
+  background-size: contain;
+}
+
+.right {
+  height: 100%;
+  width: 516px;
+  background: #fff;
+}
+
+.title {
+  width: 384px;
+  height: 38px;
+  display: flex;
+  margin: 0 auto;
+  margin-top: 100px;
+}
+
+.title-left {
+  height: 38px;
+  width: 105px;
+  background: url(https://6875-huihenduo-2gx127w7f837b584-1255802371.tcb.qcloud.la/miniapp-static/%E6%B1%87%E5%BE%88%E5%A4%9Alogo-%E5%B7%A6%E5%8F%B3.png?sign=22b9335300bbef8d04da1b9b75589f7e&t=1634706935);
+  background-size: contain;
+  background-repeat: no-repeat;
+}
+
+.title-mid {
+  font-size: 25px;
+  color: #e4e4e4;
+  margin: 0 12px;
+}
+
+.title-right {
+  font-size: 28px;
+  font-family: Adobe Heiti Std;
+  font-weight: normal;
+  color: #434343;
+  line-height: 38px;
+}
+
+.form-container {
+  margin: 0 auto;
+  margin-top: 60px;
+  width: 384px;
+}
+
+.copyright {
+  position: absolute;
+  width: 520px;
+  bottom: 70px;
+  left: calc(50% - 250px);
+  font-family: PingFang SC;
+  font-weight: 400;
+  color: #aaa;
+  opacity: 0.8;
+  cursor: pointer;
+}
+</style>

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

@@ -0,0 +1,117 @@
+<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>

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

@@ -0,0 +1,475 @@
+<template>
+  <div class="line-container-p18">
+    <i class="el-icon-arrow-left"></i>
+    <div class="dib go-back ml8">返回航次列表</div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">航次名称</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.voyageName"
+          disabled
+        ></el-input>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">货主</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.cargoOwnerName"
+          disabled
+        ></el-input>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">船东</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.shipOwnerName"
+          disabled
+        ></el-input>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">船东手机号</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.shipOwnerPhone"
+          disabled
+        ></el-input>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">船舶</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.shipName"
+          disabled
+        ></el-input>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">MMSI</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.shipMmsi"
+          disabled
+        ></el-input>
+      </div>
+    </div>
+    <div id="map-container" class="map-container"></div>
+    <div class="line" style="margin-top: 30px">
+      <div class="info-line">
+        <div class="info-line-title">开始时间</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.startTime"
+          disabled
+        ></el-input>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">结束时间</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.endTime"
+          disabled
+        ></el-input>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">装货港</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.loadPort"
+          disabled
+        ></el-input>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">卸货港</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.dischargeProt"
+          disabled
+        ></el-input>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">货种</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.cargo"
+          disabled
+        ></el-input>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">吨位</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.tons"
+          disabled
+        ></el-input>
+      </div>
+    </div>
+  </div>
+  <div class="container-title">航次信息</div>
+  <div class="line-container-p18">
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">运输状态</div>
+        <el-select
+          v-model="voyage.transStatus"
+          placeholder="Select"
+          class="info-line-text"
+          :disabled="disabledStatus"
+        >
+          <el-option
+            v-for="item in options"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          ></el-option>
+        </el-select>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">实际卸货吨位</div>
+        <el-input
+          class="info-line-text"
+          v-model="voyage.actualDischargeTons"
+          :disabled="disabledStatus"
+        ></el-input>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">装货开始时间</div>
+        <el-date-picker
+          class="info-line-text"
+          v-model="voyage.loadStartTime"
+          type="datetime"
+          format="YYYY/MM/DD HH:mm:ss"
+          value-format="YYYY/MM/DD HH:mm:ss"
+          placeholder="装货开始时间"
+          :disabled="disabledStatus"
+        ></el-date-picker>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">装货结束时间</div>
+        <el-date-picker
+          class="info-line-text"
+          v-model="voyage.loadEndTime"
+          type="datetime"
+          format="YYYY/MM/DD HH:mm:ss"
+          value-format="YYYY/MM/DD HH:mm:ss"
+          placeholder="装货开始时间"
+          :disabled="disabledStatus"
+        ></el-date-picker>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">卸货开始时间</div>
+        <el-date-picker
+          class="info-line-text"
+          v-model="voyage.dischargeStartTime"
+          type="datetime"
+          format="YYYY/MM/DD HH:mm:ss"
+          value-format="YYYY/MM/DD HH:mm:ss"
+          placeholder="装货开始时间"
+          :disabled="disabledStatus"
+        ></el-date-picker>
+      </div>
+      <div class="info-line">
+        <div class="info-line-title">卸货结束时间</div>
+        <el-date-picker
+          class="info-line-text"
+          v-model="voyage.dischargeEndTime"
+          type="datetime"
+          format="YYYY/MM/DD HH:mm:ss"
+          value-format="YYYY/MM/DD HH:mm:ss"
+          placeholder="装货开始时间"
+          :disabled="disabledStatus"
+        ></el-date-picker>
+      </div>
+    </div>
+    <div class="line">
+      <div class="info-line">
+        <div class="info-line-title">备注</div>
+        <el-input
+          class="info-line-textarea"
+          v-model="voyage.remark"
+          autosize
+          type="textarea"
+          :disabled="disabledStatus"
+        ></el-input>
+      </div>
+    </div>
+    <div
+      class="media-content df ffw"
+      style="width: 100%; background: #f7f7f7; border-radius: 2px"
+    >
+      <el-card
+        style="
+          width: 240px;
+          height: 360px;
+          margin-left: 20px;
+          margin-bottom: 15px;
+        "
+        v-for="item in medias"
+        :key="item"
+        shadow="hover"
+      >
+        <div class="card-note">
+          {{ item.note }}
+        </div>
+        <div class="media-box" style="position: relative">
+          <el-image
+            v-if="item.mediaType == 1"
+            style="width: 100%; height: 100%"
+            fit="contain"
+            :src="item.downloadUrl"
+            :preview-src-list="previewSrcList"
+          ></el-image>
+          <video
+            style="width: 100%; height: 100%"
+            v-else
+            :src="item.downloadUrl"
+          ></video>
+          <img
+            @click="openVideoModal(item.downloadUrl)"
+            v-if="item.mediaType == 2"
+            src="../../assets/icon-player.png"
+            style="
+              object-fit: contain;
+              width: 40px;
+              height: 40px;
+              position: absolute;
+              top: calc(50% - 20px);
+              left: calc(50% - 20px);
+              background: #fff;
+              border-radius: 50%;
+            "
+            alt=""
+          />
+        </div>
+      </el-card>
+
+      <el-dialog
+        v-model="videoModal"
+        title="视频审核"
+        width="30%"
+        :before-close="videoClose"
+      >
+        <video
+          autoplay
+          controls
+          style="width: 100%; height: 100%"
+          :src="currentUrl"
+        ></video>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+<script>
+import { onMounted, reactive, ref, toRefs } from "_vue@3.2.20@vue";
+import api from "../../apis/fetch";
+import { useRoute } from "vue-router";
+import _ from "lodash";
+import { ElNotification } from "element-plus";
+
+export default {
+  setup() {
+    const route = useRoute();
+    let map = ref();
+    let voyage = ref({});
+    let medias = ref();
+    let coordinates = ref();
+    let previewSrcList = ref([]);
+
+    async function getVoyageDetail() {
+      let res = await api.getVoyageDetail({
+        type: 2,
+        voyageId: route.query.id,
+      });
+
+      coordinates.value = res.data.result.coordinates;
+      voyage.value = res.data.result.voyage;
+      medias.value = res.data.result.medias;
+      for (let i of medias.value) {
+        previewSrcList.value.push(i.downloadUrl);
+      }
+      setShipMarker();
+    }
+
+    function initMap() {
+      var center = new TMap.LatLng(31.228721, 121.524761);
+      //初始化地图
+      map.value = new TMap.Map("map-container", {
+        zoom: 12, //设置地图缩放级别
+        center: center, //设置地图中心点坐标
+      });
+    }
+
+    function setShipMarker(longitude, latitude) {
+      var marker = new TMap.MultiMarker({
+        id: "marker-layer", //图层id
+        map: map.value,
+        styles: {
+          //点标注的相关样式
+          marker: new TMap.MarkerStyle({
+            width: 25,
+            height: 35,
+            anchor: { x: 16, y: 32 },
+            src: "https://hhd-pat-1255802371.cos.ap-shanghai.myqcloud.com/frontend/ship-red-icon.png",
+          }),
+        },
+        geometries: [
+          {
+            //点标注数据数组
+            id: "demo",
+            styleId: "marker",
+            position: new TMap.LatLng(31.228721, 121.524761),
+            properties: {
+              title: "marker",
+            },
+          },
+        ],
+      });
+    }
+
+    let disabledStatus = ref(true);
+    let updateCache = {};
+    function changeVoyageInfo() {
+      updateCache = _.cloneDeep(voyage.value);
+      disabledStatus.value = false;
+    }
+    function cancelVoyageChange() {
+      voyage.value = updateCache;
+      disabledStatus.value = true;
+    }
+    async function submitVoyageChange() {
+      let {
+        id,
+        transStatus,
+        loadStartTime,
+        loadEndTime,
+        dischargeStartTime,
+        dischargeEndTime,
+        actualDischargeTons,
+        remark,
+      } = voyage.value;
+      let res = await api.updateVoyage({
+        id,
+        transStatus,
+        loadStartTime,
+        loadEndTime,
+        dischargeStartTime,
+        dischargeEndTime,
+        actualDischargeTons,
+        remark,
+      });
+      if (res.data.status == 0) {
+        ElNotification({
+          type: "success",
+          title: res.data.msg,
+        });
+        disabledStatus.value = true;
+      } else {
+        ElNotification({
+          type: "error",
+          title: res.data.msg,
+        });
+        console.log(res);
+      }
+    }
+    let options = ref([
+      {
+        value: 1,
+        label: "航行",
+      },
+      {
+        value: 2,
+        label: "停泊",
+      },
+      {
+        value: 3,
+        label: "装货",
+      },
+      {
+        value: 4,
+        label: "运输中",
+      },
+      {
+        value: 5,
+        label: "卸货",
+      },
+    ]);
+
+    async function finishVoyage() {
+      if (!voyage.value.dischargeEndTime) return;
+      let res = await api.finishVoyage({
+        voyageId: route.query.id,
+      });
+
+      if (res.data.status == 0) {
+        voyage.value.voyageStatus = 2;
+        ElNotification({
+          type: "success",
+          title: res.data.msg,
+        });
+      } else {
+        ElNotification({
+          type: "error",
+          title: res.data.msg,
+        });
+        console.log(res);
+      }
+    }
+
+    function openVideoModal(url) {
+      currentUrl.value = url;
+      videoModal.value = true;
+    }
+
+    onMounted(() => {
+      initMap();
+      getVoyageDetail();
+    });
+    return {
+      options,
+      voyage,
+      coordinates,
+      medias,
+      disabledStatus,
+      changeVoyageInfo,
+      cancelVoyageChange,
+      submitVoyageChange,
+      finishVoyage,
+      openVideoModal,
+      previewSrcList,
+    };
+  },
+};
+</script>
+<style scoped>
+.map-container {
+  width: 100%;
+  height: 500px;
+}
+:deep().el-input__inner {
+  color: #333 !important;
+}
+.card-note {
+  height: 30px;
+  font-size: 12px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #777777;
+}
+
+.media-box {
+  width: 200px;
+  height: 200px;
+  margin-top: 20px;
+}
+</style>

+ 521 - 0
src/views/voyage/voyageList.vue

@@ -0,0 +1,521 @@
+<template>
+  <div class="full-container-p24">
+    <div style="display: flex; justify-content: space-between">
+      <div class="df aic">
+        <div
+          @click="changeVoyageType(1)"
+          :class="
+            currentbtn
+              ? 'currentbtn radio-btns left-radius'
+              : 'radio-btns left-radius'
+          "
+        >
+          执行中航次
+        </div>
+        <div
+          @click="changeVoyageType(2)"
+          :class="
+            currentbtn
+              ? ' radio-btns right-radius'
+              : 'radio-btns right-radius currentbtn'
+          "
+          style="margin-right: 40px"
+        >
+          历史航次
+        </div>
+        <el-input
+          placeholder="请输入货主名称/联系人/联系人手机号"
+          prefix-icon="el-icon-search"
+          v-model="term"
+          style="width: 330px"
+        ></el-input>
+        <div class="seach-btn" @click="getVoyageList()">查询</div>
+      </div>
+      <!-- <div class="cargo-owner-add" @click="voyageAddDialogVisible = true">
+        添加航次
+      </div> -->
+    </div>
+    <el-dialog v-model="voyageAddDialogVisible" title="添加航次">
+      <el-form
+        :rules="rules"
+        label-position="right"
+        label-width="80px"
+        ref="addVoyageForm"
+        :model="voyageForm"
+        :before-close="resetAddVoyageForm"
+      >
+        <div class="df ffw">
+          <el-form-item prop="voyageName" label="航次名称">
+            <el-input v-model="voyageForm.voyageName"></el-input>
+          </el-form-item>
+          <el-form-item label=""></el-form-item>
+          <el-form-item prop="shipOwnerId" label="船东">
+            <!-- <el-input v-model="voyageForm.shipOwnerId"></el-input> -->
+            <el-autocomplete
+              v-model="voyageForm.shipOwnerName"
+              :fetch-suggestions="seachShipOwner"
+              placeholder="选择船东"
+              @select="selectShipOwner"
+            />
+          </el-form-item>
+          <el-form-item prop="cargoOwnerId" label="货主">
+            <el-autocomplete
+              v-model="voyageForm.cargoOwnerName"
+              :fetch-suggestions="seachCargoOwner"
+              placeholder="选择货主"
+              @select="selectCargoOwner"
+            />
+          </el-form-item>
+          <el-form-item prop="startTime" label="开始时间">
+            <el-date-picker
+              v-model="voyageForm.startTime"
+              type="date"
+              value-format="YYYY/MM/DD"
+              placeholder="航次开始时间"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item prop="endTime" label="结束时间">
+            <el-date-picker
+              v-model="voyageForm.endTime"
+              type="date"
+              value-format="YYYY/MM/DD"
+              placeholder="航次结束时间"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item prop="loadPort" label="装货港">
+            <el-input v-model="voyageForm.loadPort"></el-input>
+          </el-form-item>
+          <el-form-item prop="dischargeProt" label="卸货港">
+            <el-input v-model="voyageForm.dischargeProt"></el-input>
+          </el-form-item>
+          <el-form-item prop="cargo" label="货种">
+            <el-input v-model="voyageForm.cargo"></el-input>
+          </el-form-item>
+          <el-form-item prop="tons" label="吨位">
+            <el-input v-model="voyageForm.tons"></el-input>
+          </el-form-item>
+        </div>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="resetAddVoyageForm">取消</el-button>
+          <el-button type="primary" @click="addVoyage">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <el-table :data="tableData" stripe style="width: 100%; margin-top: 24px">
+      <el-table-column
+        type="index"
+        label="序号"
+        min-width="80"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="voyageName"
+        label="航次名称"
+        min-width="120"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="loadDiscPort"
+        label="装货港-卸货港"
+        min-width="260"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="startEndTime"
+        label="开始时间-结束时间"
+        min-width="200"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="cargo"
+        label="货种"
+        min-width="100"
+        align="center"
+      ></el-table-column>
+
+      <el-table-column
+        prop="tons"
+        label="吨位(吨)"
+        min-width="60"
+        align="center"
+      ></el-table-column>
+
+      <el-table-column
+        prop="transStatus"
+        label="船舶状态"
+        min-width="100"
+        align="center"
+      ></el-table-column>
+
+      <el-table-column
+        prop="remark"
+        label="备注"
+        min-width="200"
+        align="center"
+      ></el-table-column>
+      <el-table-column label="操作" min-width="80" align="center">
+        <template v-slot="scope">
+          <el-button
+            @click="voyageDetail(scope.row.id, tableData)"
+            type="text"
+            size="small"
+          >
+            查看详情
+          </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>
+</template>
+<script>
+import { ref, h, reactive, toRefs, onMounted } from "vue";
+import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
+import store from "../../store";
+import router from "../../router";
+import md5 from "md5";
+import api from "../../apis/fetch";
+
+export default {
+  setup() {
+    let currentbtn = ref(true);
+    let currentPage = ref(1);
+    let term = ref();
+    let tableData = ref();
+    let total = ref();
+    let status = ref(1);
+    async function getVoyageList() {
+      let res = await api.getVoyageList({
+        cargoOwnerId: 0,
+        shipId: 0,
+        status: status.value,
+        term: term.value,
+        currentPage: currentPage.value,
+        size: 10,
+      });
+
+      if (res.data.status == 0) {
+        tableData.value = res.data.result;
+      }
+    }
+    function changeVoyageType(s) {
+      currentbtn.value = s == 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: true, message: "请填写航次名称", trigger: "blur" },
+        ],
+        shipOwnerId: [
+          { 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" },
+        ],
+        dischargeProt: [
+          { required: true, message: "请填写卸货港", trigger: "blur" },
+        ],
+        cargo: [{ required: true, message: "请填写货种", trigger: "blur" }],
+        tons: [{ required: true, message: "请填写吨位", trigger: "blur" }],
+        // -----------
+        shipOwnerName: [
+          { required: true, message: "请选择船东", trigger: "blur" },
+        ],
+        cargoOwnerName: [
+          { required: true, message: "请选择货主", trigger: "blur" },
+        ],
+      },
+    });
+    let voyageForm = reactive({
+      voyageForm: {
+        voyageName: "",
+        cargoOwnerId: "",
+        startTime: "",
+        endTime: "",
+        loadPort: "",
+        dischargeProt: "",
+        cargo: "",
+        tons: "",
+        // -----
+        shipOwnerName: "",
+        cargoOwnerName: "",
+      },
+    });
+    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();
+          } else {
+            console.log(res);
+            ElNotification({
+              title: res.data.msg,
+              type: "error",
+            });
+          }
+        }
+      });
+    }
+
+    async function seachShipOwner(queryString, cb) {
+      if (!queryString) return;
+      let res = await api.getUserInfoAndShipInfo({
+        term: queryString,
+      });
+      let shipOwners = [];
+      if (res.data.status == 0) {
+        shipOwners = res.data.result;
+        for (let i of shipOwners) {
+          i.value = `${i.shipOwnerName}-${i.shipName}`;
+        }
+        cb(shipOwners);
+      }
+    }
+    const selectShipOwner = (item) => {
+      voyageForm.voyageForm.shipOwnerId = item.shipOwnerId;
+      voyageForm.voyageForm.shipId = item.shipId;
+    };
+
+    async function seachCargoOwner(queryString, cb) {
+      if (!queryString) return;
+      let res = await api.seachUser({
+        term: queryString,
+        identity: 2,
+      });
+      let cargoOwners = [];
+      if (res.data.status == 0) {
+        cargoOwners = res.data.result;
+        for (let i of cargoOwners) {
+          i.value = `${i.userName}`;
+        }
+        cb(cargoOwners);
+      }
+    }
+
+    const selectCargoOwner = (item) => {
+      voyageForm.voyageForm.cargoOwnerId = item.userId;
+    };
+
+    function resetAddVoyageForm() {
+      voyageAddDialogVisible.value = false;
+      voyageForm.voyageForm.shipOwnerName = "";
+      voyageForm.voyageForm.cargoOwnerName = "";
+      addVoyageForm.value.resetFields();
+    }
+
+    getVoyageList();
+    onMounted(() => {});
+
+    return {
+      currentPage,
+      term,
+      tableData,
+      total,
+      currentbtn,
+      changeVoyageType,
+      getVoyageList,
+      voyageDetail,
+      pageChange,
+      goToVoyageAdd,
+      addVoyage,
+      voyageAddDialogVisible,
+      addVoyageForm,
+      ...toRefs(rules),
+      ...toRefs(voyageForm),
+      seachShipOwner,
+      selectShipOwner,
+      seachCargoOwner,
+      selectCargoOwner,
+      resetAddVoyageForm,
+    };
+  },
+};
+</script>
+<style scoped>
+.seach-btn {
+  display: inline-block;
+  width: 60px;
+  height: 32px;
+  background: #0094fe;
+  border-radius: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #ffffff;
+  text-align: center;
+  line-height: 32px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+
+.cargo-owner-add {
+  width: 80px;
+  height: 32px;
+  border-radius: 2px;
+  border: 1px solid #0094fe;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #0094fe;
+  line-height: 32px;
+  text-align: center;
+  cursor: pointer;
+}
+:deep().el-dialog {
+  width: 560px;
+  padding: 20px 50px;
+  border-radius: 6px;
+}
+
+:deep() .el-dialog__title {
+  font-size: 18px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #0094fe;
+}
+
+.normal-label {
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #353a42;
+  margin-right: 10px;
+}
+
+.show-input {
+  width: 280px;
+  height: 32px;
+  background: #ffffff;
+  border-radius: 2px;
+  border: 1px solid #dee0e3;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #333333;
+  line-height: 32px;
+  padding-left: 12px;
+  margin-right: 40px;
+}
+
+.radio-btns {
+  height: 38px;
+  width: 103px;
+  border: 1px solid #1486f9;
+  line-height: 38px;
+  text-align: center;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #0094fe;
+  cursor: pointer;
+}
+
+.left-radius {
+  border-top-left-radius: 19px;
+  border-bottom-left-radius: 19px;
+}
+
+.right-radius {
+  border-top-right-radius: 19px;
+  border-bottom-right-radius: 19px;
+}
+.currentbtn {
+  background: #1486f9;
+  color: #fff;
+}
+
+.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;
+}
+
+.voyage-add {
+  width: 80px;
+  height: 36px;
+  border-radius: 2px;
+  border: 1px solid #0094fe;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #0094fe;
+  line-height: 36px;
+  text-align: center;
+  cursor: pointer;
+}
+
+:deep() .el-dialog {
+  width: 800px;
+}
+
+:deep() .el-form-item {
+  margin-right: 22px;
+  width: 300px;
+}
+
+:deep() .el-autocomplete {
+  width: 220px;
+}
+:deep() .el-input__inner {
+  text-align: center;
+}
+</style>

+ 8 - 0
vite.config.js

@@ -0,0 +1,8 @@
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
+import viteCompression from "vite-plugin-compression";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [vue(), viteCompression()],
+});