Quellcode durchsuchen

refactor(shipOwnerManage): 重构船东管理详情页面

- 更新页面布局和样式,优化用户体验
- 重新设计表单结构,增加船东证书信息和其他文件上传功能
- 改进数据处理逻辑,支持新增和编辑船东信息
- 优化图片上传和删除功能
- 增加表单验证和友好提示
wzg vor 9 Monaten
Ursprung
Commit
e9c1c3aa87
1 geänderte Dateien mit 479 neuen und 265 gelöschten Zeilen
  1. 479 265
      src/views/shipOwnerManage/shipOwnerDetail.vue

+ 479 - 265
src/views/shipOwnerManage/shipOwnerDetail.vue

@@ -5,126 +5,223 @@
       @click="router.replace('/shipOwnerManage/shipOwnerList')"
     >
       <el-icon class="mr10"><ArrowLeftBold /></el-icon>
-      <div>返回船列表</div>
+      <div>返回船列表</div>
     </div>
   </div>
 
-  <div class="container-title">船员信息</div>
+  <div class="container-title">基本信息</div>
   <div class="line-container-p24">
-    <div class="df jcfe" v-if="shipOwnerDetail.code">
-      <el-button
-        v-if="unchangeableShipOwner"
-        type="primary"
-        @click="showUpdate"
-      >
-        更新船员信息
-      </el-button>
-      <el-button
-        v-if="!unchangeableShipOwner"
-        type="primary"
-        @click="confirmUpdate"
-      >
-        确认更新
-      </el-button>
-      <el-button
-        v-if="!unchangeableShipOwner"
-        type="primary"
-        @click="cancelUpdate"
-      >
-        取消更新
-      </el-button>
-    </div>
-    <div class="line">
-      <div class="info-line">
-        <div class="info-line-title">
-          <span class="red">*</span>
-          船员姓名
-        </div>
-        <el-input
-          class="info-line-text"
-          v-model="shipOwnerDetail.userName"
-          :disabled="unchangeableShipOwner"
-        ></el-input>
-      </div>
-      <div class="info-line">
-        <div class="info-line-title">
-          <span class="red">*</span>
-          船员手机号
-        </div>
-        <el-input
-          class="info-line-text"
-          v-model="shipOwnerDetail.userPhone"
-          :disabled="unchangeableShipOwner"
-        ></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="shipOwnerDetail.idcardNo"
-          :disabled="unchangeableShipOwner"
-        ></el-input>
-      </div>
-    </div>
-    <div class="line">
-      <div class="info-line aic">
-        <div class="info-line-title">上传身份证 :</div>
-        <IdUploader
-          uploaderId="idfront"
-          :limit="1"
-          :params="{}"
-          :actionUrl="store.state.idCardUrl"
-          :disabled="unchangeableShipOwner"
-          :fileList="idFrontList"
-          @onUploadFileList="idFrontUpload"
-          @onRemoveFileList="idFrontRemove"
-          uploadText="身份证人像面"
-          class="mr20"
-        ></IdUploader>
-        <IdUploader
-          uploaderId="idback"
-          :limit="1"
-          :params="{}"
-          :actionUrl="store.state.idCardUrl"
-          :disabled="unchangeableShipOwner"
-          :fileList="idBackList"
-          @onUploadFileList="idBackUpload"
-          @onRemoveFileList="idBackRemove"
-          uploadText="身份证国徽面"
-        ></IdUploader>
-      </div>
-    </div>
-    <div class="df aic jcfe">
-      <el-button
-        v-if="!shipOwnerDetail.id"
-        type="primary"
-        @click="addShipOwner()"
-        :loading="isAddShipOwnerLoading"
-      >
-        {{ isAddShipOwnerLoading ? "正在提交" : "确定" }}
-      </el-button>
-      <el-button
-        v-if="shipOwnerDetail.code && shipOwnerDetail.shipInfo.length == 0"
-        type="primary"
-        @click="addShip()"
-      >
-        新增船舶
-      </el-button>
-    </div>
+    <el-form :model="shipOwnerForm" label-width="100px" label-position="left">
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="船东姓名" required>
+            <el-input
+              v-model="shipOwnerForm.userName"
+              placeholder="请输入"
+              :disabled="!!shipOwnerForm.shipOwnerId"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="船东手机号" required>
+            <el-input
+              v-model="shipOwnerForm.userPhone"
+              placeholder="请输入"
+              :disabled="!!shipOwnerForm.shipOwnerId"
+              @blur="handlePhoneBlur"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="船东身份证号">
+            <el-input
+              v-model="shipOwnerForm.idcardNo"
+              placeholder="请输入"
+              :disabled="!!shipOwnerForm.shipOwnerId"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="身份证人像面">
+            <Uploader
+              :action-url="uploadConfig.uploadUrl"
+              :file-list="idCardFrontFileList"
+              :params="{ type: 'idCardFront' }"
+              @on-upload-file-list="handleIdCardFrontUpload"
+              @on-remove-file-list="handleIdCardFrontRemove"
+              :limit="1"
+              upload-text="点击上传人像面"
+              :disabled="!!shipOwnerForm.shipOwnerId"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="身份证国徽面">
+            <Uploader
+              :action-url="uploadConfig.uploadUrl"
+              :file-list="idCardBackFileList"
+              :params="{ type: 'idCardBack' }"
+              @on-upload-file-list="handleIdCardBackUpload"
+              @on-remove-file-list="handleIdCardBackRemove"
+              :limit="1"
+              upload-text="点击上传国徽面"
+              :disabled="!!shipOwnerForm.shipOwnerId"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+  </div>
+
+  <div class="container-title">船东证书信息</div>
+  <div class="line-container-p24">
+    <el-form
+      :model="shipOwnerForm.cerificate"
+      label-width="100px"
+      label-position="left"
+    >
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="性别">
+            <el-select
+              v-model="shipOwnerForm.cerificate.gender"
+              placeholder="请选择"
+            >
+              <el-option label="男" :value="1"></el-option>
+              <el-option label="女" :value="2"></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="证书编号" required>
+            <el-input
+              v-model="shipOwnerForm.cerificate.certNo"
+              placeholder="请输入"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="证书类型">
+            <el-input
+              v-model="shipOwnerForm.cerificate.certType"
+              placeholder="请输入"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="职务资格" required>
+            <el-select
+              v-model="shipOwnerForm.cerificate.postRole"
+              placeholder="请选择"
+            >
+              <el-option label="大副" value="大副"></el-option>
+              <el-option label="二副" value="二副"></el-option>
+              <el-option label="三副" value="三副"></el-option>
+              <el-option label="船长" value="船长"></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="发证日期" required>
+            <el-date-picker
+              v-model="shipOwnerForm.cerificate.issuerAt"
+              type="date"
+              placeholder="选择日期"
+              format="YYYY/MM/DD"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="截止日期" required>
+            <el-date-picker
+              v-model="shipOwnerForm.cerificate.expiryAt"
+              type="date"
+              placeholder="选择日期"
+              format="YYYY/MM/DD"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <el-form-item label="签发机构" required>
+            <el-input
+              v-model="shipOwnerForm.cerificate.issuerAuthority"
+              placeholder="请输入"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="16">
+          <el-form-item label="适用限制">
+            <el-input
+              type="textarea"
+              v-model="shipOwnerForm.cerificate.applicableRestrictions"
+              :rows="4"
+              placeholder="请输入"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="证书照片" required>
+            <Uploader
+              :action-url="uploadConfig.uploadUrl"
+              :file-list="certFileList"
+              :params="{ type: 'certFile', docType: 1 }"
+              @on-upload-file-list="handleCertUpload"
+              @on-remove-file-list="handleCertRemove"
+              :limit="1"
+              upload-text="点击上传证书"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+  </div>
+
+  <div class="container-title">其他文件</div>
+  <div class="line-container-p24">
+    <div class="mb20">健康证明</div>
+    <Uploader
+      :action-url="uploadConfig.uploadUrl"
+      :file-list="healthFileList"
+      :params="{ type: 'healthFile', docType: 2 }"
+      @on-upload-file-list="handleHealthUpload"
+      @on-remove-file-list="handleHealthRemove"
+      :limit="2"
+      upload-text="点击上传"
+    />
+
+    <div class="mb20 mt30">服务簿</div>
+    <Uploader
+      :action-url="uploadConfig.uploadUrl"
+      :file-list="serviceFileList"
+      :params="{ type: 'serviceFile', docType: 3 }"
+      @on-upload-file-list="handleServiceUpload"
+      @on-remove-file-list="handleServiceRemove"
+      :limit="2"
+      upload-text="点击上传"
+    />
   </div>
