wangfumin 2 meses atrás
pai
commit
355b8a74af

+ 9 - 2
src/view/newFireCase/newFireDetails/components/mix3d.vue

@@ -25,13 +25,14 @@
           >
             <div class="item-main">
               <div class="title" :title="item.title">{{ item.title }}</div>
-              <div class="sub">{{ SceneTypeDesc[item.type] }}</div>
+              <!-- <div class="sub">{{ SceneTypeDesc[item.type] }}</div> -->
+               <div class="sub">多元融合</div>
             </div>
             <div class="actions" v-if="editOrShow === 'edit'">
               <el-tooltip content="编辑">
                 <span class="act" @click.stop="onEdit(item)"><i class="iconfont icon-Edit" /></span>
               </el-tooltip>
-              <el-tooltip content="除">
+              <el-tooltip content="除">
                 <span class="act" @click.stop="onDelete(item)"><i class="iconfont icon-CloseCircle" /></span>
               </el-tooltip>
             </div>
@@ -123,6 +124,7 @@ import { SceneTypeDesc } from '@/constant/scene';
 import { getFusionAndSceneList, addMix3dFusionIds } from '@/store/editCsae';
 import { getMix3dPagging } from '@/store/scene';
 import { confirm } from '@/helper/message';
+import { ElMessage } from 'element-plus';
 import { user } from "@/store/user";
 const url = import.meta.env.VITE_SEVER_URL || 'https://mix3d.4dkankan.com';
 
