ソースを参照

更新 消防安检审核

wzg 1 年間 前
コミット
49e8afec44

+ 27 - 0
src/apis/fetch.js

@@ -195,4 +195,31 @@ export default {
   updateFireSafetyTemplate(data) {
     return $http("/fire/inspection/temp/update", data);
   },
+  // 选择安全检查人员/承诺人
+  getFireSafetyCheckUser(data) {
+    return $http("/fire/inspection/account/select", data);
+  },
+
+  // 消防检查详情
+  getFireSafetyCheckDetail(data) {
+    return $http("/fire/inspection/detail", data);
+  },
+  // 审核消防检查项目
+  checkFireSafetyItem(data) {
+    return $http("/fire/inspection/item/check", data);
+  },
+
+  // 消防检查列表
+  getFireSafetyCheckList(data) {
+    return $http("/fire/inspection/list", data);
+  },
+
+  // 保存整改意见
+  saveFireSafetyCheckRectification(data) {
+    return $http("/fire/inspection/save/rectification", data);
+  },
+  // 保存安全检查员
+  saveFireSafetyCheckUser(data) {
+    return $http("/fire/inspection/save/security/account", data);
+  },
 };

+ 18 - 0
src/router/index.js

@@ -171,6 +171,24 @@ const router = createRouter({
       component: () =>
         import("../views/fireSafetyManage/fireSafetyTemplateDetail.vue"),
     },
+    {
+      path: "/fireSafetyManage/checkFireSafetyExamineList",
+      name: "checkFireSafetyExamineList",
+      meta: {
+        title: "消防安检审核列表",
+      },
+      component: () =>
+        import("../views/fireSafetyManage/checkFireSafetyExamineList.vue"),
+    },
+    {
+      path: "/fireSafetyManage/checkFireSafetyExamine",
+      name: "checkFireSafetyExamine",
+      meta: {
+        title: "消防安检审核",
+      },
+      component: () =>
+        import("../views/fireSafetyManage/checkFireSafetyExamine.vue"),
+    },
   ],
 });
 

+ 518 - 0
src/views/fireSafetyManage/checkFireSafetyExamine.vue