-  <div v-if="shipOwnerDetail.shipInfo.length != 0">
-    <ShipInfo
-      ref="shipInfoRef"
-      :shipInfos="shipOwnerDetail.shipInfo"
-      :shipOwnerId="shipOwnerId"
-    ></ShipInfo>
+
+  <div class="df jcc mt30 mb30">
+    <el-button @click="router.replace('/shipOwnerManage/shipOwnerList')">
+      取消
+    </el-button>
+    <el-button type="primary" @click="saveShipOwner">保存</el-button>
   </div>
 </template>
 <script setup>
-import { ref, h, reactive, toRefs, onMounted } from "vue";
-// import { uploadUrl } from "../../apis/config";
+import { ref, h, reactive, toRefs, onMounted, computed } from "vue";
 import { ElNotification, ElMessageBox, ElMessage } from "element-plus";
 import store from "../../store";
 import router from "../../router";
@@ -134,179 +231,296 @@ import { useRoute } from "vue-router";
 import _ from "lodash";
 
 const route = useRoute();
-let shipOwnerDetail = ref({
-  userName: "",
-  userPhone: "",
-  shipInfo: [],
-});
-let shipDetail = ref({
-  shipId: 0,
+const uploadConfig = ref({});
+
+// 初始化船东表单数据结构
+const shipOwnerForm = ref({
+  shipOwnerId: 0, // 船东ID,匹配时返回,未匹配到为0,非0时下方内容无效
+  userName: "", // 船东姓名(必填)
+  userPhone: "", // 船东手机(必填)
+  idcardNo: "", // 船东身份证号(非必填)
+  idcardFrontFileKey: "", // 船东身份证正面文件key(非必填)
+  idcardFrontViewUrl: "", // 船东身份证正面文件预览地址(非必填)
+  idcardFrontDownloadUrl: "", // 船东身份证正面文件下载地址(非必填)
+  idcardBackFileKey: "", // 船东身份证反面文件key(非必填)
+  idcardBackViewUrl: "", // 船东身份证反面文件预览地址(非必填)
+  idcardBackDownloadUrl: "", // 船东身份证反面文件下载地址(非必填)
+  cerificate: {
+    gender: "", // 性别(1-男;2-女)
+    certNo: "", // 证书编号(必填)
+    certType: "", // 证书类型(非必填)
+    postRole: "", // 职务资格(必填)
+    issuerAt: "", // 发证日期(必填)
+    expiryAt: "", // 截止日期(必填)
+    issuerAuthority: "", // 签发机构(必填)
+    applicableRestrictions: "", // 适用限制(非必填)
+  },
+  documents: [], // 文件信息数组
 });
 
-let idFrontList = ref([]);
-function idFrontUpload({ response, file, list }) {
-  if (response.status == 0) {
-    let { downloadUrl, key, viewUrl } = response.result;
-    shipOwnerDetail.value.idcardFrontDownloadUrl = downloadUrl;
-    shipOwnerDetail.value.idcardFrontFileKey = key;
-    shipOwnerDetail.value.idcardFrontViewUrl = viewUrl;
-    idFrontList.value = [{ url: viewUrl }];
+// 计算属性 - 文件列表
+const idCardFrontFileList = computed(() => {
+  if (
+    shipOwnerForm.value.idcardFrontFileKey &&
+    shipOwnerForm.value.idcardFrontViewUrl
+  ) {
+    return [
+      {
+        fileKey: shipOwnerForm.value.idcardFrontFileKey,
+        viewUrl: shipOwnerForm.value.idcardFrontViewUrl,
+        downloadUrl: shipOwnerForm.value.idcardFrontDownloadUrl,
+      },
+    ];
   }
-}
+  return [];
+});
 
-function idFrontRemove({ file, list }) {
-  idFrontList.value = [];
-  shipOwnerDetail.value.idcardFrontDownloadUrl = "";
-  shipOwnerDetail.value.idcardFrontFileKey = "";
-  shipOwnerDetail.value.idcardFrontViewUrl = "";
-}
-let idBackList = ref([]);
-function idBackUpload({ response, file, list }) {
-  if (response.status == 0) {
-    let { downloadUrl, key, viewUrl } = response.result;
-    shipOwnerDetail.value.idcardBackDownloadUrl = downloadUrl;
-    shipOwnerDetail.value.idcardBackFileKey = key;
-    shipOwnerDetail.value.idcardBackViewUrl = viewUrl;
-    idBackList.value = [{ url: viewUrl }];
+const idCardBackFileList = computed(() => {
+  if (
+    shipOwnerForm.value.idcardBackFileKey &&
+    shipOwnerForm.value.idcardBackViewUrl
+  ) {
+    return [
+      {
+        fileKey: shipOwnerForm.value.idcardBackFileKey,
+        viewUrl: shipOwnerForm.value.idcardBackViewUrl,
+        downloadUrl: shipOwnerForm.value.idcardBackDownloadUrl,
+      },
+    ];
   }
-}
-function idBackRemove({ file, list }) {
-  idBackList.value = [];
-  shipOwnerDetail.value.idcardBackDownloadUrl = "";
-  shipOwnerDetail.value.idcardBackFileKey = "";
-  shipOwnerDetail.value.idcardBackViewUrl = "";
-}
+  return [];
+});
+
+const certFileList = computed(() => {
+  return (
+    shipOwnerForm.value.documents?.filter((doc) => doc.docType === 1) || []
+  );
+});
+
+const healthFileList = computed(() => {
+  return (
+    shipOwnerForm.value.documents?.filter((doc) => doc.docType === 2) || []
+  );
+});
+
+const serviceFileList = computed(() => {
+  return (
+    shipOwnerForm.value.documents?.filter((doc) => doc.docType === 3) || []
+  );
+});
 
-function checkShipOwner() {
-  let { userName, userPhone } = shipOwnerDetail.value;
-  if (!userName) {
-    ElNotification.error({
-      title: "请填写船员姓名",
-      duration: 1500,
+// 初始化数据
+onMounted(() => {
+  // 如果有ID参数,获取船东详情
+  if (route.query.id) {
+    api.getShipOwnerDetail({ id: route.query.id }).then((res) => {
+      if (res.data.code === 0) {
+        const data = res.data.data;
+        // 直接使用API返回的数据结构
+        Object.assign(shipOwnerForm.value, data);
+      }
     });
-    return false;
   }
-  if (!userPhone) {
-    ElNotification.error({
-      title: "请填写船员手机号",
-      duration: 1500,
+});
+
+// 手机号失去焦点时检查是否已存在船东信息
+const handlePhoneBlur = () => {
+  if (!shipOwnerForm.value.userPhone) return;
+
+  // 如果已经有shipOwnerId,说明已经匹配到了船东,不需要再查询
+  if (shipOwnerForm.value.shipOwnerId) return;
+
+  api
+    .getShipOwnerDetail({ phone: shipOwnerForm.value.userPhone })
+    .then((res) => {
+      if (res.data.code === 0 && res.data.data && res.data.data.shipOwnerId) {
+        // 匹配到船东信息,更新表单
+        Object.assign(shipOwnerForm.value, res.data.data);
+        ElMessage.info("已匹配到船东信息");
+      }
     });
-    return false;
+};
+
+// 身份证人像面上传处理
+const handleIdCardFrontUpload = ({ response }) => {
+  if (response.code === 0) {
+    shipOwnerForm.value.idcardFrontFileKey = response.data.fileKey;
+    shipOwnerForm.value.idcardFrontViewUrl = response.data.viewUrl;
+    shipOwnerForm.value.idcardFrontDownloadUrl = response.data.downloadUrl;
+  } else {
+    ElMessage.error(response.msg || "上传失败");
   }
-  return true;
-}
+};
 
-let shipInfoRef = ref(null);
-let isAddShipOwnerLoading = ref(false);
-async function addShipOwner() {
-  if (!checkShipOwner()) return;
-  isAddShipOwnerLoading.value = true;
-  let postData = shipOwnerDetail.value;
-  let { data } = await api.addShipOwner(postData);
-  isAddShipOwnerLoading.value = false;
-  if (data.status == 0) {
-    ElMessageBox.confirm("添加船员成功,是否添加新船舶?", "添加成功", {
-      confirmButtonText: "是",
-      cancelButtonText: "否",
-      type: "success",
-    })
-      .then(async () => {
-        data.result.shipInfo = [];
-        shipOwnerDetail.value = data.result;
-        shipOwnerId.value = shipOwnerDetail.value.id;
-        unchangeableShipOwner.value = true;
-        addShip();
-      })
-      .catch(() => {
-        store.commit("removeAlive", "shipOwnerList");
-        router.push("/shipOwnerManage/shipOwnerList");
-      });
+// 身份证人像面删除处理
+const handleIdCardFrontRemove = () => {
+  shipOwnerForm.value.idcardFrontFileKey = "";
+  shipOwnerForm.value.idcardFrontViewUrl = "";
+  shipOwnerForm.value.idcardFrontDownloadUrl = "";
+};
+
+// 身份证国徽面上传处理
+const handleIdCardBackUpload = ({ response }) => {
+  if (response.code === 0) {
+    shipOwnerForm.value.idcardBackFileKey = response.data.fileKey;
+    shipOwnerForm.value.idcardBackViewUrl = response.data.viewUrl;
+    shipOwnerForm.value.idcardBackDownloadUrl = response.data.downloadUrl;
+  } else {
+    ElMessage.error(response.msg || "上传失败");
   }
-}
+};
 
-let unchangeableShipOwner = ref(false);
-let unchangeableShip = ref(false);
-
-let shipCertsRef = ref(null);
-
-async function getShipOwnerDetail(shipOwnerId) {
-  let { data } = await api.getShipOwnerDetail({
-    shipOwnerId,
-  });
-  if (data.status === 0) {
-    data.result.shipOwnerId = data.result.id;
-    shipOwnerDetail.value = data.result;
-    idFrontList.value = data.result.idcardFrontViewUrl
-      ? [
-          {
-            url: data.result.idcardFrontViewUrl,
-          },
-        ]
-      : [];
-    idBackList.value = data.result.idcardBackViewUrl
-      ? [
-          {
-            url: data.result.idcardBackViewUrl,
-          },
-        ]
-      : [];
-    for (let i of shipOwnerDetail.value.shipInfo) {
-      i.disabled = true;
-      for (let j of i.shipCerts) {
-        for (let k of j.certs) {
-          k.url = k.viewUrl;
-        }
-      }
+// 身份证国徽面删除处理
+const handleIdCardBackRemove = () => {
+  shipOwnerForm.value.idcardBackFileKey = "";
+  shipOwnerForm.value.idcardBackViewUrl = "";
+  shipOwnerForm.value.idcardBackDownloadUrl = "";
+};
+
+// 证书上传处理
+const handleCertUpload = ({ response }) => {
+  if (response.code === 0) {
+    if (!shipOwnerForm.value.documents) {
+      shipOwnerForm.value.documents = [];
     }
+    shipOwnerForm.value.documents.push({
+      docType: 1, // 船员适任证书
+      fileKey: response.data.fileKey,
+      viewUrl: response.data.viewUrl,
+      downloadUrl: response.data.downloadUrl,
+    });
   } else {
-    ElNotification.error({
-      title: data.msg,
-      duration: 1500,
+    ElMessage.error(response.msg || "上传失败");
+  }
+};
+
+// 证书删除处理
+const handleCertRemove = ({ fileIndex }) => {
+  const certDocs = shipOwnerForm.value.documents.filter(
+    (doc) => doc.docType === 1
+  );
+  const docToRemove = certDocs[fileIndex];
+  shipOwnerForm.value.documents = shipOwnerForm.value.documents.filter(
+    (doc) => doc !== docToRemove
+  );
+};
+
+// 健康证明上传处理
+const handleHealthUpload = ({ response }) => {
+  if (response.code === 0) {
+    if (!shipOwnerForm.value.documents) {
+      shipOwnerForm.value.documents = [];
+    }
+    shipOwnerForm.value.documents.push({
+      docType: 2, // 体检表
+      fileKey: response.data.fileKey,
+      viewUrl: response.data.viewUrl,
+      downloadUrl: response.data.downloadUrl,
     });
+  } else {
+    ElMessage.error(response.msg || "上传失败");
   }
-}
-async function addShip() {
-  let { data } = await api.getAddShipCerts({});
-
-  shipOwnerDetail.value.shipInfo = [
-    {
-      shipId: 0,
-      disabled: false,
-      shipCerts: data.result,
-    },
-  ];
-  setTimeout(() => {
-    shipInfoRef.value.changeDisable(false);
-  }, 200);
-}
+};
 
-let isUpdate = ref(false);
-let cacheDetail = ref({});
-function showUpdate() {
-  unchangeableShipOwner.value = false;
-  cacheDetail.value = _.cloneDeep(shipOwnerDetail.value);
-}
-async function confirmUpdate() {
-  let { data } = await api.updateShipOwner({
-    ...shipOwnerDetail.value,
-  });
-  getShipOwnerDetail(route.query.shipOwnerId);
-  unchangeableShipOwner.value = true;
-}
-function cancelUpdate() {
-  shipOwnerDetail.value = _.cloneDeep(cacheDetail.value);
-  unchangeableShipOwner.value = true;
-}
-let shipOwnerId = ref("");
+// 健康证明删除处理
+const handleHealthRemove = ({ fileIndex }) => {
+  const healthDocs = shipOwnerForm.value.documents.filter(
+    (doc) => doc.docType === 2
+  );
+  const docToRemove = healthDocs[fileIndex];
+  shipOwnerForm.value.documents = shipOwnerForm.value.documents.filter(
+    (doc) => doc !== docToRemove
+  );
+};
 
-onMounted(() => {
-  if (route.query.shipOwnerId) {
-    shipOwnerId.value = route.query.shipOwnerId;
-    getShipOwnerDetail(route.query.shipOwnerId);
-    unchangeableShipOwner.value = true;
-    unchangeableShip.value = false;
+// 服务簿上传处理
+const handleServiceUpload = ({ response }) => {
+  if (response.code === 0) {
+    if (!shipOwnerForm.value.documents) {
+      shipOwnerForm.value.documents = [];
+    }
+    shipOwnerForm.value.documents.push({
+      docType: 3, // 服务簿
+      fileKey: response.data.fileKey,
+      viewUrl: response.data.viewUrl,
+      downloadUrl: response.data.downloadUrl,
+    });
+  } else {
+    ElMessage.error(response.msg || "上传失败");
   }
-});
+};
+
+// 服务簿删除处理
+const handleServiceRemove = ({ fileIndex }) => {
+  const serviceDocs = shipOwnerForm.value.documents.filter(
+    (doc) => doc.docType === 3
+  );
+  const docToRemove = serviceDocs[fileIndex];
+  shipOwnerForm.value.documents = shipOwnerForm.value.documents.filter(
+    (doc) => doc !== docToRemove
+  );
+};
+
+// 保存船东信息
+const saveShipOwner = () => {
+  // 表单验证
+  if (!shipOwnerForm.value.userName) {
+    ElMessage.warning("请输入船东姓名");
+    return;
+  }
+  if (!shipOwnerForm.value.userPhone) {
+    ElMessage.warning("请输入船东手机号");
+    return;
+  }
+  if (!shipOwnerForm.value.cerificate.certNo) {
+    ElMessage.warning("请输入证书编号");
+    return;
+  }
+  if (!shipOwnerForm.value.cerificate.postRole) {
+    ElMessage.warning("请选择职务资格");
+    return;
+  }
+  if (!shipOwnerForm.value.cerificate.issuerAt) {
+    ElMessage.warning("请选择发证日期");
+    return;
+  }
+  if (!shipOwnerForm.value.cerificate.expiryAt) {
+    ElMessage.warning("请选择截止日期");
+    return;
+  }
+  if (!shipOwnerForm.value.cerificate.issuerAuthority) {
+    ElMessage.warning("请输入签发机构");
+    return;
+  }
+  if (!certFileList.value.length) {
+    ElMessage.warning("请上传证书照片");
+    return;
+  }
+
+  // 如果有ID,则更新,否则新增
+  const apiMethod = route.query.id ? api.updateShipOwner : api.addShipOwner;
+  const successMsg = route.query.id ? "更新成功" : "添加成功";
+
+  // 构建保存数据
+  const saveData = { ...shipOwnerForm.value };
+  if (route.query.id) {
+    saveData.id = route.query.id;
+  }
+
+  apiMethod(saveData)
+    .then((res) => {
+      if (res.data.code === 0) {
+        ElMessage.success(successMsg);
+        router.replace("/shipOwnerManage/shipOwnerList");
+      } else {
+        ElMessage.error(res.data.msg || "保存失败");
+      }
+    })
+    .catch((err) => {
+      console.error(err);
+      ElMessage.error("保存失败");
+    });
+};
 </script>
 <style scoped>
 :deep().el-upload-list__item-thumbnail {