wangfumin 1 bulan lalu
induk
melakukan
f9c60e2fd8

+ 1 - 0
src/helper/message.ts

@@ -6,6 +6,7 @@ export const confirm = (msg: string, okText = "确定") =>
   ElMessageBox.confirm(msg, "系统提示", {
     type: "warning",
     confirmButtonText: okText,
+    cancelButtonText: "取消",
     // icon: markRaw(InfoFilled),
   });
 export const alert = (msg: string) =>

+ 2 - 2
src/store/case.ts

@@ -168,7 +168,7 @@ export const exportCaseInquestInfo = async (caseId: number, id?: number): Promis
     params: { caseId, id, ingoreRes: true },
     responseType: "blob",
   });
-  return res.data;
+  return res as any;
 };
 
 // 兼容两种入参:按案件查询或按提取清单ID查询
@@ -187,7 +187,7 @@ export const exportCaseDetailInfo = async (caseId: number, id?: number): Promise
     params: { caseId, ingoreRes: true, id },
     responseType: "blob",
   });
-  return res.data;
+  return res as any;
 };
 
 // 

+ 59 - 13
src/view/case/records/manifest.vue

@@ -167,8 +167,9 @@
   </div>
 </template>
 <script setup>
-import { onMounted, ref, watch } from "vue";
+import { computed, onMounted, ref, watch } from "vue";
 import { reactive } from "vue";