@@ -0,0 +1,518 @@
+<template>
+  <el-card class="pl30 pt20 mt30" style="width: 1000px">
+    <div v-if="templateDetail.id">
+      <div class="df aic jcsb mb10">
+        <div class="df aic">
+          <div class="c6 mr30">消防安检名称:</div>
+          <div class="c6 mr30">{{ templateDetail.securityCheckName }}</div>
+        </div>
+        <div class="c6 mr30">
+          <span class="mr20">通过项目:</span>
+          {{ templateDetail.finishCheckItem }} /
+          {{ templateDetail.totalCheckItem }}
+        </div>
+      </div>
+      <div class="df aic mb10">
+        <div class="c6 mr30">消防安检目标:</div>
+        <div class="c6 mr30">{{ templateDetail.securityCheckTarget }}</div>
+      </div>
+      <div class="df aic">
+        <div class="c6 mr30">消防安检重点:</div>
+        <div class="c6 mr30">{{ templateDetail.securityCheckFocus }}</div>
+      </div>
+    </div>
+    <el-divider />
+    <div>
+      <div class="c6 mb20">船舶信息</div>
+      <div class="df aic mb20">
+        <div class="ship-label">船名</div>
+        <div class="ship-text">{{ shipDetail.shipname }}</div>
+        <div class="ship-label">船东姓名</div>
+        <div class="ship-text">{{ shipDetail.shipOwnerName }}</div>
+        <div class="ship-label">船东手机号</div>
+        <div class="ship-text">{{ shipDetail.shipOwnerPhone }}</div>
+        <div class="ship-label">MMSI</div>
+        <div class="ship-text">{{ shipDetail.mmsi }}</div>
+        <div class="ship-label">IMO</div>
+        <div class="ship-text">{{ shipDetail.imo }}</div>
+      </div>
+      <div class="df aic mb20">
+        <div class="ship-label">船龄</div>
+        <div class="ship-text">
+          {{ shipDetail.age }}
+          <span class="unit">年</span>
+        </div>
+        <div class="ship-label">船长</div>
+        <div class="ship-text">
+          {{ shipDetail.length }}
+          <span class="unit">米</span>
+        </div>
+        <div class="ship-label">船宽</div>
+        <div class="ship-text">
+          {{ shipDetail.breadth }}
+          <span class="unit">米</span>
+        </div>
+        <div class="ship-label">吨位</div>
+        <div class="ship-text">
+          {{ shipDetail.loadTons }}
+          <span class="unit">吨</span>
+        </div>
+        <div class="ship-label">满载吃水</div>
+        <div class="ship-text">
+          {{ shipDetail.draught }}
+          <span class="unit">米</span>
+        </div>
+      </div>
+    </div>
+    <el-divider />
+    <div class="df aic">
+      <div class="mr20">选择安全检查员/承诺人(可多选):</div>
+      <el-select
+        class="mr20"
+        v-model="checkedUsers"
+        value-key="key"
+        multiple
+        style="width: 300px"
+        placeholder="请选择检查员/承诺人"
+        clearable
+      >
+        <el-option
+          v-for="item in checkUsers"
+          :key="item.key"
+          :label="item.value"
+          :value="item"
+        />
+      </el-select>
+      <el-button type="primary" @click="saveFireSafetyCheckUser()">
+        保存
+      </el-button>
+    </div>
+    <el-divider />
+    <div :id="mapId" class="map-container"></div>
+
+    <el-divider />
+    <div class="mt40 fs16 c6">安检项目</div>
+    <div
+      class="mb20 mt30 fs14 c6"
+      v-for="item in templateDetail.fileInspectionItems"
+    >
+      <div class="df aic">
+        <div class="ml20 mr10 item-title">检查项目名称:</div>
+        <div class="mr30 item-text">{{ item.checkItemName }}</div>
+        <div class="mr10 item-title">检查项目类型:</div>
+        <div class="mr30 item-text">{{ item.checkItemTypeName }}</div>
+        <div class="mr10 item-title">检查项目备注:</div>
+        <div class="mr10 item-text">{{ item.checkItemRemark }}</div>
+      </div>
+      <div class="mt10">
+        <div v-if="item.viewUrl">
+          <el-image
+            v-if="isImage(item.fileKey)"
+            style="width: 200px; height: 200px"
+            :src="item.viewUrl"
+            :preview-src-list="[item.viewUrl]"
+            fit="cover"
+          ></el-image>
+          <div class="video-box" v-else>
+            <img
+              class="play-icon"
+              style="width: 50px; height: 50px"
+              src="../../assets/play.png"
+              alt=""
+              @click="showModal(item)"
+            />
+            <video
+              style="width: 200px; height: 200px"
+              :src="item.viewUrl"
+            ></video>
+          </div>
+        </div>
+        <el-empty
+          v-else
+          style="width: 220px"
+          class="p10"
+          :image-size="100"
+          description="暂无图片"
+        />
+        <div
+          class="df aic jcsa mt10"
+          style="width: 200px"
+          v-if="item.auditStatus == 0"
+        >
+          <el-button
+            @click="checkSecurityItem(item.id, 1)"
+            class="ml10"
+            size="small"
+            type="primary"
+            :disabled="!item.viewUrl"
+          >
+            通过
+          </el-button>
+          <el-button
+            @click="checkSecurityItem(item.id, 2)"
+            size="small"
+            type="danger"
+            :disabled="!item.viewUrl"
+          >
+            不通过
+          </el-button>
+        </div>
+        <div v-else class="df aic jcsa mt10" style="width: 200px">
+          <el-tag
+            class="ml-2"
+            :type="item.auditStatus == 1 ? 'success' : 'danger'"
+          >
+            {{ item.auditStatus == 1 ? "已通过" : "未通过" }}
+          </el-tag>
+        </div>
+      </div>
+
+      <el-divider />
+    </div>
+    <el-dialog
+      v-model="isModalVisable"
+      destroy-on-close
+      title="视频查看"
+      style="width: 400px"
+    >
+      <div class="video-mark-box">
+        <video
+          style="width: 100%; height: auto"
+          autoplay
+          controls
+          :src="currentItem.downloadUrl"
+        ></video>
+        <div class="video-mark">
+          <div class="mb10">{{ shipDetail.shipname }}</div>
+          <div class="mb10">{{ currentItem.uploadTime }}</div>
+          <div class="mb10">
+            {{ currentItem.province }}·{{ currentItem.city }}·{{
+              currentItem.district
+            }}
+          </div>
+          <div class="mb10">
+            {{ currentItem.week }} {{ currentItem.weather }}
+            {{ currentItem.temperature }} ℃
+          </div>
+          <div class="mb10">真实 实时 精准</div>
+        </div>
+      </div>
+    </el-dialog>
+    <el-form
+      ref="ruleFormRef"
+      :model="ruleForm"
+      :rules="rules"
+      label-width="auto"
+      class="demo-ruleForm"
+      status-icon
+    >
+      <!-- <el-form-item label="Activity time">
+          <el-col :span="11">
+            <el-form-item prop="date1">
+              <el-date-picker
+                v-model="ruleForm.date1"
+                type="date"
+                label="Pick a date"
+                placeholder="Pick a date"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col class="text-center" :span="2">
+            <span class="text-gray-500">-</span>
+          </el-col>
+          <el-col :span="11">
+            <el-form-item prop="date2">
+              <el-time-picker
+                v-model="ruleForm.date2"
+                label="Pick a time"
+                placeholder="Pick a time"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-form-item> -->
+      <el-form-item label="问题整改" prop="problems">
+        <el-input v-model="ruleForm.problems" type="textarea" />
+      </el-form-item>
+      <el-form-item label="整改意见" prop="problems">
+        <el-input v-model="ruleForm.rectificationOpinions" type="textarea" />
+      </el-form-item>
+      <el-form-item>
+        <div class="df aic jcfe" style="width: 100%">
+          <el-button @click="resetForm(ruleFormRef)">重置</el-button>
+
+          <el-button
+            type="primary"
+            @click="saveFireSafetyCheckRectification(ruleFormRef)"
+          >
+            保存整改意见
+          </el-button>
+        </div>
+      </el-form-item>
+    </el-form>
+    <el-divider />
+  </el-card>
+</template>
+<script setup>
+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";
+import { useRoute } from "vue-router";
+import _ from "lodash";
+import { subTimeStr } from "../../utils/utils";
+import { Picture as IconPicture } from "@element-plus/icons-vue";
+
+const route = useRoute();
+let status = ref(0);
+let templateDetail = ref({
+  items: [],
+});
+let shipDetail = ref({});
+const fireInspectionInfo = ref({});
+async function getFireSafetyCheckDetail(shipSecurityCheckId) {
+  let { data } = await api.getFireSafetyCheckDetail({
+    shipSecurityCheckId,
+  });
+  if (data.status == 0) {
+    status.value = data.result.status;
+    templateDetail.value = data.result;
+    shipDetail.value = data.result.ship;
+    coordinates.value = data.result.coordinates;
+    fireInspectionInfo.value = data.result.fireInspectionInfo;
+    ruleForm.value.problems = fireInspectionInfo.value.problems;
+    ruleForm.value.rectificationOpinions =
+      fireInspectionInfo.value.rectificationOpinions;
+  }
+  initMap();
+}
+
+async function checkSecurityItem(shipSecurityCheckItemId, auditStatus) {
+  let shipSecurityCheckId = route.query.id;
+  let { data } = await api.checkSecurityItem({
+    shipSecurityCheckId,
+    shipSecurityCheckItemId,
+    auditStatus,
+  });
+  if (data.status == 0) {
+    ElNotification({
+      title: "成功",
+      message: data.msg,
+      type: "success",
+      duration: 1500,
+    });
+    getFireSafetyCheckDetail(shipSecurityCheckId);
+  }
+}
+function isImage(url) {
+  let imgArr = ["jpg", "jpeg", "png", "gif"];
+  let lastIndex = url.lastIndexOf(".");
+  return imgArr.indexOf(url.substring(lastIndex + 1, url.length)) != -1;
+}
+
+let isModalVisable = ref(false);
+let currentItem = ref({});
+function showModal(item) {
+  currentItem.value = item;
+  isModalVisable.value = true;
+}
+
+const weeks = ref([
+  "星期一",
+  "星期二",
+  "星期三",
+  "星期四",
+  "星期五",
+  "星期六",
+  "星期日",
+]);
+
+const checkUsers = ref([]);
+const checkedUsers = ref([]);
+async function getFireSafetyCheckUser() {
+  let { data } = await api.getFireSafetyCheckUser({});
+  checkUsers.value = data.result;
+}
+
+async function saveFireSafetyCheckUser() {
+  if (checkedUsers.value.length === 0) {
+    ElMessage({
+      message: "请至少选择一名人员",
+      type: "warning",
+    });
+    return;
+  }
+  let checkedUserIds = checkedUsers.value.map((item) => item.key);
+  let checkedUserNames = checkedUsers.value.map((item) => item.value);
+  console.log(checkedUsers.value, checkedUserIds, checkedUserNames);
+
+  let { data } = await api.saveFireSafetyCheckUser({
+    shipSecurityCheckId: route.query.id,
+    shippingAccountIds: checkedUserIds.join(","),
+    shippingAccountNames: checkedUserNames.join(","),
+  });
+  if (data.status == 0) {
+    ElNotification({
+      title: "成功",
+      message: data.msg,
+      type: "success",
+      duration: 1500,
+    });
+    getFireSafetyCheckDetail(route.query.id);
+  } else {
+    ElNotification({
+      title: "失败",
+      message: data.msg,
+      type: "error",
+      duration: 1500,
+    });
+  }
+  console.log(data);
+}
+
+let map = ref({});
+let mapId = ref(`map${route.query.id}`);
+const coordinates = ref([]);
+function initMap() {
+  let c;
+  let longitude = 121.524761;
+  let latitude = 31.228721;
+  // if (medias.value.length) {
+  //   c = Math.floor(medias.value.length / 2);
+  //   longitude = medias.value[c].longitude;
+  //   latitude = medias.value[c].latitude;
+  // }
+  map.value = new AMap.Map(mapId.value, {
+    zoom: 16, //级别
+    center: [longitude, latitude], //中心点坐标
+    mapStyle: "amap://styles/f48d96805f5fa7f5aada657c5ee37017",
+    zoomEnable: false,
+    dragEnable: false,
+  });
+  let toolBar = new AMap.ToolBar({
+    position: {
+      top: "40px",
+      right: "40px",
+    },
+  });
+  let hawkEye = new AMap.HawkEye({
+    opened: false,
+  });
+  map.value.addControl(toolBar);
+  map.value.addControl(hawkEye);
+  let markers = [];
+  for (let i of coordinates.value) {
+    let content = `<div style='width:160px'>
+        <img src='https://frontend-1255802371.cos.ap-shanghai.myqcloud.com/green-circle.png' style='display:block;width:20px;height:20px;margin:6px auto'/
+      </div>`;
+
+    let marker = new AMap.Marker({
+      content,
+      zIndex: 5,
+      position: new AMap.LngLat(i.longitude, i.latitude),
+      offset: new AMap.Pixel(-75, i.audit == 1 ? -195 : -30),
+    });
+
+    markers.push(marker);
+  }
+
+  let overlayGroups = new AMap.OverlayGroup(markers);
+  map.value.on("complete", function () {
+    let t = setTimeout(() => {
+      map.value.add(overlayGroups);
+      map.value.setFitView(markers, true, [200, 50, 0, 0], 18);
+      clearTimeout(t);
+    }, 2000);
+  });
+}
+
+const ruleFormRef = ref(null);
+const ruleForm = ref({});
+const rules = ref({});
+
+const saveFireSafetyCheckRectification = async (formEl) => {
+  if (!formEl) return;
+  formEl.validate(async (valid, fields) => {
+    if (valid) {
+      console.log("submit!");
+      let { data } = await api.saveFireSafetyCheckRectification({
+        ...ruleForm.value,
+        shipSecurityCheckId: route.query.id,
+      });
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+
+const resetForm = (formEl) => {
+  if (!formEl) return;
+  formEl.resetFields();
+};
+
+onMounted(() => {
+  getFireSafetyCheckDetail(route.query.id);
+  getFireSafetyCheckUser();
+});
+</script>
+
+<style scoped>
+.ship-label {
+  width: 80px;
+  color: #666;
+  font-size: 14px;
+  text-align: right;
+  margin-right: 10px;
+}
+
+.ship-text {
+  width: 100px;
+  color: #333;
+  font-size: 14px;
+}
+
+.item-title {
+  width: 100px;
+}
+
+.item-text {
+  width: 100px;
+}
+.video-box {
+  position: relative;
+  width: 200px;
+  height: 200px;
+}
+
+.play-icon {
+  position: absolute;
+  z-index: 100;
+  top: calc(50% - 25px);
+  left: calc(50% - 25px);
+  transition: all 0.5s;
+}
+
+.play-icon:hover {
+  transform: scale(1.2);
+}
+.video-mark-box {
+  position: relative;
+}
+
+.video-mark {
+  position: absolute;
+  bottom: 70px;
+  left: 15px;
+  font-size: 16px;
+  font-weight: 700;
+  z-index: 100;
+  color: #fff;
+}
+.map-container {
+  width: 100%;
+  height: 500px;
+}
+</style>

+ 261 - 0
src/views/fireSafetyManage/checkFireSafetyExamineList.vue

@@ -0,0 +1,261 @@
+<template>
+  <div class="full-container-p24">
+    <div class="df jcsb">
+      <div class="df aic">
+        <el-button-group class="mr30">
+          <el-button
+            @click="(type = 1), getFireSafetyCheckList(1)"
+            :type="type == 1 ? 'primary' : ''"
+          >
+            待审核船舶
+          </el-button>
+          <el-button
+            @click="(type = 2), getFireSafetyCheckList(1)"
+            :type="type == 2 ? 'primary' : ''"
+          >
+            未审核船舶
+          </el-button>
+        </el-button-group>
+        <el-input
+          placeholder="请输入安检名称"
+          prefix-icon="el-icon-search"
+          v-model="term"
+          clearable
+          class="mr10"
+          style="width: 200px"
+        ></el-input>
+        <el-button type="primary" @click="getFireSafetyCheckList(1)">
+          查询
+        </el-button>
+      </div>
+      <!-- <el-button type="primary" @click="goToAdd">发起船舶安检</el-button> -->
+    </div>
+    <div style="margin-top: 24px">
+      <el-table border :data="tableData" stripe style="width: 100%">
+        <el-table-column
+          type="index"
+          label="序号"
+          width="80"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="shipname"
+          label="船舶名称"
+          min-width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="securityCheckName"
+          label="安检名称"
+          min-width="120"
+          align="center"
+          v-if="type == 1"
+        ></el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="发起安检日期"
+          min-width="120"
+          align="center"
+          v-if="type == 1"
+        >
+          <template v-slot="scope">
+            {{ subTimeStr(scope.row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="shipOwnerName"
+          label="船东名称"
+          min-width="120"
+          align="center"
+          v-if="type == 1"
+        ></el-table-column>
+        <el-table-column
+          prop="shipOwnerPhone"
+          label="船东手机号"
+          min-width="120"
+          align="center"
+          v-if="type == 1"
+        ></el-table-column>
+        <el-table-column
+          prop="mmsi"
+          label="MMSI"
+          min-width="120"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="shipname"
+          label="IMO"
+          min-width="120"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="length"
+          label="船长"
+          min-width="80"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="breadth"
+          label="船宽"
+          min-width="80"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="tonnage"
+          label="船舶总吨"
+          min-width="100"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="loadTons"
+          label="载重吨位"
+          min-width="100"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="draught"
+          label="吃水"
+          min-width="80"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column
+          prop="age"
+          label="船龄"
+          min-width="80"
+          align="center"
+          v-if="type == 2"
+        ></el-table-column>
+        <el-table-column label="操作" min-width="120" align="center">
+          <template v-slot="scope">
+            <el-button type="primary" link @click="goTo(scope.row.id)">
+              详情
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="df aic jcfe mt40 mr20">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          :current-page="currentPage"
+          :total="total"
+          @current-change="pageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+<script setup>
+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";
+import { subTimeStr } from "../../utils/utils";
+
+function goToAdd() {
+  router.push("/shipSecurityManage/checkShip");
+}
+let currentPage = ref(1);
+let term = ref("");
+let tableData = ref([]);
+let total = ref(0);
+let type = ref(1);
+let choiceCompanyId = ref(0);
+async function getFireSafetyCheckList(page) {
+  currentPage.value = page || currentPage.value;
+  let res = await api.getFireSafetyCheckList({
+    choiceCompanyId: choiceCompanyId.value,
+    currentPage: currentPage.value,
+    size: 10,
+    term: term.value,
+    type: type.value,
+  });
+  if (res.data.status == 0) {
+    tableData.value = res.data.result;
+    total.value = res.data.total;
+  } else {
+    tableData.value = [];
+    total.value = 0;
+  }
+}
+
+async function getSecurityTemplateDetail(id) {
+  router.push({
+    path: "/fireSafetyManage/shipCheckTemplateDetail",
+    query: {
+      id,
+    },
+  });
+}
+
+function goTo(id) {
+  store.commit("addAlive", "checkFireSafetyExamineList");
+  router.push({
+    path: "/fireSafetyManage/checkFireSafetyExamine",
+    query: {
+      id,
+    },
+  });
+}
+
+function pageChange(e) {
+  currentPage.value = e;
+  getFireSafetyCheckList();
+}
+onMounted(() => {
+  getFireSafetyCheckList();
+});
+</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;
+}
+
+.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;
+}
+</style>