@@ -163,6 +165,11 @@ const onEdit = (scene: Scene) => {
 };
 
 const onDelete = async (scene: any) => {
+  // 若仅剩一个融合场景,提示并阻止删除
+  if ((scenes.value || []).length <= 1) {
+    ElMessage.error('至少保留一个多元融合场景');
+    return;
+  }
   if (await confirm('确定要移除当前融合场景吗?')) {
     const delId = String((scene as any)?.fusionId || '');
     const newList = (scenes.value || []).filter((s: any) => String(s?.fusionId || '') !== delId);

+ 35 - 13
src/view/newFireCase/newFireDetails/components/otherFiles.vue

@@ -1,5 +1,15 @@
 <template>
   <div class="other-files">
+    <!-- 重命名弹窗组件:用于列表项“更改名字”入口 -->
+    <RelName
+      v-if="editOrShow === 'edit'"
+      v-model:visible="renameVisible"
+      :initial="renameTarget?.filesTitle || ''"
+      title="更改名字"
+      placeholder="文件名"
+      :validator="fileNameValidator"
+      @confirm="onRenameConfirm"
+    />
     <!-- 左侧:上传 + 列表 -->
     <div class="left-panel">
       <!-- 顶部拖拽上传区 -->
@@ -64,6 +74,7 @@
 
 <script setup lang="ts">
 import { ref, computed, onMounted } from 'vue';
+import RelName from './relName.vue';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { Upload, View, Download, Close } from '@element-plus/icons-vue';
@@ -126,23 +137,34 @@ const handleView = (file: CaseFile) => {
   }
 };
 
-// 更改名字:弹窗输入并更新文件标题
-const handleRename = async (file: CaseFile) => {
+// 重命名弹窗状态与校验
+const renameVisible = ref(false);
+const renameTarget = ref<CaseFile | null>(null);
+const fileNameValidator = (val: string) => {
+  if (!val || !val.trim()) return '文件名不合法或过长';
+  if (!/^[^\/\:*?"<>|]{1,100}$/.test(val)) return '文件名不合法或过长';
+  return true;
+};
+// 打开重命名弹窗
+const handleRename = (file: CaseFile) => {
+  renameTarget.value = file;
+  renameVisible.value = true;
+};
+// 确认重命名
+const onRenameConfirm = async (title: string) => {
+  const file = renameTarget.value;
+  if (!file) return;
+  const name = (title || '').trim();
+  if (!name || name === file.filesTitle) {
+    renameVisible.value = false;
+    return;
+  }
   try {
-    const { value } = await ElMessageBox.prompt('', '更改名字', {
-      inputValue: file.filesTitle || '',
-      inputPattern: /^[^\/:*?"<>|]{1,100}$/,
-      inputErrorMessage: '文件名不合法或过长',
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-    });
-    const title = (value || '').trim();
-    if (!title || title === file.filesTitle) return;
-    await setCaseFile({ filesId: file.filesId, filesTitle: title });
+    await setCaseFile({ filesId: file.filesId, filesTitle: name });
     ElMessage.success('更改成功');
+    renameVisible.value = false;
     await refresh();
   } catch (err: any) {
-    if (err === 'cancel' || err === 'close') return; // 取消或关闭不提示错误
     ElMessage.error('更改失败');
     console.error(err);
   }

+ 91 - 0
src/view/newFireCase/newFireDetails/components/relName.vue

@@ -0,0 +1,91 @@
+<template>
+  <el-dialog
+    :title="title || '重命名'"
+    v-model="dialogVisible"
+    width="400px"
+    class="rename-dialog"
+    @closed="handleClosed"
+  >
+    <div class="dialog-content">
+      <el-input
+        v-model="renameTitle"
+        :maxlength="maxLength || 100"
+        :placeholder="placeholder || '请输入新的标题'"
+        @keyup.enter="confirmRename"
+      />
+    </div>
+    <template #footer>
+      <el-button plain type="primary" @click="cancel">取消</el-button>
+      <el-button type="primary" @click="confirmRename">确定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, watch } from 'vue';
+import { ElMessage } from 'element-plus';
+
+// Props:父组件控制可见性与初始值,可自定义文案与校验
+const props = defineProps<{
+  visible: boolean;
+  initial?: string;
+  title?: string;
+  placeholder?: string;
+  maxLength?: number;
+  validator?: (value: string) => true | string;
+}>();
+
+const emit = defineEmits<{
+  (e: 'update:visible', value: boolean): void;
+  (e: 'confirm', value: string): void;
+  (e: 'cancel'): void;
+}>();
+
+// v-model 代理到父 prop:visible
+const dialogVisible = computed({
+  get: () => props.visible,
+  set: (v: boolean) => emit('update:visible', v),
+});
+
+const renameTitle = ref('');
+
+// 打开时同步初始值
+watch(
+  () => props.visible,
+  (v) => {
+    if (v) renameTitle.value = (props.initial ?? '').toString();
+  }
+);
+
+const cancel = () => {
+  emit('cancel');
+  emit('update:visible', false);
+};
+
+const defaultValidate = (val: string): true | string => {
+  if (!val || !val.trim()) return '名称不能为空';
+  return true;
+};
+
+const confirmRename = () => {
+  const val = (renameTitle.value || '').trim();
+  const result = props.validator ? props.validator(val) : defaultValidate(val);
+  if (result !== true) {
+    ElMessage.warning(result as string);
+    return;
+  }
+  emit('confirm', val);
+  emit('update:visible', false);
+};
+
+const handleClosed = () => {
+  // 重置输入,避免保留旧值
+  renameTitle.value = '';
+};
+</script>
+
+<style scoped>
+.rename-dialog .dialog-content {
+  margin: 24px;
+}
+</style>

+ 7 - 1
src/view/newFireCase/newFireDetails/components/scene.vue

@@ -25,7 +25,7 @@
           >
             <div class="item-main">
               <div class="title" :title="item.title">{{ item.title }}</div>
-              <div class="sub">{{ SceneTypeDesc[item.type] }}</div>
+              <div class="sub">{{ SceneTypeDesc[item.sceneType] }}</div>
             </div>
             <div class="actions" v-if="editOrShow === 'edit'" v-show="String(item.sceneNumId) === activeId">
               <el-tooltip content="编辑">
@@ -152,6 +152,7 @@ import { SceneTypeDesc } from '@/constant/scene';
 import { getCaseScenes, replaceCaseScenes, getSceneKey, getCaseScenesBySceneType } from '@/store/case';
 import { getFusionAndSceneList } from '@/store/editCsae';
 import { confirm } from '@/helper/message';
+import { ElMessage } from 'element-plus';
 
 const route = useRoute();
 const router = useRouter();
@@ -243,6 +244,11 @@ const onEdit = (scene: Scene) => {
 };
 
 const onDelete = async (scene: Scene) => {
+  // 若仅剩一个场景,提示并阻止删除
+  if ((scenes.value || []).length <= 1) {
+    ElMessage.error('至少保留一个实景三维场景');
+    return;
+  }
   if (await confirm('确定要移除当前场景吗?')) {
     const newList = scenes.value.filter(s => getSceneKey(s) !== getSceneKey(scene));
     scenes.value = newList;

+ 28 - 18
src/view/newFireCase/newFireDetails/components/screenShot.vue

@@ -1,5 +1,15 @@
 <template>
   <div class="screen-shot-container">
+    <!-- 重命名弹窗组件:用于列表项“更改”入口 -->
+    <RelName
+      v-if="editOrShow === 'edit'"
+      v-model:visible="renameVisible"
+      :initial="renameTarget?.videoFolderName || renameTarget?.filesTitle || ''"
+      title="更改名称"
+      placeholder="视频名称"
+      :validator="nonEmptyValidator"
+      @confirm="onRenameConfirm"
+    />
     <div class="left-panel">
       <div class="screen-shot" v-if="editOrShow === 'edit'" @click="startShot">
         <i class="iconfont icon-record" />开始录制
@@ -56,6 +66,7 @@ import { saveAs } from '@/util/file-serve';
 import { axios, deleteRecord as deleteRecordUrl } from '@/request';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { updateRecordInfo } from '@/store/editCsae';
+import RelName from './relName.vue';
 
 const props = defineProps<{ fire?: any, caseId?: number, editOrShow?: string, processingIds?: (number | string)[], recentAddedItem?: any | null }>();
 const emit = defineEmits<{
@@ -160,24 +171,23 @@ const handleContinue = (file: any) => {
   });
 };
 // 更改视频名称
-const handleRename = async (file: any) => {
+const renameVisible = ref(false);
+const renameTarget = ref<any | null>(null);
+const nonEmptyValidator = (val: string) => (val && val.trim()) ? true : '名称不可为空';
+const handleRename = (file: any) => {
+  renameTarget.value = file;
+  renameVisible.value = true;
+};
+const onRenameConfirm = async (value: string) => {
+  const file = renameTarget.value;
+  if (!file) return;
+  const id = file?.videoFolderId;
+  if (!id) {
+    ElMessage.error('无法识别文件ID');
+    renameVisible.value = false;
+    return;
+  }
   try {
-    const currentName = file?.videoFolderName || file?.filesTitle || '';
-    const { value, action } = await ElMessageBox.prompt('', '更改名称', {
-      inputValue: currentName,
-      inputPlaceholder: '视频名称',
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      inputPattern: /.+/,
-      inputErrorMessage: '名称不可为空',
-    }) as any;
-    if (action !== 'confirm') return;
-    const id = file?.videoFolderId;
-    if (!id) {
-      ElMessage.error('无法识别文件ID');
-      return;
-    }
-    
     await updateRecordInfo({
       videoFolderId: id,
       videoFolderName: String(value).trim(),
@@ -187,10 +197,10 @@ const handleRename = async (file: any) => {
       videoMergeUrl: file?.videoMergeUrl,
     });
     ElMessage.success('名称已更新');
+    renameVisible.value = false;
     // 刷新列表以展示最新名称
     fetchRecordList();
   } catch (e) {
-    if (String(e).includes('cancel')) return;
     console.error('更改名称失败', e);
     ElMessage.error('更改名称失败');
   }

+ 23 - 10
src/view/newFireCase/newFireDetails/components/siteInspection.vue

@@ -1,5 +1,15 @@
 <template>
   <div class="scene-3dmix">
+    <!-- 重命名弹窗组件:用于“现场图”两处重命名入口 -->
+    <RelName
+      v-if="editOrShow === 'edit'"
+      v-model:visible="renameVisible"
+      :initial="currentSelectedFile?.filesTitle || ''"
+      title="重命名"
+      placeholder="文件名"
+      :validator="fileNameValidator"
+      @confirm="applyRenameSelected"
+    />
     <!-- 左侧:切换栏 + 内容 -->
     <div class="scene-detail">
       <div class="let-bar">
@@ -326,6 +336,7 @@
 <script setup lang="ts">
 import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import RelName from './relName.vue';
 // 弹窗已移除,直接跳转到绘制页面
 import { getCaseFiles, CaseFile, BoardType, setCaseFile, delCaseFile, getCaseFileImageInfo } from '@/store/caseFile';
 import { FileDrawType } from '@/constant/caseFile';
@@ -556,24 +567,24 @@ const currentSelectedFile = computed<CaseFile | null>(() => {
 });
 
 // 重命名当前选中
-const renameSelected = async () => {
+const renameVisible = ref(false);
+const fileNameValidator = (val: string) => (val && val.trim().length > 0) ? true : '名称不能为空';
+const renameSelected = () => {
   const file = currentSelectedFile.value;
   if (!file) {
     ElMessage.warning('请先在左侧列表选择一个文件');
     return;
   }
+  renameVisible.value = true;
+};
+const applyRenameSelected = async (newName: string) => {
+  const file = currentSelectedFile.value;
+  if (!file) return;
   try {
-    const { value } = await ElMessageBox.prompt('', '重命名', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      inputValue: file.filesTitle,
-      inputPlaceholder: '文件名',
-      inputValidator: (val: string) => !!val && val.trim().length > 0 || '名称不能为空',
-    });
-    await setCaseFile({ filesId: file.filesId, filesTitle: value.trim() });
+    await setCaseFile({ filesId: file.filesId, filesTitle: newName.trim() });
     ElMessage.success('名称已更新');
     // 本地同步更新名称,避免再次请求
-    file.filesTitle = value.trim();
+    file.filesTitle = newName.trim();
     const mapToPic = (item: any): PicItem => ({
       url: item.listCover || item.filesUrl || item.cover || item.url,
       name: item.filesTitle || item.title || '未命名',
@@ -1485,3 +1496,5 @@ watch(caseId, () => { loadListsFromTree(); loadAlbum(); });
 }
 
 </style>
+import relName from './editName.vue';
+import { ref } from 'vue';

+ 387 - 111
src/view/newFireCase/newdispatch/editFire.vue

@@ -1,106 +1,200 @@
 <template>
-  <el-form ref="form" label-width="84px" class="camera-from">
-    <div class="el-form-item">
-      <el-col :span="12">
-        <el-form-item label="项目编号" class="mandatory">
-          <el-input
-            v-model="bindFire.projectSn"
-            maxlength="18"
-            placeholder="请输入项目编号"
-          />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="起火对象" class="mandatory">
-          <el-input
-            v-model="bindFire.projectName"
-            maxlength="50"
-            placeholder="请输入起火对象"
-          />
-        </el-form-item>
-      </el-col>
-    </div>
-    <el-form-item label="详细地址" class="mandatory">
-      <el-input
-        v-model="bindFire.mapUrl"
-        placeholder="输入名称搜索"
-        clearable
-        readonly
-        class="mandatory"
-      >
-        <template #append>
-          <el-button :icon="Search" @click="searchAMapAddress" />
-        </template>
-      </el-input>
-    </el-form-item>
-
-    <el-form-item label="起火地址" class="mandatory">
-      <el-input
-        v-model="bindFire.projectAddress"
-        maxlength="50"
-        placeholder="请输入起火地址"
-      />
-    </el-form-item>
-    <el-form-item label="起火场所" class="mandatory">
-      <el-cascader
-        style="width: 100%"
-        v-model="projectSite"
-        placeholder="起火场所"
-        :options="place"
-        :props="{ expandTrigger: 'hover' }"
-      />
-    </el-form-item>
-    <div class="el-form-item">
-      <el-col :span="12">
-        <el-form-item label="承办单位" class="mandatory">
-          <companySelect v-model="bindFire.deptId" hideAll :notUpdate="true" disabled />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="承办人员" class="mandatory" placeholder="请输入承办人员">
-          <el-input v-model="bindFire.organizerUsers" maxlength="50" />
-        </el-form-item>
-      </el-col>
-    </div>
-    <div class="el-form-item">
-      <el-col :span="12">
-        <el-form-item label="事故日期" class="mandatory" placeholder="请选择事故日期">
-          <el-date-picker
-            type="date"
-            v-model="accidentDate"
-            style="width: 100%"
-            :disabled-date="(date) => date.getTime() > new Date().getTime()"
-          />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="火灾原因" class="mandatory">
-          <el-cascader
+  <el-form ref="form" label-width="84px" class="camera-from scene-edit-dialog">
+    <!-- 新增模式:仅保留起火对象与场景选择 -->
+    <template v-if="isAddMode">
+      <el-form-item label="起火对象" class="mandatory">
+        <el-input
+          v-model="bindFire.projectName"
+          maxlength="50"
+          placeholder="请输入起火对象"
+        />
+      </el-form-item>
+
+      <!-- 起火场景:复用 scene.vue 的筛选与表格 -->
+      <el-form-item label="起火场景" class="mandatory scene-form-item">
+        <div class="scene-form-item-content" style="width: 100%">
+          <div class="dialog-filter-row">
+            <div class="filter-item">
+              <span class="label">类型:</span>
+              <el-select
+                v-model="query.type"
+                filterable
+                placeholder="请选择类型"
+                style="width: 200px"
+                @change="fetchTableData"
+              >
+                <el-option
+                  v-for="opt in typeOptions"
+                  :key="opt.value"
+                  :label="opt.label"
+                  :value="opt.value"
+                />
+              </el-select>
+            </div>
+            <div class="filter-item">
+              <span class="label">标题:</span>
+              <el-input
+                v-model="query.modelTitle"
+                placeholder="请输入标题"
+                clearable
+                @input="onTitleInput"
+              >
+              </el-input>
+            </div>
+          </div>
+
+          <el-table
+            ref="tableRef"
+            :data="tableData"
+            border
+            height="420"
             style="width: 100%"
-            v-model="fireReason"
-            placeholder="火灾原因:"
-            :options="reason"
-            :props="{ expandTrigger: 'hover' }"
-          />
-        </el-form-item>
-      </el-col>
-  
-    </div>
-    <div class="el-form-item">
-      <el-col :span="12">
-        <el-form-item label="首页显示" class="mandatory">
-          <el-switch v-model="bindFire.mapShow" :disabled="!bindFire.latAndLong" />
-        </el-form-item>
-      </el-col>
-    </div>
-    <!-- 地图弹窗:与 basicInfo.vue 一致的选择逻辑 -->
-    <creatMap v-model="showMapDialog" @confirm="handleMapConfirm" />
+            :row-class-name="rowClassName"
+            v-loading="tableLoading"
+            @selection-change="onSelectionChange"
+          >
+            <el-table-column type="selection" width="48" :selectable="isRowSelectable" />
+            <el-table-column label="标题" min-width="140">
+              <template #default="{ row }">
+                {{ row.name }}
+              </template>
+            </el-table-column>
+            <el-table-column label="类型" width="120">
+              <template #default="{ row }">
+                {{ SceneTypeDesc[row.type] || (query.type === 1 ? 'Mesh场景' : '点云场景') }}
+              </template>
+            </el-table-column>
+            <el-table-column label="所属组织" width="180">
+              <template #default="{ row }">
+                {{ row.deptName }}
+              </template>
+            </el-table-column>
+            <el-table-column label="拍摄时间" width="200">
+              <template #default="{ row }">
+                {{ row.createTime }}
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <div style="display:flex; justify-content:flex-end; margin-top: 12px;">
+            <el-pagination
+              background
+              layout="prev, pager, next, jumper"
+              :current-page="pager.page"
+              :page-size="pager.pageSize"
+              :total="pager.total"
+              @current-change="onPageChange"
+              @size-change="onSizeChange"
+            />
+          </div>
+        </div>
+      </el-form-item>
+    </template>
+
+    <!-- 编辑模式:保持原表单 -->
+    <template v-else>
+      <div class="el-form-item">
+        <el-col :span="12">
+          <el-form-item label="项目编号" class="mandatory">
+            <el-input
+              v-model="bindFire.projectSn"
+              maxlength="18"
+              placeholder="请输入项目编号"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="起火对象" class="mandatory">
+            <el-input
+              v-model="bindFire.projectName"
+              maxlength="50"
+              placeholder="请输入起火对象"
+            />
+          </el-form-item>
+        </el-col>
+      </div>
+      <el-form-item label="详细地址" class="mandatory">
+        <el-input
+          v-model="bindFire.mapUrl"
+          placeholder="输入名称搜索"
+          clearable
+          readonly
+          class="mandatory"
+        >
+          <template #append>
+            <el-button :icon="Search" @click="searchAMapAddress" />
+          </template>
+        </el-input>
+      </el-form-item>
+
+      <el-form-item label="起火地址" class="mandatory">
+        <el-input
+          v-model="bindFire.projectAddress"
+          maxlength="50"
+          placeholder="请输入起火地址"
+        />
+      </el-form-item>
+      <el-form-item label="起火场所" class="mandatory">
+        <el-cascader
+          style="width: 100%"
+          v-model="projectSite"
+          placeholder="起火场所"
+          :options="place"
+          :props="{ expandTrigger: 'hover' }"
+        />
+      </el-form-item>
+      <div class="el-form-item">
+        <el-col :span="12">
+          <el-form-item label="承办单位" class="mandatory">
+            <companySelect v-model="bindFire.deptId" hideAll :notUpdate="true" disabled />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="承办人员" class="mandatory" placeholder="请输入承办人员">
+            <el-input v-model="bindFire.organizerUsers" maxlength="50" />
+          </el-form-item>
+        </el-col>
+      </div>
+      <div class="el-form-item">
+        <el-col :span="12">
+          <el-form-item label="事故日期" class="mandatory" placeholder="请选择事故日期">
+            <el-date-picker
+              type="date"
+              v-model="accidentDate"
+              style="width: 100%"
+              :disabled-date="(date) => date.getTime() > new Date().getTime()"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="火灾原因" class="mandatory">
+            <el-cascader
+              style="width: 100%"
+              v-model="fireReason"
+              placeholder="火灾原因:"
+              :options="reason"
+              :props="{ expandTrigger: 'hover' }"
+            />
+          </el-form-item>
+        </el-col>
+      
+      </div>
+      <div class="el-form-item">
+        <el-col :span="12">
+          <el-form-item label="首页显示" class="mandatory">
+            <el-switch v-model="bindFire.mapShow" :disabled="!bindFire.latAndLong" />
+          </el-form-item>
+        </el-col>
+      </div>
+      <!-- 地图弹窗:与 basicInfo.vue 一致的选择逻辑 -->
+      <creatMap v-model="showMapDialog" @confirm="handleMapConfirm" />
+    </template>
   </el-form>
 </template>
 
 <script setup lang="ts">
 import companySelect from "@/components/company-select/index.vue";
-import { ref } from "vue";
+import { ref, computed, nextTick, onMounted } from "vue";
 import { Fire, setFire, addFire } from "@/app/fire/store/fire";
 import { reason, place } from "@/app/fire/constant/fire";
 import { ElMessage } from "element-plus";
@@ -110,6 +204,9 @@ import { QuiskExpose } from "@/helper/mount";
 import { user } from "@/store/user";
 import { Search } from "@element-plus/icons-vue";
 import creatMap from "../newFireDetails/components/creatMap.vue";
+import { getScenePagging, Scene } from "@/store/scene";
+import { SceneTypeDesc } from "@/constant/scene";
+import { getCaseScenes } from "@/store/case";
 
 const props = defineProps<{ fire?: Fire }>();
 
@@ -127,11 +224,155 @@ const accidentDate = ref(
   bindFire.value.accidentDate ? new Date(bindFire.value.accidentDate) : new Date()
 );
 
-// 地图弹窗开关(对齐 basicInfo.vue 的逻辑)
+// 模式判断:新增 or 编辑
+const isAddMode = computed(() => !props.fire || !props.fire.id);
+
+// 地图弹窗开关(仅编辑模式使用)
 const showMapDialog = ref(false);
 
+// ===== 新增模式:场景列表选择(复用 scene.vue 的逻辑) =====
+// isObj使用1/2;searchType默认传2;关键字使用modelTitle
+const query = ref<{ type: number; modelTitle: string; searchType: string }>({ type: 1, modelTitle: '', searchType: '2' });
+const typeOptions = ref([
+  { label: 'Mesh场景', value: 1 },
+  { label: '点云场景', value: 0 },
+]);
+const tableRef = ref<any>(null);
+const tableData = ref<Scene[]>([]);
+const selectedRows = ref<Scene[]>([]);
+const tableLoading = ref(false);
+const pager = ref({ page: 1, pageSize: 40, total: 0 });
+let titleDebounceTimer: any = null;
+
+const rowKey = (row: any) => `${Number(row?.type)}__${String(row?.num)}`;
+const isRowSelectable = (_row: any) => true;
+const rowClassName = () => '';
+const preselectRows = () => {
+  nextTick(() => {
+    try {
+      tableRef.value?.clearSelection?.();
+      (tableData.value || []).forEach((r: any) => {
+        const exists = (selectedRows.value || []).some((s: any) => rowKey(s) === rowKey(r));
+        if (exists) {
+          tableRef.value?.toggleRowSelection?.(r, true);
+        }
+      });
+    } catch (e) {
+      // 忽略选择异常
+    }
+  });
+};
+
+const fetchTableData = async () => {
+  if (!isAddMode.value) return;
+  tableLoading.value = true;
+  try {
+    const params: any = {
+      isObj: Number(query.value.type),
+      searchType: query.value.searchType,
+      modelTitle: query.value.modelTitle,
+      sceneName: query.value.modelTitle,
+      deptId: '',
+      snCode: '',
+      cameraType: '',
+      pageNum: pager.value.page,
+      pageSize: pager.value.pageSize,
+    };
+    const data = await getScenePagging(params);
+    const list = (data as any)?.list || [];
+    const total = (data as any)?.total || list.length || 0;
+    pager.value.total = total;
+    tableData.value = list;
+    preselectRows();
+  } catch (e) {
+    console.error('获取场景分页失败', e);
+    pager.value.total = 0;
+    tableData.value = [];
+  } finally {
+    tableLoading.value = false;
+  }
+};
+
+const onTitleInput = () => {
+  if (!isAddMode.value) return;
+  if (titleDebounceTimer) clearTimeout(titleDebounceTimer);
+  titleDebounceTimer = setTimeout(() => {
+    pager.value.page = 1;
+    fetchTableData();
+  }, 300);
+};
+
+const onSelectionChange = (rows: Scene[]) => {
+  const map = new Map<string, any>();
+  (selectedRows.value || []).forEach((r: any) => map.set(rowKey(r), r));
+  (rows || []).forEach((r: any) => map.set(rowKey(r), r));
+  selectedRows.value = Array.from(map.values());
+};
+
+const onPageChange = (page: number) => {
+  pager.value.page = page;
+  fetchTableData();
+};
+
+const onSizeChange = (size: number) => {
+  pager.value.pageSize = size;
+  pager.value.page = 1;
+  fetchTableData();
+};
+
+onMounted(() => {
+  if (isAddMode.value) {
+    fetchTableData();
+  }
+});
+
 defineExpose<QuiskExpose>({
   async submit() {
+    // 新增:仅校验起火对象,并构建 sceneNumParam
+    if (isAddMode.value) {
+      if (!bindFire.value.projectName || !bindFire.value.projectName.trim()) {
+        ElMessage.error("起火对象不能为空!");
+        throw "起火对象不能为空!";
+      } else if (!selectedRows.value || selectedRows.value.length === 0) {
+        ElMessage.error("请至少添加一个场景");
+        throw "请至少添加一个场景";
+      }
+
+      const mergedList = Array.from(selectedRows.value || []) as any[];
+      const sceneNumParam = getCaseScenes(mergedList as any[]);
+
+      // 原本参数除起火对象均为空,新增 sceneNumParam
+      const payload: any = {
+        projectName: bindFire.value.projectName,
+        projectSn: "",
+        projectAddress: "",
+        mapUrl: "",
+        latAndLong: "",
+        latlng: "",
+        projectSite: "",
+        projectSiteCode: "",
+        organizerUsers: "",
+        fireReason: "",
+        accidentDate: "",
+        deptId: "",
+        field1: "",
+        field2: "",
+        field3: "",
+        field4: "",
+        field5: "",
+        field6: "",
+        field7: "",
+        field8: "",
+        field9: "",
+        field10: "",
+        sceneNumParam,
+      };
+
+      await addFire(payload as any);
+      return;
+    }
+
+    // 编辑:保持原校验与提交流程
     if (!bindFire.value.latAndLong || !bindFire.value.latAndLong.trim()) {
       ElMessage.error("详细地址不能为空");
       throw "详细地址不能为空!";
@@ -163,21 +404,11 @@ defineExpose<QuiskExpose>({
 
     bindFire.value.accidentDate = dateFormat(accidentDate.value, "yyyy-MM-dd");
     bindFire.value.projectSiteCode = getCode(place, bindFire.value.projectSite);
-    
-    // 保存数据
-    if (bindFire.value.id) {
-      await setFire(bindFire.value);
-    } else {
-      await addFire(bindFire.value as any);
-    }
-    
-    // 保存成功后,刷新fireDetails页面的数据
-    // 通过事件总线或全局事件触发刷新
-    window.location.reload()
+    await setFire(bindFire.value);
   },
 });
 
-// 打开地图弹窗选择地址(与 basicInfo.vue 一致
+// 打开地图弹窗选择地址(仅编辑模式使用)
 const searchAMapAddress = async () => {
   showMapDialog.value = true;
 };
@@ -185,12 +416,57 @@ const searchAMapAddress = async () => {
 // 处理地图弹窗的确认事件(与 basicInfo.vue 一致)
 const handleMapConfirm = (LocationInfo: any) => {
   const { cityname, adname, address, name, location } = LocationInfo || {};
-  // 拼接显示地址
   bindFire.value.mapUrl = `${cityname || ''}${adname || ''}${address || ''}${name || ''}`;
-  // 写入经纬度
   if (location && typeof location.lat !== 'undefined' && typeof location.lng !== 'undefined') {
     bindFire.value.latlng = bindFire.value.latAndLong = `${location.lat},${location.lng}`;
   }
   showMapDialog.value = false;
 };
 </script>
+
+<style lang="scss" scoped>
+.scene-edit-dialog :deep(.el-dialog__body) {
+  padding-top: 8px;
+}
+
+.dialog-filter-row {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  margin-bottom: 10px;
+}
+
+.dialog-filter-row .filter-item {
+  display: flex;
+  align-items: center;
+}
+.dialog-filter-row .filter-item .label {
+  width: 60px;
+  margin-right: 8px;
+  color: rgba(0,0,0,.85);
+}
+.live-search-tip {
+  margin-left: 8px;
+  color: #f56c6c;
+  font-size: 12px;
+}
+.scene-form-item{
+  flex-direction: column;
+  gap: 12px;
+  .scene-form-item-content{
+    border: 1px solid #f5f5f5;
+    padding: 10px;
+    :deep(.el-table-column--selection ) {
+      .cell {
+        width: 30px;
+        margin-left: 4px;
+      }
+    }
+  }
+}
+/* 已导入的行:选中且置灰不可操作 */
+.is-imported-row {
+  opacity: 0.6;
+  pointer-events: none;
+}
+</style>

+ 1 - 0
src/view/newFireCase/newdispatch/quisk.ts

@@ -6,6 +6,7 @@ import { quiskMountFactory } from "@/helper/mount";
 
 export const addFire = quiskMountFactory(EditFire, {
   title: "新增火调项目",
+  width: 800,
 });
 
 export const editFire = quiskMountFactory(EditFire, {