+import { useRoute } from "vue-router";
 import {
   getCaseDetailInfo,
   saveCaseDetailInfo,
@@ -178,8 +179,14 @@ import saveAs from "@/util/file-serve";
 import { ElMessage } from "element-plus";
 
 const props = defineProps({ caseId: Number, title: String });
+const route = useRoute();
+const routeExtractId = computed(() => {
+  const val = route.query.id;
+  return typeof val === "string" ? Number(val) : undefined;
+});
 
 const isDisableExport = ref(false);
+const currentExtractId = ref(undefined);
 
 console.log(props);
 
@@ -286,31 +293,70 @@ const addextractUser = () => {
 };
 const handleSave = async () => {
   console.log("data", data);
-  const res = await saveCaseDetailInfo(props.caseId, data);
+  if (!props.caseId) return;
+  const payload =
+    currentExtractId.value !== undefined
+      ? { id: currentExtractId.value, ...data }
+      : { ...data };
+  const res = await saveCaseDetailInfo(props.caseId, payload);
   if (res.code === 0) {
     ElMessage.success("保存成功!");
     initInfo();
   }
 };
 const handleExport = async () => {
-  await saveCaseDetailInfo(props.caseId, data);
-  const res = await exportCaseDetailInfo(props.caseId);
+  if (!props.caseId) return;
+  const payload =
+    currentExtractId.value !== undefined
+      ? { id: currentExtractId.value, ...data }
+      : { ...data };
+  await saveCaseDetailInfo(props.caseId, payload);
+
+  let exportId = routeExtractId.value ?? currentExtractId.value;
+  if (exportId === undefined) {
+    const latest: any = await getCaseDetailInfo(props.caseId);
+    const list = latest?.data;
+    if (Array.isArray(list) && list.length) {
+      const sorted = list
+        .filter((i) => i && (typeof i.id === "number" || typeof i.id === "string"))
+        .sort((a, b) => Number(b.id) - Number(a.id));
+      exportId = sorted[0] ? Number(sorted[0].id) : undefined;
+    } else if (list && (typeof list.id === "number" || typeof list.id === "string")) {
+      exportId = Number(list.id);
+    }
+  }
+
+  const res = await exportCaseDetailInfo(props.caseId, exportId);
   console.log("res", res);
-  saveAs(res, `${props.title}_提取清单.docx`);
+  saveAs(res, `${props.title || "提取清单"}_提取清单.docx`);
 };
 const initInfo = async () => {
+  if (!props.caseId) return;
   const res = await getCaseDetailInfo(props.caseId);
 
+  const picked = (() => {
+    const d = res?.data;
+    if (Array.isArray(d)) {
+      if (!d.length) return undefined;
+      const id = routeExtractId.value;
+      if (id !== undefined) return d.find((i) => Number(i?.id) === id);
+      const sorted = d
+        .filter((i) => i && (typeof i.id === "number" || typeof i.id === "string"))
+        .sort((a, b) => Number(b.id) - Number(a.id));
+      return sorted[0] || d[0];
+    }
+    return d;
+  })();
+
+  currentExtractId.value =
+    (picked && (picked.id !== undefined ? picked.id : undefined)) ??
+    routeExtractId.value;
+  isDisableExport.value = !picked;
+
   console.log("res", res);
   for (var k in data) {
-    if (!res.data) {
-      isDisableExport.value = true;
-    } else {
-      isDisableExport.value = false;
-    }
-    if (res.data && res.data.hasOwnProperty(k)) {
-      // console.log("Key is " + k)
-      data[k] = res.data[k];
+    if (picked && Object.prototype.hasOwnProperty.call(picked, k)) {
+      data[k] = picked[k];
     }
   }
 };

+ 6 - 3
src/view/newFireCase/dyManager/list.vue

@@ -54,12 +54,15 @@ const listOptions = computed(() => {
     { name: "场景列表", value: "0" },
     { name: "场景共享", value: "1" },
   ];
-  return canShowAll.value ? options.concat({ name: "全部", value: "2" }) : options;
+  return canShowAll.value ? [{ name: "全部", value: "2" }] : options;
 });
 
 watchEffect(() => {
-  if (!canShowAll.value && props.params.pagging.state.query.searchType === "2") {
-    props.params.pagging.state.query.searchType = "0";
+  const current = props.params.pagging.state.query.searchType;
+  if (canShowAll.value) {
+    if (current !== "2") props.params.pagging.state.query.searchType = "2";
+  } else {
+    if (current === "2") props.params.pagging.state.query.searchType = "0";
   }
 });
 </script>

+ 6 - 3
src/view/newFireCase/meshManager/list.vue

@@ -52,12 +52,15 @@ const listOptions = computed(() => {
     { name: "场景列表", value: "0" },
     { name: "场景共享", value: "1" },
   ];
-  return canShowAll.value ? options.concat({ name: "全部", value: "2" }) : options;
+  return canShowAll.value ? [{ name: "全部", value: "2" }] : options;
 });
 
 watchEffect(() => {
-  if (!canShowAll.value && props.params.pagging.state.query.searchType === "2") {
-    props.params.pagging.state.query.searchType = "0";
+  const current = props.params.pagging.state.query.searchType;
+  if (canShowAll.value) {
+    if (current !== "2") props.params.pagging.state.query.searchType = "2";
+  } else {
+    if (current === "2") props.params.pagging.state.query.searchType = "0";
   }
 });
 </script>

+ 11 - 3
src/view/newFireCase/mix3dManager/list.vue

@@ -40,15 +40,23 @@ const canShowAll = computed(() => {
 });
 
 const listOptions = computed(() => {
-  const options = [
+  if (canShowAll.value) {
+    return [{ name: "全部", value: "2" }];
+  }
+  return [
     { name: "多元融合列表", value: "0" },
     { name: "多元融合共享", value: "1" },
   ];
-  return canShowAll.value ? options.concat({ name: "全部", value: "2" }) : options;
 });
 
 watchEffect(() => {
-  if (!canShowAll.value && props.params.pagging.state.query.searchType === "2") {
+  if (canShowAll.value) {
+    if (props.params.pagging.state.query.searchType !== "2") {
+      props.params.pagging.state.query.searchType = "2";
+    }
+    return;
+  }
+  if (props.params.pagging.state.query.searchType === "2") {
     props.params.pagging.state.query.searchType = "0";
   }
 });

+ 32 - 22
src/view/newFireCase/newFireDetails/components/basicInfo.vue

@@ -4,14 +4,14 @@
     <div class="show-view-content" v-if="props.editOrShow === 'show'">
       <!-- criminal 展示 -->
       <div v-if="props.fromRoute === 'criminal'" class="camera-from show-view">
-        <div class="form-title">案件信息</div>
+        <!-- <div class="form-title">案件信息</div> -->
         <div class="info-row"><span class="label">案件名称:</span><span class="value">{{ bindFire?.caseTitle || '-' }}</span></div>
         <div class="info-row"><span class="label">详细地址:</span><span class="value">{{ bindFire?.mapUrl || '-' }}</span></div>
       </div>
       <!-- fire 展示(原有) -->
       <div v-else class="camera-from show-view">
         <div class="all-content">
-          <div class="form-title">案件信息</div>
+          <!-- <div class="form-title">案件信息</div> -->
           <div class="info-row"><span class="label">项目编号:</span><span class="value">{{ bindFire.projectSn || '-' }}</span></div>
           <div class="info-row"><span class="label">起火对象:</span><span class="value">{{ bindFire.projectName || '-' }}</span></div>
           <div class="info-row"><span class="label">详细地址:</span><span class="value">{{ bindFire.mapUrl || '-' }}</span></div>
@@ -30,7 +30,7 @@
       <!-- criminal 编辑(复原 edit.vue) -->
       <el-form v-if="props.fromRoute === 'criminal'" ref="form" label-width="96px" class="camera-from">
         <div class="all-content">
-          <div class="form-title">案件信息</div>
+          <!-- <div class="form-title">案件信息</div> -->
           <el-form-item label="案件名称">
             <el-input v-model="bindFire.caseTitle" maxlength="50" placeholder="请输入案件名称" />
           </el-form-item>
@@ -47,7 +47,7 @@
       <!-- fire 编辑(原有) -->
       <el-form v-else ref="form" label-width="100px" class="camera-from">
         <div class="all-content">
-          <div class="form-title">案件信息</div>
+          <!-- <div class="form-title">案件信息</div> -->
           <el-form-item label="项目编号" class="mandatory">
             <el-input
               v-model="bindFire.projectSn"
@@ -126,7 +126,7 @@
 
 <script setup lang="ts">
 import companySelect from "@/components/company-select/index.vue";
-import { ref, watch, toRef, onMounted, onUnmounted } from "vue";
+import { ref, watch, toRef, onMounted, onUnmounted, onBeforeUnmount } from "vue";
 import { Fire, setFire, addFire } from "@/app/fire/store/fire";
 import { reason, place } from "@/app/fire/constant/fire";
 import { ElMessage } from "element-plus";
@@ -152,6 +152,25 @@ watch(() => props.fire, (newVal, oldVal) => {
   accidentDate.value = bindFire.value.accidentDate ? new Date(bindFire.value.accidentDate) : new Date()
 })
 
+const dispatchRecordUpdate = () => {
+  try {
+    const title = props.fromRoute === 'criminal' ? (bindFire.value as any).caseTitle : (bindFire.value as any).projectName;
+    const mapUrl = (bindFire.value as any).mapUrl;
+    const latAndLong = (bindFire.value as any).latAndLong;
+    window.dispatchEvent(
+      new CustomEvent('fireDetails:updateTitle', {
+        detail: {
+          title,
+          mapUrl,
+          latAndLong,
+          fire: props.fromRoute === 'fire' ? { ...(bindFire.value as any) } : undefined,
+          criminal: props.fromRoute === 'criminal' ? { ...(bindFire.value as any) } : undefined,
+        },
+      })
+    );
+  } catch (e) {}
+};
+
 const showMapDialog = ref(false)
 const fireReason = genCascaderValue(bindFire, "fireReason");
 const projectSite = genCascaderValue(bindFire, "projectSite");
@@ -216,15 +235,8 @@ const autoSave = async () => {
       ElMessage.success('新增成功');
     }
     lastSavedSnapshot = snapshot;
-    // 保存成功后派发标题更新事件,供父组件同步 currentRecord
-    try {
-      const title = props.fromRoute === 'criminal' ? (bindFire.value as any).caseTitle : (bindFire.value as any).projectName;
-      const mapUrl = (bindFire.value as any).mapUrl;
-      const latAndLong = (bindFire.value as any).latAndLong;
-      window.dispatchEvent(
-        new CustomEvent('fireDetails:updateTitle', { detail: { title, mapUrl, latAndLong } })
-      );
-    } catch (e) {}
+    // 保存成功后派发更新事件,供父组件同步 currentRecord
+    dispatchRecordUpdate();
     // 自动保存成功后不刷新页面,也不打扰用户
   } catch (e) {
     // 自动保存失败不打断填写,可在控制台查看错误
@@ -273,14 +285,7 @@ const autoSaveCriminal = async () => {
     }
     lastSavedSnapshotCriminal = snapshot;
     // 保存成功后派发标题更新事件,附带地图信息,供父组件同步 currentRecord
-    try {
-      const title = (bindFire.value as any).caseTitle;
-      const mapUrl = (bindFire.value as any).mapUrl;
-      const latAndLong = (bindFire.value as any).latAndLong;
-      window.dispatchEvent(
-        new CustomEvent('fireDetails:updateTitle', { detail: { title, mapUrl, latAndLong } })
-      );
-    } catch (e) {}
+    dispatchRecordUpdate();
   } catch (e) {
     console.error('criminal auto-save error', e);
   }
@@ -302,6 +307,11 @@ watch(accidentDate, () => {
   if (props.fromRoute !== 'criminal') triggerAutoSave();
 });
 
+onBeforeUnmount(() => {
+  if (props.editOrShow === 'show') return;
+  dispatchRecordUpdate();
+});
+
 // 监听来自 header 的重命名事件,并直接触发保存以更新标题
 onMounted(() => {
   const renameHandler = (evt: any) => {

+ 8 - 8
src/view/newFireCase/newFireDetails/components/creatMap.vue

@@ -133,9 +133,9 @@ watch(selectedSearchAdress, (newVal, oldVal) => {
     }
     
     // 清空地图标记
-    if (map) {
-      map.clearMap()
-    }
+    // if (map) {
+    //   map.clearMap()
+    // }
     
     // 根据搜索类型调整POI选择器的显示
     if (poiPicker) {
@@ -174,7 +174,7 @@ const initMap = async () => {
     poiPicker.on('poiPicked', function(poiResult) {
         selectedLocation.value = poiResult.item;//选中的信息
         poiPicker.hideSearchResults();
-        map.clearMap();
+        // map.clearMap();
         var source = poiResult.source, poi = poiResult.item;
         if (source !== 'search') {
             //suggest来源的,同样调用搜索
@@ -256,7 +256,7 @@ const locateByCoordinates = (lat: number, lng: number) => {
     map.setZoomAndCenter(15, lngLat)
     
     // 清除之前的标记
-    map.clearMap()
+    // map.clearMap()
     
     // 添加标记
     const marker = new AMap.Marker({
@@ -532,9 +532,9 @@ onUnmounted(() => {
         margin-bottom: 16px;
     }
     // https://a.amap.com/jsapi/static/image/plugin/marker_red.png
-    .amap-marker{
-        display: none!important;
-    }
+    // .amap-marker{
+    //     display: none!important;
+    // }
     .amap-info-contentContainer{
       display: none!important;
     }

+ 12 - 5
src/view/newFireCase/newFireDetails/components/headerTop.vue

@@ -13,11 +13,18 @@
       <el-button
         type="primary"
         class="preview-btn"
-        v-if="showSave && exportReady"
+        v-if="showSave && exportReady && typeName === '照片制卷'"
         @click="handleExport"
         :loading="!isSenseLoaded"
         :disabled="!isSenseLoaded"
       >导出</el-button>
+      <!-- 提取清单的导出 -->
+      <el-button
+        type="primary"
+        class="preview-btn"
+        v-if="typeName === '提取清单' || typeName === '勘验笔录'"
+        @click="emit('export')"
+      >导出</el-button>
     </div>
   </div>
   <el-dialog
@@ -39,7 +46,7 @@
 <script setup lang="ts">
 import { ref, computed, onMounted, onUnmounted } from "vue";
 import { ArrowLeft } from "@element-plus/icons-vue";
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
 import { RouteName, router } from "@/router";
 import { ElMessage } from "element-plus";
 const props = defineProps<{
@@ -50,7 +57,6 @@ const props = defineProps<{
 }>();
 const appId = import.meta.env.VITE_APP_APP === 'criminal' ? 'criminal' : !import.meta.env.VITE_APP_APP ? '' : 'fire'
 const route = useRoute();
-const vueRouter = useRouter();
 const typeName = computed(() => {
   let type = route.query.type as string || route.query.editSub as string;
   if (type === 'inquest') return '勘验笔录';
@@ -117,7 +123,8 @@ onUnmounted(() => { if (timer) clearInterval(timer); });
 
 const emit = defineEmits<{
   save: [],
-  back: []
+  back: [],
+  export: [],
 }>()
 
 // 导出相关状态与方法(复用 photos/index 的逻辑)
@@ -160,7 +167,7 @@ const handleExport = () => {
     .back-icon{
       cursor: pointer;
       margin-right: 16px;
-      font-size: 24px;
+      font-size: 32px;
       color: #909399;
       &:hover{
         color: #606266;

+ 3 - 1
src/view/newFireCase/newFireDetails/components/mix3d.vue

@@ -2,7 +2,7 @@
   <div class="scene-3dmix">
     <div class="no-detail" v-if="scenes.length == 0">
       <div class="no-card" @click="handleImportClick">
-        <el-icon :size="26" style="color: var(--primaryColor);"><DocumentAdd /></el-icon>
+        <i style="color: var(--primaryColor); font-size: 26px;" class="iconfont icon-import" />
         <span>导入</span>
       </div>
       暂无数据
@@ -349,6 +349,8 @@ onMounted(async () => {
     flex-direction: column;
     justify-content: center;
     align-items: center;
+    color: rgba(0,0,0,0.5);
+    font-family: Microsoft YaHei, Microsoft YaHei;
     .no-card{
       display: flex;
       flex-direction: column;

+ 5 - 3
src/view/newFireCase/newFireDetails/components/scene.vue

@@ -159,6 +159,7 @@ import { getCaseScenes, replaceCaseScenes, getSceneKey, getCaseScenesBySceneType
 import { confirm } from '@/helper/message';
 import { ElMessage } from 'element-plus';
 import { isOfflineMode } from '@/util/offline'
+import { user, transformSWToken } from "@/store/user";
 
 const route = useRoute();
 const router = useRouter();
@@ -279,12 +280,13 @@ const onDelete = async (scene: Scene) => {
     }
   }
 };
-const onOpen = (scene: Scene) => {
+const onOpen = async (scene: Scene) => {
+  const token = await transformSWToken(scene);
   let newUrl = ''
   if(window.location.host.includes('localhost') || window.location.host.includes('test')){
-    newUrl = `https://test.4dkankan.com/epg.html?m=${scene.num}`
+    newUrl = `https://test.4dkankan.com/epg.html?m=${scene.num}&token=${token}`
   } else {
-    newUrl = `https://4dkankan.com/epg.html?m=${scene.num}`
+    newUrl = `https://4dkankan.com/epg.html?m=${scene.num}&token=${token}`
   }
   window.open(newUrl, '_blank');
 };

+ 7 - 6
src/view/newFireCase/newFireDetails/components/siteInspection.vue

@@ -145,7 +145,7 @@
                   <span class="name">{{ rec.title }}</span>
                   <div class="header-actions" v-if="editOrShow === 'edit'">
                       <el-tooltip content="下载" placement="top">
-                        <span class="action-icon" @click="downloadSelected(rec.id)">
+                        <span class="action-icon" @click="downloadSelected(rec)">
                           <i class="iconfont icon-download" />
                         </span>
                       </el-tooltip>
@@ -188,7 +188,7 @@
                   <span class="name">{{ rec.title }}</span>
                   <div class="header-actions" v-if="editOrShow === 'edit'">
                     <el-tooltip content="下载" placement="top">
-                      <span class="action-icon" @click="downloadSelected(rec.id)">
+                      <span class="action-icon" @click="downloadSelected(rec)">
                         <i class="iconfont icon-download" />
                       </span>
                     </el-tooltip>
@@ -230,7 +230,7 @@
                   <span class="name">{{ alb.title }}</span>
                   <div class="header-actions" v-if="editOrShow === 'edit'">
                     <el-tooltip content="下载" placement="top">
-                      <span class="action-icon" @click="downloadSelected(alb.id)">
+                      <span class="action-icon" @click="downloadSelected(alb)">
                         <i class="iconfont icon-download" />
                       </span>
                     </el-tooltip>
@@ -976,19 +976,20 @@ const loadAlbum = async () => {
 };
 
 // 统一下载:按当前 tab 类型以 blob 方式下载
-const downloadSelected = async (id?: number) => {
+const downloadSelected = async (item: any) => {
+  console.log(item, 55555)
   try {
     if (activeTab.value === 'inspection') {
       if (!caseId.value) return;
       const title = ((inspectionList.value.find((i) => Number(i?.id) === Number(selectedInquestId.value))?.title) || '勘验笔录') + '.docx';
-      const blob: Blob = await (exportCaseInquestInfo(caseId.value!, id) as any);
+      const blob: Blob = await (exportCaseInquestInfo(caseId.value!, item.inquestId) as any);
       await saveAs(blob, title);
       return;
     }
     if (activeTab.value === 'extraction') {
       if (!caseId.value) return;
       const title = ((extractionList.value.find((i) => Number(i?.id) === Number(selectedExtractId.value))?.title) || '提取清单') + '.docx';
-      const blob: Blob = await (exportCaseDetailInfo(caseId.value!, id) as any);
+      const blob: Blob = await (exportCaseDetailInfo(caseId.value!, item.extractId) as any);
       await saveAs(blob, title);
       return;
     }

+ 12 - 7
src/view/newFireCase/newFireDetails/editFilePage.vue

@@ -348,18 +348,23 @@ watch([visible, type], async ([v, t]) => {
   }
 }, { immediate: true });
 
-const handleSave = async (): Promise<boolean> => {
+const handleSave = async (): Promise<any> => {
   try {
+    let res: any;
     if (type.value === 'inquest') {
-      const idParam = route.query.id ? Number(route.query.id) : undefined;
+      const idParam = (inquest as any).id || (route.query.id ? Number(route.query.id) : undefined);
       const payload = idParam !== undefined ? { id: idParam, ...inquest } : { ...inquest };
-      await saveCaseInquestInfo(props.caseId, payload);
+      res = await saveCaseInquestInfo(props.caseId, payload);
+      const savedId = res?.data?.id || res?.data?.data?.id;
+      if (savedId) (inquest as any).id = savedId;
     } else {
-      const idParam = route.query.id ? Number(route.query.id) : undefined;
+      const idParam = (extract as any).id || (route.query.id ? Number(route.query.id) : undefined);
       const payload = idParam !== undefined ? { id: idParam, ...extract } : { ...extract };
-      await saveCaseDetailInfo(props.caseId, payload);
+      res = await saveCaseDetailInfo(props.caseId, payload);
+      const savedId = res?.data?.id || res?.data?.data?.id;
+      if (savedId) (extract as any).id = savedId;
     }
-    return true;
+    return res?.data || true;
   } catch (e) {
     console.error('保存失败', e);
     ElMessage.error('保存失败,请稍后重试');
@@ -376,7 +381,7 @@ defineExpose({ handleSave });
   display: flex;
   justify-content: center;
   width: 100vw;
-  top: 120px;
+  top: 130px;
   left: 0;
   right: 0;
   bottom: 0;

+ 11 - 7
src/view/newFireCase/newFireDetails/editIndex.vue

@@ -60,13 +60,17 @@ const route = useRoute();
 const vueRouter = useRouter();
 const caseId = computed(() => Number(route.params.caseId));
 let tempFire = ref({});
-watch(() => props.currentRecord, (newVal, oldVal) => {
-  if(props.fromRoute === 'fire') {
-    tempFire.value = newVal?.tmProject;
-  } else if(props.fromRoute === 'criminal') {
-    tempFire.value = newVal;
-  }
-})
+watch(
+  () => props.currentRecord,
+  (newVal) => {
+    if (props.fromRoute === 'fire') {
+      tempFire.value = newVal?.tmProject;
+    } else if (props.fromRoute === 'criminal') {
+      tempFire.value = newVal;
+    }
+  },
+  { immediate: true }
+);
 const startShot = (payload?: any) => {
   emit("start", payload);
 }

+ 7 - 5
src/view/newFireCase/newFireDetails/editInspection.vue

@@ -165,12 +165,14 @@ const initInfo = async () => {
 
 watch(visible, (v) => { if (v) initInfo(); }, { immediate: true });
 
-const handleSave = async (): Promise<boolean> => {
+const handleSave = async (): Promise<any> => {
   try {
-    const idParam = route.query.id ? Number(route.query.id) : undefined;
+    const idParam = (data as any).id || (route.query.id ? Number(route.query.id) : undefined);
     const payload = idParam !== undefined ? { id: idParam, ...data } : { ...data };
-    await saveCaseDetailInfo(props.caseId, payload);
-    return true;
+    const res: any = await saveCaseDetailInfo(props.caseId, payload);
+    const savedId = res?.data?.id || res?.data?.data?.id;
+    if (savedId) (data as any).id = savedId;
+    return res?.data || true;
   } catch (e) {
     console.error('保存提取清单失败', e);
     ElMessage.error('保存失败,请稍后重试');
@@ -187,7 +189,7 @@ defineExpose({ handleSave });
   display: flex;
   justify-content: center;
   width: 100vw;
-  top: 120px;
+  top: 130px;
   left: 0;
   right: 0;
   bottom: 0;

+ 79 - 2
src/view/newFireCase/newFireDetails/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="new-fire-details">
     <!-- 顶部标题栏 -->
-    <headerTop :caseId="caseId" :currentRecord="currentRecord" :editOrShow="editOrShow" :showSave="showSave" @save="saveEditSub" @back="backEditSub" />
+    <headerTop :caseId="caseId" :currentRecord="currentRecord" :editOrShow="editOrShow" :showSave="showSave" @save="saveEditSub" @export="exportEditSub" @back="backEditSub" />
     <editFilePage :caseId="caseId" :currentRecord="currentRecord" :editOrShow="editOrShow" ref="editFilePageRef" />
     <editInspection :caseId="caseId" :currentRecord="currentRecord" :editOrShow="editOrShow" ref="editInspectionRef" />
     <photoEdit :caseId="caseId" :title="pageTitle" ref="photoEditRef" />
@@ -38,7 +38,7 @@ import { ElMessage } from "element-plus";
 import { useRoute, useRouter } from 'vue-router';
 import showIndex from './showIndex.vue';
 import editIndex from './editIndex.vue';
-import { copyCase, updateCaseInfo } from "@/store/case";
+import { copyCase, updateCaseInfo, exportCaseDetailInfo, getCaseDetailInfo, getCaseInquestInfo, exportCaseInquestInfo } from "@/store/case";
 import { getCaseInfoOffline as getCaseInfo, getCaseSceneListOffline as getCaseSceneList, uploadRecordFragments, getUploadRecordProgress } from "@/store/editCsae";
 import { RouteName, router } from "@/router";
 import shot from './components/shot.vue';
@@ -54,6 +54,7 @@ import {
   createTemploraryID,
 } from '@/store/system'
 import { isOfflineMode } from '@/util/offline'
+import saveAs from "@/util/file-serve";
 
 // 从路由获取参数
 const appId = import.meta.env.VITE_APP_APP === 'criminal' ? 'criminal' : !import.meta.env.VITE_APP_APP ? '' : 'fire';
@@ -136,6 +137,8 @@ onMounted(() => {
     const title = detail?.title || '';
     const mapUrl = detail?.mapUrl;
     const latAndLong = detail?.latAndLong;
+    const firePayload = detail?.fire;
+    const criminalPayload = detail?.criminal;
     if (!title) return;
     const cr: any = currentRecord.value || {};
     if (fromRoute.value === 'fire') {
@@ -143,10 +146,18 @@ onMounted(() => {
       cr.tmProject.projectName = title;
       if (mapUrl !== undefined) cr.tmProject.mapUrl = mapUrl;
       if (latAndLong !== undefined) cr.tmProject.latAndLong = latAndLong;
+      if (firePayload && typeof firePayload === 'object') {
+        cr.tmProject = { ...(cr.tmProject || {}), ...(firePayload || {}) };
+      }
+      if (mapUrl !== undefined) cr.mapUrl = mapUrl;
+      if (latAndLong !== undefined) cr.latAndLong = latAndLong;
     } else if (fromRoute.value === 'criminal') {
       cr.caseTitle = title;
       if (mapUrl !== undefined) cr.mapUrl = mapUrl;
       if (latAndLong !== undefined) cr.latAndLong = latAndLong;
+      if (criminalPayload && typeof criminalPayload === 'object') {
+        Object.assign(cr, criminalPayload || {});
+      }
     }
     currentRecord.value = { ...cr };
   };
@@ -308,6 +319,72 @@ const saveEditSub = async () => {
   }
 }
 
+const exportEditSub = async () => {
+  const currentType = route.query.type as string | undefined;
+  if (currentType !== 'extraction' && currentType !== 'inquest') {
+    ElMessage.warning('当前页面不支持导出');
+    return;
+  }
+
+  const sub = route.query.editSub as string | '';
+  let comp: any = null;
+  if (sub === 'editInspection') comp = editInspectionRef.value as any;
+  else comp = editFilePageRef.value as any;
+  if (!comp || typeof comp.handleSave !== 'function') {
+    ElMessage.warning('当前页面暂未就绪,稍后再试');
+    return;
+  }
+
+  try {
+    const res = await comp.handleSave();
+    if (!res) return;
+
+    let exportId = typeof route.query.id === 'string' ? Number(route.query.id) : undefined;
+    
+    // 优先使用保存返回的 ID
+    if (res && typeof res === 'object') {
+      if (res.id) exportId = Number(res.id);
+      else if (res.data && res.data.id) exportId = Number(res.data.id);
+    }
+
+    if (currentType === 'extraction') {
+      if (exportId === undefined) {
+        const resInfo: any = await getCaseDetailInfo(caseId.value);
+        const list = resInfo?.data;
+        if (Array.isArray(list) && list.length) {
+          const sorted = list
+            .filter((i: any) => i && (typeof i.id === 'number' || typeof i.id === 'string'))
+            .sort((a: any, b: any) => Number(b.id) - Number(a.id));
+          exportId = sorted[0] ? Number(sorted[0].id) : undefined;
+        } else if (list && (typeof list.id === 'number' || typeof list.id === 'string')) {
+          exportId = Number(list.id);
+        }
+      }
+      const blob = await exportCaseDetailInfo(caseId.value, exportId);
+      await saveAs(blob, `${pageTitle.value || '提取清单'}_提取清单.docx`);
+    } else if (currentType === 'inquest') {
+      if (exportId === undefined) {
+        const resInfo: any = await getCaseInquestInfo(caseId.value);
+        const list = resInfo?.data;
+        if (Array.isArray(list) && list.length) {
+          const sorted = list
+            .filter((i: any) => i && (typeof i.id === 'number' || typeof i.id === 'string'))
+            .sort((a: any, b: any) => Number(b.id) - Number(a.id));
+          exportId = sorted[0] ? Number(sorted[0].id) : undefined;
+        } else if (list && (typeof list.id === 'number' || typeof list.id === 'string')) {
+          exportId = Number(list.id);
+        }
+      }
+      const blob = await exportCaseInquestInfo(caseId.value, exportId);
+      await saveAs(blob, `${pageTitle.value || '勘验笔录'}_勘验笔录.docx`);
+    }
+
+  } catch (e) {
+    console.error('导出失败', e);
+    ElMessage.error('导出失败,请稍后重试');
+  }
+}
+
 const backEditSub = () => {
   const newQuery: any = { ...route.query };
   delete newQuery.editSub;

+ 6 - 3
src/view/newFireCase/newdispatch/header.vue

@@ -86,7 +86,7 @@ const head = computed(() => {
     { name: "火调列表", value: "0" },
     { name: "火调共享", value: "1" },
   ];
-  return canShowAll.value ? options.concat({ name: "全部", value: "2" }) : options;
+  return canShowAll.value ? [{ name: "全部", value: "2" }] : options;
 });
 
 watch(() => props.pagging.state.query.searchType, (newVal) => {
@@ -95,8 +95,11 @@ watch(() => props.pagging.state.query.searchType, (newVal) => {
 });
 
 watchEffect(() => {
-  if (!canShowAll.value && props.pagging.state.query.searchType === "2") {
-    props.pagging.state.query.searchType = "0";
+  const current = props.pagging.state.query.searchType;
+  if (canShowAll.value) {
+    if (current !== "2") props.pagging.state.query.searchType = "2";
+  } else {
+    if (current === "2") props.pagging.state.query.searchType = "0";
   }
 });
 const projectSite = genCascaderValue(

+ 6 - 3
src/view/user/index.vue

@@ -102,7 +102,7 @@ const canShowAll = computed(() => {
 
 const head = computed(() => {
   const options = [{ name: "用户列表", value: "0" }];
-  return canShowAll.value ? options.concat({ name: "全部", value: "1" }) : options;
+  return canShowAll.value ? [{ name: "全部", value: "1" }] : options;
 });
 let { state, queryReset, refresh, changPageCurrent, changPageSize } = usePagging({
   get: getUserPagging,
@@ -126,8 +126,11 @@ watch(() => state.query.searchType, (newVal) => {
 });
 
 watchEffect(() => {
-  if (!canShowAll.value && state.query.searchType === "1") {
-    state.query.searchType = "0";
+  const current = state.query.searchType;
+  if (canShowAll.value) {
+    if (current !== "1") state.query.searchType = "1";
+  } else {
+    if (current === "1") state.query.searchType = "0";
   }
 });
 const delInfo = async (row: UserInfo) => {