Преглед изворни кода

Merge remote-tracking branch 'origin/dev' into dev

Hwf пре 8 месеци
родитељ
комит
5b9d98f431

+ 82 - 0
src/api/duty/eventing.ts

@@ -0,0 +1,82 @@
+import request from '@/utils/request';
+
+// 获取事件列表
+export function getEvent(params) {
+  return request({
+    url: '/api/event_management/event/list',
+    url: '/api/event_management/event/list',
+    method: 'get',
+    params: params
+  });
+}
+
+// 新建事件
+export function addEvent(data) {
+  return request({
+    url: '/api/event_management/event/create',
+    method: 'post',
+    data: data
+  });
+}
+
+// 事件详情
+export function getEventDetail(params) {
+  return request({
+    url: '/api/event_management/event/detail',
+    method: 'get',
+    params: params
+  });
+}
+
+// 编辑事件
+export function editEvent(params) {
+  return request({
+    url: '/api/event_management/event/edit',
+    method: 'get',
+    params: params
+  });
+}
+
+export function postEditEvent(data) {
+  return request({
+    url: '/api/event_management/event/edit',
+    method: 'post',
+    data
+  });
+}
+
+// 关闭事件
+export function closeEvent(data) {
+  return request({
+    url: '/api/event_management/event/close',
+    method: 'post',
+    data: data
+  });
+}
+
+// 开始指挥事件
+export function startEvent(data) {
+  return request({
+    url: '/api/event_management/event/start',
+    method: 'post',
+    data: data
+  });
+}
+
+// 删除事件
+export function deleteEvent(data) {
+  return request({
+    url: '/api/event_management/event/delete',
+    method: 'post',
+    data: data
+  });
+}
+
+// 上报伤亡情况
+export function uploadEventCasualties(data) {
+  return request({
+    url: '/api/event_management/event/uploadEventCasualties',
+    method: 'post',
+    data: data
+  });
+}

+ 89 - 0
src/components/Dialog/index3.vue

@@ -0,0 +1,89 @@
+<template>
+  <div>
+    <template v-for="(item, index) in options">
+      <template v-if="values.includes(item.value)">
+        <span v-if="item.elTagType === 'default' || item.elTagType === ''" :key="item.value" :index="index" :class="item.elTagClass">
+          {{ item.label + ' ' }}
+        </span>
+        <el-tag
+          v-else
+          :key="item.value + ''"
+          :disable-transitions="true"
+          :index="index"
+          :type="
+            item.elTagType === 'primary' ||
+            item.elTagType === 'success' ||
+            item.elTagType === 'info' ||
+            item.elTagType === 'warning' ||
+            item.elTagType === 'danger'
+              ? item.elTagType
+              : 'default'
+          "
+          :class="item.elTagClass"
+        >
+          {{ item.label + ' ' }}
+        </el-tag>
+      </template>
+    </template>
+    <template v-if="unmatch && showValue">
+      {{ unmatchArray }}
+    </template>
+  </div>
+</template>
+
+<script setup lang="ts">
+interface Props {
+  options: Array<DictDataOption>;
+  value: number | string | Array<number | string>;
+  showValue?: boolean;
+  separator?: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  showValue: true,
+  separator: ','
+});
+
+const values = computed(() => {
+  if (props.value === '' || props.value === null || typeof props.value === 'undefined') return [];
+  return Array.isArray(props.value) ? props.value.map((item) => '' + item) : String(props.value).split(props.separator);
+});
+
+const unmatch = computed(() => {
+  if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
+  // 传入值为非数组
+  let unmatch = false; // 添加一个标志来判断是否有未匹配项
+  values.value.forEach((item) => {
+    if (!props.options.some((v) => v.value === item)) {
+      unmatch = true; // 如果有未匹配项,将标志设置为true
+    }
+  });
+  return unmatch; // 返回标志的值
+});
+
+const unmatchArray = computed(() => {
+  // 记录未匹配的项
+  const itemUnmatchArray: Array<string | number> = [];
+  if (props.value !== '' && props.value !== null && typeof props.value !== 'undefined') {
+    values.value.forEach((item) => {
+      if (!props.options.some((v) => v.value === item)) {
+        itemUnmatchArray.push(item);
+      }
+    });
+  }
+  // 没有value不显示
+  return handleArray(itemUnmatchArray);
+});
+
+const handleArray = (array: Array<string | number>) => {
+  if (array.length === 0) return '';
+  return array.reduce((pre, cur) => {
+    return pre + ' ' + cur;
+  });
+};
+</script>
+
+<style scoped>
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+</style>

+ 3 - 7
src/types/components.d.ts

@@ -19,7 +19,6 @@ declare module 'vue' {
     ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
     ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
-    ElCard: typeof import('element-plus/es')['ElCard']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
     ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
@@ -41,13 +40,12 @@ declare module 'vue' {
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
-    ElPopover: typeof import('element-plus/es')['ElPopover']
-    ElRadio: typeof import('element-plus/es')['ElRadio']
-    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSlider: typeof import('element-plus/es')['ElSlider']
+    ElStep: typeof import('element-plus/es')['ElStep']
+    ElSteps: typeof import('element-plus/es')['ElSteps']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
@@ -58,8 +56,6 @@ declare module 'vue' {
     ElTimeline: typeof import('element-plus/es')['ElTimeline']
     ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
-    ElTree: typeof import('element-plus/es')['ElTree']
-    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
     FooterSection: typeof import('./../components/FooterSection/index.vue')['default']
@@ -70,11 +66,11 @@ declare module 'vue' {
     HikvisionPlayer: typeof import('./../components/HKVideo/hikvision-player.vue')['default']
     HKVideo: typeof import('./../components/HKVideo/index.vue')['default']
     IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
-    IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
     IFrame: typeof import('./../components/iFrame/index.vue')['default']
     ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
     ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']
     Index2: typeof import('./../components/Dialog/index2.vue')['default']
+    Index3: typeof import('./../components/Dialog/index3.vue')['default']
     LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
     Map: typeof import('./../components/Map/index.vue')['default']
     MapLogical: typeof import('./../components/Map/MapLogical.vue')['default']

+ 68 - 55
src/views/emergencyCommandMap/LeftSection.vue

@@ -5,9 +5,9 @@
         <div class="event-box">
           <div style="padding-top: 15px">
             <div class="label-box">
-              <div class="gradient-text">{{ eventData.name }}</div>
+              <div class="gradient-text">{{ eventData.event_title }}</div>
             </div>
-            <div class="time-text">{{ eventData.time }}</div>
+            <div class="time-text">{{ eventData.report_time }}</div>
             <div class="address-box">
               <i class="address-icon"></i>
               <div class="address-text">{{ eventData.address }}</div>
@@ -16,16 +16,22 @@
           <div style="margin-left: 95px">
             <div class="flex">
               <div class="status-box warning-status">
-                <div class="text">{{ eventData.grade }}</div>
+                <div class="text">
+                  <dict-tag :options="mm_event_level" :value="eventData.event_level" />
+                </div>
               </div>
               <div class="status-box danger-status">
-                <div class="text">{{ eventData.type }}</div>
+                <div class="text">
+                  <dict-tag :options="mm_event_type" :value="eventData.event_type" />
+                </div>
               </div>
               <div class="status-box">
-                <div class="text">{{ eventData.status }}</div>
+                <div class="text">
+                  <dict-tag :options="mm_event_state" :value="eventData.event_status" />
+                </div>
               </div>
             </div>
-            <div class="unit-box">报送单位:{{ eventData.unit }}</div>
+            <div class="unit-box">报送单位:{{ eventData.event_source }}</div>
           </div>
         </div>
         <div class="line"></div>
@@ -89,32 +95,33 @@
       </div>
     </div>
 
-<!--    <div class="card" style="height: 990px">-->
-<!--      <div class="card-header">-->
-<!--        <div>分析研判</div>-->
-<!--      </div>-->
-<!--      <div class="card-content">-->
-<!--        <div v-for="(item, index) in analyzeJudge" :key="index" class="line">-->
-<!--          <div>{{ item.label }}</div>-->
-<!--          <div class="line-content">-->
-<!--            <div v-for="(item2, index2) in item.children" :key="index2" class="content-item">-->
-<!--              <el-icon size="72px" style="margin-right: 10px;"><Shop /></el-icon>-->
-<!--              <div class="text-box">-->
-<!--                <div class="">{{ item2.data + item2.unit }}</div>-->
-<!--                <div class="">{{ item2.label }}</div>-->
-<!--              </div>-->
-<!--            </div>-->
-<!--          </div>-->
-<!--        </div>-->
-<!--      </div>-->
-<!--    </div>-->
+    <!--    <div class="card" style="height: 990px">-->
+    <!--      <div class="card-header">-->
+    <!--        <div>分析研判</div>-->
+    <!--      </div>-->
+    <!--      <div class="card-content">-->
+    <!--        <div v-for="(item, index) in analyzeJudge" :key="index" class="line">-->
+    <!--          <div>{{ item.label }}</div>-->
+    <!--          <div class="line-content">-->
+    <!--            <div v-for="(item2, index2) in item.children" :key="index2" class="content-item">-->
+    <!--              <el-icon size="72px" style="margin-right: 10px;"><Shop /></el-icon>-->
+    <!--              <div class="text-box">-->
+    <!--                <div class="">{{ item2.data + item2.unit }}</div>-->
+    <!--                <div class="">{{ item2.label }}</div>-->
+    <!--              </div>-->
+    <!--            </div>-->
+    <!--          </div>-->
+    <!--        </div>-->
+    <!--      </div>-->
+    <!--    </div>-->
   </div>
 </template>
 
 <script lang="ts" setup>
-const duration = ref(0);
+import { getEventDetail } from '@/api/duty/eventing';
+// const duration = ref(0);
 const formattedDuration = ref('');
-
+const route = useRoute();
 const router = useRouter();
 const updateDuration = () => {
   duration.value += 1;
@@ -124,23 +131,28 @@ const updateDuration = () => {
   const seconds = duration.value % 60;
   formattedDuration.value = `持续时长:${days}天${hours}小时${minutes}分${seconds}秒`;
 };
-
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { mm_event_type, mm_event_level, mm_event_state, region } = toRefs<any>(
+  proxy?.useDict('mm_event_type', 'mm_event_level', 'mm_event_state', 'region')
+);
+let eventId = ref('');
 // 事件信息
 const eventData = ref({
-  name: '',
-  type: '',
-  grade: '',
+  event_title: '',
+  event_type: '',
+  event_level: '',
   address: '',
-  status: '',
-  unit: '',
-  time: '',
-  duration: {
-    day: '',
-    hour: '',
-    minute: '',
-    second: ''
-  }
+  event_status: '',
+  event_source: '',
+  report_time: ''
 });
+const fetchEventDetail = () => {
+  console.log('fetchEventDetail');
+  getEventDetail({ event_id: eventId.value }).then((res) => {
+    eventData.value = res.data;
+  });
+};
+
 // 综合值守
 const comprehensiveDutyState = ref({
   time1: '',
@@ -152,7 +164,7 @@ const comprehensiveDutyState = ref({
 });
 
 // 分析研判
-const analyzeJudge = ref({})
+const analyzeJudge = ref({});
 
 // 结束
 const endProcess = () => {
@@ -162,18 +174,18 @@ const endProcess = () => {
 const initData = () => {
   // 事件信息
   eventData.value = {
-    name: '茂名市茂南区应急指挥事件',
-    type: '自然灾害',
-    grade: '一级',
-    address: '广东省茂名市茂南区',
-    status: '进行中',
-    unit: '110',
-    time: '2024/7/1 09:00:00',
+    event_title: '',
+    type: '',
+    grade: '',
+    address: '',
+    status: '',
+    unit: '',
+    time: '',
     duration: {
-      day: '1',
-      hour: '12',
-      minute: '14',
-      second: '32'
+      day: '',
+      hour: '',
+      minute: '',
+      second: ''
     }
   };
   // 综合值守
@@ -230,10 +242,11 @@ const initData = () => {
     }
   ];
 };
-
 onMounted(() => {
   initData();
   setInterval(updateDuration, 800);
+  eventId.value = route.query.event_id as string;
+  fetchEventDetail();
 });
 </script>
 
@@ -448,7 +461,7 @@ onMounted(() => {
         /* 设置字体透明 */
         color: transparent;
         /* 设置线性渐变,从红色渐变到蓝色 */
-        background-image: linear-gradient(to bottom, #fff 20%, #f6d6a7 50%, #E48406 100%);
+        background-image: linear-gradient(to bottom, #fff 20%, #f6d6a7 50%, #e48406 100%);
         /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
         -webkit-background-clip: text;
         /* 非Webkit内核浏览器需要使用标准前缀 */
@@ -492,7 +505,7 @@ onMounted(() => {
       /* 设置字体透明 */
       color: transparent;
       /* 设置线性渐变,从红色渐变到蓝色 */
-      background-image: linear-gradient(to bottom, #fff 20%, #FF2F3C 50%, #FF2F3C 100%);
+      background-image: linear-gradient(to bottom, #fff 20%, #ff2f3c 50%, #ff2f3c 100%);
       /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
       -webkit-background-clip: text;
       /* 非Webkit内核浏览器需要使用标准前缀 */

+ 360 - 0
src/views/routineCommandMap/EventManage.vue

@@ -0,0 +1,360 @@
+<template>
+  <div v-if="modelValue" class="dialog-wrap">
+    <div class="overlay" @click="closeDialog"></div>
+    <div class="dialog" :style="{ height: height }">
+      <div class="dialog-header">
+        <div class="dialog-title">{{ title }}</div>
+        <div class="icon-close" @click="closeDialog">
+          <el-icon size="100px"><Close /></el-icon>
+        </div>
+      </div>
+      <div class="dialog-content">
+        <slot />
+        <transition name="fade">
+          <div v-show="showSearch" class="mb-[20px]">
+            <el-form ref="queryFormRef" :model="queryParams">
+              <el-row :gutter="10">
+                <!-- 第一行 -->
+                <el-col :span="6">
+                  <el-form-item label="事件类型" prop="event_type">
+                    <el-select v-model="queryParams.event_type" placeholder="全部" clearable>
+                      <el-option label="全部" value=""></el-option>
+                      <el-option v-for="item in mm_event_type" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                  <el-form-item label="事件等级" prop="event_level">
+                    <el-select v-model="queryParams.event_level" placeholder="全部" clearable>
+                      <el-option label="全部" value=""></el-option>
+                      <el-option v-for="item in mm_event_level" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                  <el-form-item label="事件状态" prop="event_status">
+                    <el-select v-model="queryParams.event_status" placeholder="全部" clearable>
+                      <el-option label="全部" value=""></el-option>
+                      <el-option v-for="item in mm_event_state" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                  <el-form-item label="行政区划" prop="region_code">
+                    <el-select v-model="queryParams.region_code" placeholder="全部" clearable>
+                      <el-option label="全部" value=""></el-option>
+                      <el-option v-for="item in region" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+              <el-row :gutter="10" class="mt-[10px]">
+                <el-col :span="6">
+                  <el-form-item label="事发时间" prop="event_time">
+                    <el-date-picker
+                      v-model="queryParams.event_time"
+                      type="date"
+                      value-format="YYYY-MM-DD"
+                      placeholder="选择事发时间"
+                    ></el-date-picker>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                  <el-form-item prop="keyword">
+                    <el-input v-model="queryParams.keyword" placeholder="请输入事件标题/事件地点" clearable @keyup.enter="handleQuery" />
+                  </el-form-item>
+                </el-col>
+                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              </el-row>
+            </el-form>
+          </div>
+        </transition>
+        <!-- 表格组件 -->
+        <el-table v-loading="loading" :data="eventList" @selection-change="handleSelectionChange">
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column label="事件编号" align="center" prop="event_id" />
+          <el-table-column label="事件标题" align="center" prop="event_title" />
+          <el-table-column label="事件类型" align="center" prop="event_type">
+            <template #default="scope">
+              <dict-tag :options="mm_event_type" :value="scope.row.event_type" />
+            </template>
+          </el-table-column>
+          <el-table-column label="事件等级" align="center" prop="event_level">
+            <template #default="scope">
+              <dict-tag :options="mm_event_level" :value="scope.row.event_level" />
+            </template>
+          </el-table-column>
+          <el-table-column label="事件地点" align="center" prop="address" />
+          <el-table-column label="事件状态" align="center" prop="event_status">
+            <template #default="scope">
+              <dict-tag :options="mm_event_state" :value="scope.row.event_status" />
+            </template>
+          </el-table-column>
+          <el-table-column label="事件时间" align="center" prop="event_time" />
+          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+            <template #default="scope">
+              <el-tooltip content="查看" placement="top">
+                <el-button link type="primary" icon="View" @click="handleView(scope.row)"></el-button>
+              </el-tooltip>
+              <el-tooltip content="编辑" placement="top">
+                <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+              </el-tooltip>
+              <el-tooltip content="关闭事件" placement="top">
+                <el-button link type="primary" icon="Close" @click="handleClose(scope.row)"></el-button>
+              </el-tooltip>
+            </template>
+          </el-table-column>
+        </el-table>
+        <eventDetails
+          v-model="eventDetailsState.show"
+          :title="eventDetailsState.title"
+          :event-id="eventId"
+          @update:model-value="getList"
+        ></eventDetails>
+        <EventEditDialog v-model="eventEditDialogState.show" :title="eventEditDialogState.title" :event-id="eventId" @update:model-value="getList" />
+        <CloseEventDialog
+          v-model="closeDialogState.show"
+          :data="closeDialogState.form"
+          :event-id="eventId"
+          @update:model-value="handleCloseEventDialog"
+        />
+
+        <pagination v-show="total > 0" v-model:page="queryParams.page" v-model:limit="queryParams.page_size" :total="total" @pagination="getList" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, toRefs, onMounted } from 'vue';
+import { getEvent } from '@/api/duty/eventing';
+
+import EventEditDialog from './eventing/EventEditDialog.vue';
+import CloseEventDialog from './eventing/CloseEventDialog.vue';
+import eventDetails from './eventing/eventDetails.vue';
+const proxy = getCurrentInstance()?.proxy;
+const { mm_event_type, mm_event_level, mm_event_state, region } = toRefs<any>(
+  proxy?.useDict('mm_event_type', 'mm_event_level', 'mm_event_state', 'region')
+);
+const router = useRouter();
+const eventList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const selectedRow = ref(null);
+const eventId = ref('');
+const queryFormRef = ref();
+
+const initFormData = {
+  event_id: '', // 事件标题
+  event_title: '', // 事件标题
+  event_type: '', // 事件类型
+  event_level: '', // 事件等级
+  event_status: '', // 事件状态
+  address: '', // 事件地点
+  longitude: '', // 经度
+  latitude: '', // 纬度
+  event_time: '', // 事发时间
+  report_time: '', // 上报时间
+  deaths: '',
+  injuries: '',
+  missing: '',
+  event_source: '', // 事件来源
+  event_description: '', // 事件描述
+  contact: '' // 联系方式
+};
+// 表单数据
+const data = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    page: 1,
+    page_size: 10,
+    event_type: '',
+    event_level: '',
+    event_status: '',
+    event_time: '',
+    region_code: '',
+    keyword: ''
+  },
+  event_levelSelection: [],
+  event_statusSelection: [],
+  regionSelection: []
+});
+
+const { queryParams, form } = toRefs(data);
+
+const getList = () => {
+  loading.value = true;
+  getEvent(queryParams.value)
+    .then((res) => {
+      eventList.value = res.data;
+      total.value = res.total;
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+};
+
+const handleQuery = () => {
+  queryParams.value.page = 1;
+  getList();
+};
+
+// 重置查询条件
+const resetQuery = () => {
+  queryParams.value = { page: 1, page_size: 10, event_type: '', event_level: '', event_status: '', event_time: '', region_code: '', keyword: '' };
+  handleQuery();
+};
+
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map((item) => item.eventId);
+  selectedRow.value = selection.length === 1 ? selection[0] : null;
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+const eventEditDialogState = reactive({
+  show: false,
+  title: ''
+});
+const eventDetailsState = reactive({
+  show: false,
+  title: ''
+});
+
+// const handleAdd = () => {
+//   eventId.value = '';
+//   eventEditDialogState.title = '新增事件';
+//   eventEditDialogState.show = true;
+// };
+
+// 修改事件
+const handleUpdate = (row) => {
+  if (row) {
+    eventId.value = row.event_id;
+    eventEditDialogState.title = '修改事件';
+    eventEditDialogState.show = true;
+  }
+};
+//查看事件详情
+const handleView = (row) => {
+  // 查看事件详情逻辑
+  eventId.value = row.event_id;
+  eventDetailsState.title = '查看事件';
+  eventDetailsState.show = true;
+};
+
+// const handleDelete = (row) => {
+//   if (row) {
+//     ElMessageBox.confirm('确认删除事件吗?', '提示', {
+//       confirmButtonText: '确定',
+//       cancelButtonText: '取消',
+//       type: 'info'
+//     }).then(() => {
+//       eventList.value = eventList.value.filter((item) => item.eventId !== row.event_id);
+//       deleteEvent({ eventId: row.event_id }).then((res) => {
+//         proxy?.$modal.msgSuccess(res.msg);
+//         setTimeout(() => {
+//           getList();
+//         }, 500);
+//       });
+//     });
+//   }
+// };
+
+// 关闭事件
+const closeDialogState = reactive({
+  show: false,
+  form: {
+    eventId: '',
+    deaths: '',
+    injuries: '',
+    missing: '',
+    fileNames: []
+  }
+});
+
+const handleClose = (row) => {
+  if (row) {
+    console.log(row);
+    closeDialogState.form.eventId = row.event_id;
+    closeDialogState.form.deaths = row.deaths;
+    closeDialogState.form.injuries = row.injuries;
+    closeDialogState.form.missing = row.missing;
+    closeDialogState.show = true;
+  }
+};
+
+const handleCloseEventDialog = (b) => {
+  console.log('handleCloseEventDialog', b);
+};
+
+onMounted(() => {
+  getList();
+});
+
+interface Props {
+  modelValue: boolean;
+  title?: string;
+  height?: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+const emit = defineEmits(['update:modelValue']);
+
+// 关闭弹窗
+const closeDialog = () => {
+  emit('update:modelValue', false);
+};
+</script>
+
+<style lang="scss" scoped>
+.dialog-wrap {
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  z-index: 2000;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  .overlay {
+    background-color: rgba(0, 0, 0, 0.5);
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: -1;
+    cursor: pointer;
+  }
+  .dialog {
+    width: 5000px;
+    height: 2000px;
+    margin: 0 auto;
+    background-color: #fff;
+    border-radius: 20px;
+  }
+}
+.dialog {
+  padding: 0 50px;
+  .dialog-header {
+    width: 100%;
+    height: 150px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .icon-close {
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 79 - 31
src/views/routineCommandMap/LeftSection.vue

@@ -36,24 +36,28 @@
     <div class="card">
       <div class="card-header">
         <div>事件接报</div>
-        <div class="more" @click="showvideoMonitorList">查看更多</div>
+        <div class="more" @click="showMoreEventManageList">查看更多</div>
       </div>
       <div class="card-content" style="height: 100%">
         <table class="table">
           <thead>
             <tr class="tr">
-              <td v-for="(item, index) in inspectionNewsState.columns" :key="index" class="td">{{ item.label }}</td>
+              <td v-for="(item, index) in eventManageState.columns" :key="index" class="td">{{ item.label }}</td>
             </tr>
           </thead>
-          <tr v-for="(item, index) in inspectionNewsState.listData" :key="index" class="tr">
-            <td class="td">{{ item.name }}</td>
-            <td class="td">{{ item.type }}</td>
+          <tr v-for="(item, index) in eventList" :key="index" class="tr">
+            <td class="td">{{ item.event_title }}</td>
+            <td class="td">
+              <dict-tag :options="mm_event_type" :value="item.event_type" />
+            </td>
             <td class="td">{{ item.address }}</td>
-            <td class="td">{{ item.time }}</td>
-            <td class="td">{{ item.status }}</td>
+            <td class="td">{{ item.event_time }}</td>
+            <td class="td">
+              <dict-tag :options="mm_event_state" :value="item.event_status" />
+            </td>
             <td class="td">
               <div class="flex">
-                <div @click="enterCommand">进入指挥</div>
+                <div @click="enterCommand(item)">进入指挥</div>
               </div>
             </td>
           </tr>
@@ -63,13 +67,24 @@
   </div>
   <!--巡查消息弹窗-->
   <Dialog v-model="inspectionNewsState.showListDialog" title="巡查消息列表" width="70%"></Dialog>
+  <!--事件接报弹窗-->
+  <EventManage v-model="eventManageState.showListDialog" title="事件管理列表" width="70%"></EventManage>
 </template>
 
 <script lang="ts" setup>
 import Dialog from '@/components/Dialog/index.vue';
-
+import EventManage from '@/views/routineCommandMap/EventManage.vue';
+import { ref, reactive, toRefs } from 'vue';
+import { getEvent, deleteEvent } from '@/api/duty/eventing';
+const eventList = ref([]);
+const loading = ref(true);
+const total = ref(0);
 const router = useRouter();
 
+const proxy = getCurrentInstance()?.proxy;
+const { mm_event_type, mm_event_level, mm_event_state, region } = toRefs<any>(
+  proxy?.useDict('mm_event_type', 'mm_event_level', 'mm_event_state', 'region')
+);
 // 综合值守
 const comprehensiveDutyState = reactive({
   data: {
@@ -100,28 +115,69 @@ const showInspectionNewsState = () => {
   inspectionNewsState.showListDialog = true;
 };
 
+const initFormData = {
+  event_title: '', // 事件标题
+  event_type: '', // 事件类型
+  event_status: '', // 事件状态
+  address: '', // 事件地点
+  event_time: '' // 上报时间
+};
+// 表单数据
+const data = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    page: 1,
+    page_size: 3
+  },
+  event_levelSelection: [],
+  event_statusSelection: [],
+  regionSelection: []
+});
+
+const { queryParams, form } = toRefs(data);
+const getList = () => {
+  loading.value = true;
+  getEvent(queryParams.value)
+    .then((res) => {
+      eventList.value = res.data;
+      total.value = res.total;
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+};
 // 事件接报
-const eventReporting = reactive({
-  showListDialog: false,
+const eventManageState = reactive({
   columns: [
-    { label: '事件名称', key: 'name' },
-    { label: '事件类型', key: 'type' },
+    { label: '事件名称', key: 'event_title' },
+    { label: '事件类型', key: 'event_type' },
     { label: '地点', key: 'address' },
-    { label: '上报时间', key: 'time' },
-    { label: '事件状态', key: 'status' },
+    { label: '上报时间', key: 'report_time' },
+    { label: '事件状态', key: 'event_status' },
     { label: '操作', key: 'operate' }
   ],
-  listData: []
+  showListDialog: false,
+  // showDetailDialog: false,
+  getList: []
 });
-// 显示更多巡查消息
-const showEventReporting = () => {
-  eventReporting.showListDialog = true;
+// 显示事件管理列表弹窗
+const showMoreEventManageList = () => {
+  eventManageState.showListDialog = true;
 };
-
 // 进入应急态界面
-const enterCommand = () => {
-  router.push('/emergencyCommandMap');
+const enterCommand = (item) => {
+  if (item) {
+    // 查看事件详情逻辑
+    console.log('进入指挥', item);
+    router.push({
+      path: '/emergencyCommandMap',
+      query: { event_id: item.event_id }
+    });
+  }
 };
+// const enterCommand = () => {
+//   router.push('/emergencyCommandMap');
+// };
 
 // 初始化数据
 const initData = () => {
@@ -140,19 +196,11 @@ const initData = () => {
     { name: '巡检任务5', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', result: '正常', img: '' },
     { name: '巡检任务6', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', result: '正常', img: '' }
   ];
-  // 事件接报
-  eventReporting.listData = [
-    { name: '巡检任务1', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', status: '正常' },
-    { name: '巡检任务2', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', status: '正常' },
-    { name: '巡检任务3', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', status: '正常' },
-    { name: '巡检任务4', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', status: '正常' },
-    { name: '巡检任务5', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', status: '正常' },
-    { name: '巡检任务6', type: '城市隐患巡查', address: '油城十路', time: '2024-7-11 16:00:21', status: '正常' }
-  ];
 };
 // 加载完成事件
 onMounted(() => {
   initData();
+  getList();
 });
 </script>
 

+ 121 - 0
src/views/routineCommandMap/eventing/CloseEventDialog.vue

@@ -0,0 +1,121 @@
+<template>
+  <el-dialog ref="formDialogRef" :model-value="visible" title="关闭事件" width="750px" append-to-body>
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="伤亡情况">
+            <el-row>
+              <el-col :span="8">
+                <el-form-item label="死亡" prop="deaths">
+                  <el-input v-model="form.deaths">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="受伤" prop="injuries">
+                  <el-input v-model="form.injuries">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="失联" prop="missing">
+                  <el-input v-model="form.missing">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="总结报告" prop="fileNames">
+            <FileUpload v-model="form.fileNames" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="closeDialog">取 消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { closeEvent } from '@/api/duty/eventing';
+
+interface Form {
+  eventId: string;
+  deaths: string;
+  injuries: string;
+  missing: string;
+  fileNames?: any;
+}
+interface Props {
+  modelValue: boolean;
+  eventId: string;
+  data: Form;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const emits = defineEmits(['update:modelValue']);
+watch(
+  () => props.modelValue,
+  () => {
+    if (props.modelValue) {
+      form.value = props.data;
+    }
+    visible.value = props.modelValue;
+  }
+);
+
+const visible = ref(false);
+const form = ref<Form>({
+  eventId: '',
+  deaths: '',
+  injuries: '',
+  missing: '',
+  fileNames: []
+});
+
+const validateFileCount = (rule, value, callback) => {
+  if (!value || value.length == 0) {
+    return callback(new Error('总结报告不能为空'));
+  }
+  callback();
+};
+
+const rules = reactive({
+  deaths: [{ required: true, message: '死亡人数不能为空', trigger: 'blur' }],
+  injuries: [{ required: true, message: '受伤人数不能为空', trigger: 'blur' }],
+  missing: [{ required: true, message: '失踪人数不能为空', trigger: 'blur' }],
+  fileNames: [{ validator: validateFileCount, message: '总结报告不能为空', trigger: 'blur' }]
+});
+const formRef = ref();
+const buttonLoading = ref(false);
+
+// 关闭事件
+const submitForm = () => {
+  formRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      closeEvent({ eventId: props.eventId, ...form.value }).then((res) => {
+        proxy?.$modal.msgSuccess(res.msg);
+        closeDialog();
+      });
+    }
+  });
+};
+
+const closeDialog = () => {
+  emits('update:modelValue');
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 106 - 0
src/views/routineCommandMap/eventing/EditCasualtiesDialog.vue

@@ -0,0 +1,106 @@
+<template>
+  <el-dialog ref="formDialogRef" :model-value="visible" title="上报伤亡情况" width="750px" append-to-body>
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="伤亡情况">
+            <el-row>
+              <el-col :span="8">
+                <el-form-item label="死亡" prop="deaths">
+                  <el-input v-model="form.deaths">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="受伤" prop="injuries">
+                  <el-input v-model="form.injuries">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="失联" prop="missing">
+                  <el-input v-model="form.missing">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确定</el-button>
+        <el-button @click="closeDialog">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { uploadEventCasualties } from '@/api/duty/eventing';
+
+interface Form {
+  eventId: string;
+  deaths: string;
+  injuries: string;
+  missing: string;
+}
+interface Props {
+  modelValue: boolean;
+  eventId: string;
+  data: Form;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const emits = defineEmits(['update:modelValue']);
+watch(
+  () => props.modelValue,
+  () => {
+    if (props.modelValue) {
+      form.value = props.data;
+    }
+    visible.value = props.modelValue;
+  }
+);
+
+const visible = ref(false);
+const form = ref<Form>({
+  eventId: '',
+  deaths: '',
+  injuries: '',
+  missing: ''
+});
+
+const rules = reactive({
+  deaths: [{ required: true, message: '死亡人数不能为空', trigger: 'blur' }],
+  injuries: [{ required: true, message: '受伤人数不能为空', trigger: 'blur' }],
+  missing: [{ required: true, message: '失踪人数不能为空', trigger: 'blur' }]
+});
+const formRef = ref();
+const buttonLoading = ref(false);
+
+// 关闭事件
+const submitForm = () => {
+  formRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      uploadEventCasualties({ eventId: props.eventId, ...form.value }).then((res) => {
+        proxy?.$modal.msgSuccess(res.msg);
+        closeDialog();
+      });
+    }
+  });
+};
+
+const closeDialog = () => {
+  emits('update:modelValue');
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 235 - 0
src/views/routineCommandMap/eventing/EventEditDialog.vue

@@ -0,0 +1,235 @@
+<template>
+  <el-dialog ref="formDialogRef" :model-value="visible" :title="props.title" width="650px" append-to-body @close="closeDialog">
+    <el-form ref="eventFormRef" :model="form" :rules="rules" label-width="80px">
+      <el-form-item label="事件标题" prop="event_title">
+        <el-input v-model="form.event_title" placeholder="请输入事件标题" />
+      </el-form-item>
+
+      <el-form-item label="事件类型" prop="event_type">
+        <el-select v-model="form.event_type" placeholder="请选择事件类型" clearable>
+          <el-option v-for="item in mm_event_type" :key="item.value" :label="item.label" :value="item.value"></el-option>
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="事件等级" prop="event_level">
+        <el-select v-model="form.event_level" placeholder="请选择事件等级" clearable style="width: calc(100% - 30px)">
+          <el-option v-for="item in mm_event_level" :key="item.value" :label="item.label" :value="item.value"></el-option>
+        </el-select>
+        <el-icon size="large" style="margin-left: 8px" @click="eventLevelInfoDialogVisible = true"><WarningFilled /></el-icon>
+      </el-form-item>
+
+      <el-form-item label="事件状态" prop="event_status">
+        <el-select v-model="form.event_status" placeholder="请选择事件状态" clearable :readonly="true">
+          <el-option v-for="item in mm_event_state" :key="item.value" :label="item.label" :value="item.value"></el-option>
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="事发时间" prop="event_time">
+        <el-date-picker v-model="form.event_time" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择事发时间"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item label="上报时间" prop="report_time">
+        <el-date-picker v-model="form.report_time" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" placeholder="选择上报时间"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item label="伤亡情况">
+        <el-row :gutter="10">
+          <el-col :span="6">
+            <el-switch
+              v-model="form.casualties"
+              size="large"
+              inline-prompt
+              active-text="已上报"
+              inactive-text="未上报"
+              active-value="1"
+              inactive-value="0"
+            />
+          </el-col>
+          <el-col v-show="form.casualties === '1'" :span="4">
+            <el-input v-model="form.deaths" placeholder="死亡">
+              <template #suffix>人</template>
+            </el-input>
+          </el-col>
+          <el-col v-show="form.casualties === '1'" :span="4">
+            <el-input v-model="form.injuries" placeholder="受伤">
+              <template #suffix>人</template>
+            </el-input>
+          </el-col>
+          <el-col v-show="form.casualties === '1'" :span="4">
+            <el-input v-model="form.missing" placeholder="失踪">
+              <template #suffix>人</template>
+            </el-input>
+          </el-col>
+        </el-row>
+      </el-form-item>
+
+      <el-form-item label="联系方式" prop="contact">
+        <el-input v-model="form.contact" placeholder="请输入联系方式" />
+      </el-form-item>
+
+      <el-form-item label="事件来源" prop="event_source">
+        <el-input v-model="form.event_source" placeholder="请输入事件来源" />
+      </el-form-item>
+
+      <el-form-item label="事件地点" prop="address">
+        <el-input v-model="form.address" placeholder="请输入事件地点" readonly>
+          <template #append>
+            <el-button type="primary" @click="openMapDialog">地图定位</el-button>
+          </template>
+        </el-input>
+      </el-form-item>
+
+      <el-form-item label="事件描述" prop="event_description">
+        <el-input v-model="form.event_description" type="textarea" placeholder="请输入事件描述" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button :loading="buttonLoading" type="primary" @click="submitForm">确定</el-button>
+        <el-button @click="closeDialog">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+
+  <!-- 事故等级弹窗 -->
+  <EventLevelInfoDialog v-model="eventLevelInfoDialogVisible" @close-dialog="closeEventLevelInfoDialog" />
+
+  <!-- 地图弹窗 -->
+  <company-map v-model:visible="mapDialogVisible" :address="form.address" @confirm="handleMapChange"></company-map>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, toRefs } from 'vue';
+import { addEvent, editEvent, postEditEvent } from '@/api/duty/eventing';
+import EventLevelInfoDialog from './EventLevelInfoDialog.vue';
+
+const proxy = getCurrentInstance()?.proxy;
+const { mm_event_type, mm_event_level, mm_event_state, region } = toRefs<any>(
+  proxy?.useDict('mm_event_type', 'mm_event_level', 'mm_event_state', 'region')
+);
+
+const buttonLoading = ref(false);
+const eventFormRef = ref();
+const visible = ref(false);
+
+interface Props {
+  modelValue: boolean;
+  eventId: string;
+  title: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+
+const emits = defineEmits(['update:modelValue']);
+watch(
+  () => props.modelValue,
+  () => {
+    // 界面显示初始化
+    if (props.modelValue) {
+      if (props.eventId != '') {
+        editEvent({ event_id: props.eventId }).then((res) => {
+          form.value = res.data;
+        });
+      } else {
+        form.value = { ...initFormData };
+        eventFormRef.value?.resetFields();
+      }
+    }
+    visible.value = props.modelValue;
+  }
+);
+
+// 表单初始数据
+const initFormData = {
+  event_title: '', // 事件标题
+  event_type: '', // 事件类型
+  event_level: '', // 事件等级
+  event_status: '0', // 事件状态
+  address: '', // 事件地点
+  longitude: '', // 经度
+  latitude: '', // 纬度
+  event_time: '', // 事发时间
+  report_time: '', // 上报时间
+  casualties: '0', // 伤亡情况
+  deaths: '',
+  injuries: '',
+  missing: '',
+  event_source: '', // 事件来源
+  event_description: '', // 事件描述
+  contact: '' // 联系方式
+};
+
+// 表单数据
+const data = reactive({
+  form: { ...initFormData },
+  rules: {
+    event_title: [{ required: true, message: '事件标题不能为空', trigger: 'blur' }],
+    event_type: [{ required: true, message: '事件类型不能为空', trigger: 'blur' }],
+    event_level: [{ required: true, message: '事件等级不能为空', trigger: 'blur' }],
+    event_status: [{ required: true, message: '事件状态不能为空', trigger: 'blur' }],
+    address: [{ required: true, message: '事件地点不能为空', trigger: 'blur' }],
+    event_time: [{ required: true, message: '事发时间不能为空', trigger: 'blur' }],
+    report_time: [{ required: true, message: '上报时间不能为空', trigger: 'blur' }],
+    event_source: [{ required: true, message: '事件来源不能为空', trigger: 'blur' }],
+    event_description: [{ required: true, message: '事件描述不能为空', trigger: 'blur' }],
+    contact: [{ required: true, message: '联系方式不能为空', trigger: 'blur' }]
+  }
+});
+
+const { form, rules } = toRefs(data);
+
+// 提交表单
+const submitForm = () => {
+  eventFormRef.value?.validate((valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      // 打印表单数据
+      console.log('表单数据', form.value);
+      if (props.eventId === '') {
+        addEvent(form.value)
+          .then((res) => {
+            proxy?.$modal.msgSuccess(res.msg);
+            closeDialog();
+          })
+          .finally(() => {
+            buttonLoading.value = false;
+          });
+      } else {
+        postEditEvent(form.value)
+          .then((res) => {
+            proxy?.$modal.msgSuccess(res.msg);
+            closeDialog();
+          })
+          .finally(() => {
+            buttonLoading.value = false;
+          });
+      }
+    }
+  });
+};
+
+const closeDialog = () => {
+  emits('update:modelValue', false);
+};
+
+// 地图定位
+const mapDialogVisible = ref(false);
+const openMapDialog = () => {
+  mapDialogVisible.value = true;
+};
+
+const handleMapChange = (data) => {
+  form.value.address = data.address;
+  form.value.longitude = data.lnglat[0];
+  form.value.latitude = data.lnglat[1];
+  mapDialogVisible.value = false;
+};
+
+// 事故等级弹窗
+const eventLevelInfoDialogVisible = ref(false);
+
+const closeEventLevelInfoDialog = () => {
+  eventLevelInfoDialogVisible.value = false;
+};
+</script>

+ 30 - 0
src/views/routineCommandMap/eventing/EventLevelInfoDialog.vue

@@ -0,0 +1,30 @@
+<template>
+  <el-dialog title="突发事故分级标准" width="450px" append-to-body @close="closeDialog">
+    <p><strong>一般突发事故:</strong>是指造成3人以下死亡(含失踪),或者10人以下重伤,或者1000万元以下直接经济损失,或产生一定社会影响的。</p>
+    <p>
+      <strong>较大突发事故:</strong
+      >是指造成3人以上10人以下死亡(含失踪),或者10人以上50人以下重伤,或者1000万元以上5000万元以下直接经济损失,或产生较大社会影响的。
+    </p>
+    <p>
+      <strong>重大突发事故:</strong
+      >是指造成10人以上30人以下死亡(含失踪),或者50人以上100人以下重伤,或者5000万元以上1亿元以下直接经济损失,或产生重大社会影响的。
+    </p>
+    <p>
+      <strong>特别重大突发事故:</strong
+      >是指造成30人以上死亡(含失踪),或者100人以上重伤(包括急性工业中毒),或者1亿元以上直接经济损失,或产生特别重大社会影响的。
+    </p>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="closeDialog">关闭</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { defineEmits } from 'vue';
+const emits = defineEmits(['closeDialog']);
+const closeDialog = () => {
+  emits('closeDialog');
+};
+</script>

+ 131 - 0
src/views/routineCommandMap/eventing/StartEventDialog.vue

@@ -0,0 +1,131 @@
+<template>
+  <el-dialog ref="formDialogRef" :model-value="visible" title="开始指挥" width="550px" append-to-body @close="closeDialog">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="事件等级" prop="event_level">
+            <el-select v-model="form.event_level" placeholder="请选择事件等级" clearable style="width: calc(100% - 30px)">
+              <el-option v-for="item in mm_event_level" :key="item.value" :label="item.label" :value="item.value"></el-option>
+            </el-select>
+            <el-icon size="large" style="margin-left: 8px" @click="eventLevelInfoDialogVisible = true"><WarningFilled /></el-icon>
+          </el-form-item>
+        </el-col>
+        <el-col v-show="false" :span="24">
+          <el-form-item label="伤亡情况">
+            <el-row>
+              <el-col :span="8">
+                <el-form-item label="死亡" prop="deaths">
+                  <el-input v-model="form.deaths">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="受伤" prop="injuries">
+                  <el-input v-model="form.injuries">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="失联" prop="missing">
+                  <el-input v-model="form.missing">
+                    <template #append>人</template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="closeDialog">取 消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+
+  <EventLevelInfoDialog v-model="eventLevelInfoDialogVisible" @close-dialog="closeEventLevelInfoDialog" />
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, toRefs } from 'vue';
+import { startEvent } from '@/api/duty/eventing';
+import EventLevelInfoDialog from './EventLevelInfoDialog.vue';
+
+const proxy = getCurrentInstance()?.proxy;
+const { mm_event_level } = toRefs<any>(proxy?.useDict('mm_event_level'));
+
+interface Form {
+  eventId: string;
+  event_level: string;
+  deaths: string;
+  injuries: string;
+  missing: string;
+}
+interface Props {
+  modelValue: boolean;
+  eventId: string;
+  data: Form;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+
+const emits = defineEmits(['update:modelValue']);
+watch(
+  () => props.modelValue,
+  () => {
+    if (props.modelValue) {
+      form.value = props.data;
+    }
+    visible.value = props.modelValue;
+  }
+);
+const router = useRouter();
+const visible = ref(false);
+const form = ref<Form>({
+  eventId: '',
+  event_level: '',
+  deaths: '',
+  injuries: '',
+  missing: ''
+});
+const rules = reactive({
+  event_level: [{ required: true, message: '事件等级不能为空', trigger: 'blur' }],
+  deaths: [{ required: true, message: '死亡人数不能为空', trigger: 'blur' }],
+  injuries: [{ required: true, message: '受伤人数不能为空', trigger: 'blur' }],
+  missing: [{ required: true, message: '失踪人数不能为空', trigger: 'blur' }]
+});
+const formRef = ref();
+const buttonLoading = ref(false);
+
+// 关闭事件
+const submitForm = () => {
+  console.log('submitForm');
+  formRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      startEvent({ eventId: props.eventId, ...form.value }).then(() => {
+        proxy?.$modal.msgSuccess('开始指挥');
+        closeDialog();
+      });
+    } else {
+    }
+  });
+};
+
+const closeDialog = () => {
+  emits('update:modelValue', false);
+};
+
+const eventLevelInfoDialogVisible = ref(false);
+
+const closeEventLevelInfoDialog = () => {
+  eventLevelInfoDialogVisible.value = false;
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 359 - 0
src/views/routineCommandMap/eventing/eventDetails.vue

@@ -0,0 +1,359 @@
+<template>
+  <div v-if="modelValue" class="dialog-wrap">
+    <div class="overlay" @click="closeDialog"></div>
+    <div class="dialog" :style="{ height: height }">
+      <div class="dialog-header">
+        <div class="icon-close" @click="closeDialog">
+          <el-icon size="100px"><Close /></el-icon>
+        </div>
+      </div>
+      <div class="dialog-content">
+        <slot />
+        <div class="app-container">
+          <div class="line">
+            <div class="title">{{ detailData.event_title }}</div>
+          </div>
+          <div class="sub-title">基础信息</div>
+          <div class="line2">
+            <div class="line-item">
+              <div class="item-label">事件编号:</div>
+              <div class="item-value">{{ detailData.event_id }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">事件类型:</div>
+              <div class="item-value">
+                <dict-tag :options="mm_event_type" :value="detailData.event_type" />
+              </div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">事件等级:</div>
+              <div class="item-value" style="display: flex; align-items: center">
+                <dict-tag :options="mm_event_level" :value="detailData.event_level" />
+                <el-icon style="margin-left: 20px; cursor: pointer" @click="handleEventLevelOpen"><Fold /></el-icon>
+              </div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">事件状态:</div>
+              <div class="item-value">
+                <dict-tag :options="mm_event_state" :value="detailData.event_status" />
+              </div>
+            </div>
+          </div>
+          <div class="line2">
+            <div class="line-item">
+              <div class="item-label">事发地点:</div>
+              <div class="item-value link">{{ detailData.address }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">事发时间:</div>
+              <div class="item-value">{{ detailData.event_time }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">上报时间:</div>
+              <div class="item-value">{{ detailData.report_time }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">伤亡情况:</div>
+              <div v-if="detailData.casualties == '0'" class="flex">
+                <span>未上报</span>
+                <span class="link" style="margin-left: 20px" @click="uploadeCasualties">去上报</span>
+              </div>
+              <div v-if="detailData.casualties == '1'" class="flex">
+                <span>已上报</span>
+              </div>
+            </div>
+          </div>
+          <div class="line2">
+            <div class="line-item">
+              <div class="item-label">登记人:</div>
+              <div class="item-value">{{ detailData.reported_by }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">登记时间:</div>
+              <div class="item-value">{{ detailData.report_time }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">联系方式:</div>
+              <div class="item-value">{{ detailData.contact }}</div>
+            </div>
+            <div class="line-item">
+              <div class="item-label">事件来源:</div>
+              <div class="item-value">{{ detailData.event_source }}</div>
+            </div>
+          </div>
+          <div class="sub-title">事件概要</div>
+          <div class="textarea">{{ detailData.event_description }}</div>
+
+          <div class="sub-title">事件跟踪</div>
+
+          <el-steps :active="eventTrackState.active" :align-center="true" style="width: 100%">
+            <el-step v-for="(item, index) in eventTrackState.items" :key="index" :title="item.title" :description="item.description"></el-step>
+          </el-steps>
+
+          <!--          <div class="sub-title">指挥记录</div>-->
+          <!--          <div class="list">-->
+          <!--            <div class="list-item">-->
+          <!--              <i class="img"></i>-->
+          <!--              <div class="item-title">大屏指挥记录</div>-->
+          <!--            </div>-->
+          <!--            <div class="list-item">-->
+          <!--              <i class="img"></i>-->
+          <!--              <div class="item-title">中屏指挥记录</div>-->
+          <!--            </div>-->
+          <!--          </div>-->
+          <!--          <div class="sub-title">匹配预案</div>-->
+          <!--          <div class="list">-->
+          <!--            <div class="list-item2">{{ detailData.plan_name }}</div>-->
+          <!--          </div>-->
+          <!--          <div class="sub-title">事件总结报告</div>-->
+          <!--          <div class="list2">-->
+          <!--            <div v-for="(item, index) in summaryFiles" :key="index" class="list-item" @click="downloadSummaryFile(item.url)">-->
+          <!--              <div class="link">{{ item.file_name }}</div>-->
+          <!--              <el-icon class="icon"><Download /></el-icon>-->
+          <!--            </div>-->
+          <!--          </div>-->
+          <el-dialog v-model="eventLevelState.show" title="事件等级" width="500" :before-close="handleEventLevelClose">
+            <el-timeline>
+              <el-timeline-item
+                v-for="(item, index) in eventLevelState.data"
+                :key="index"
+                :timestamp="item.timestamp + '&nbsp;&nbsp;&nbsp;&nbsp;' + item.name"
+                placement="top"
+              >
+                <div class="dict-item">
+                  <dict-tag :options="mm_event_level" :value="item.level" />
+                </div>
+              </el-timeline-item>
+            </el-timeline>
+          </el-dialog>
+          <EditCasualtiesDialog
+            v-model="editCasualtiesDialogState.show"
+            :data="editCasualtiesDialogState.form"
+            :event-id="eventId"
+            @update:model-value="fetchEventDetail"
+          />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getEventDetail, deleteEvent } from '@/api/duty/eventing';
+import EditCasualtiesDialog from './EditCasualtiesDialog.vue';
+import { ref } from 'vue';
+
+const route = useRoute();
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { mm_event_type, mm_event_level, mm_event_state, region } = toRefs<any>(
+  proxy?.useDict('mm_event_type', 'mm_event_level', 'mm_event_state', 'region')
+);
+// 事件等级数据
+let eventLevelState = reactive({
+  show: false,
+  data: [
+    {
+      timestamp: '2024/7/1 09:00:09',
+      name: '张佳佳',
+      level: '4'
+    },
+    {
+      timestamp: '2024/7/1 09:00:09',
+      name: '张佳佳',
+      level: '3'
+    },
+    {
+      timestamp: '2024/7/1 09:00:09',
+      name: '张佳佳',
+      level: '2'
+    },
+    {
+      timestamp: '2024/7/1 09:00:09',
+      name: '张佳佳',
+      level: '1'
+    }
+  ]
+});
+
+const handleEventLevelOpen = () => {
+  eventLevelState.show = true;
+};
+const handleEventLevelClose = () => {
+  eventLevelState.show = false;
+};
+
+// 下载总结报告
+let summaryFiles = ref([]);
+const downloadSummaryFile = (url) => {
+  window.open(url);
+};
+
+// 事件跟踪数据
+let eventTrackState = reactive({
+  active: 0,
+  items: [
+    {
+      title: '事件登记',
+      description: ''
+    },
+    {
+      title: '进入指挥',
+      description: ''
+    },
+    {
+      title: '结束指挥',
+      description: ''
+    },
+    {
+      title: '关闭事件',
+      description: ''
+    }
+  ]
+});
+interface Form {
+  eventId: '';
+  event_id: '';
+  event_title: '';
+  event_type: '';
+  event_level: '';
+  event_status: '';
+  event_time: '';
+  address: '';
+  report_time: '';
+  event_source: '';
+  reported_by: '';
+  event_description: '';
+  contact: '';
+  plan_name: '';
+  casualties: '';
+  deaths: '';
+  injuries: '';
+  missing: '';
+  del_flag: '';
+}
+interface Props {
+  modelValue: boolean;
+  eventId: string;
+  data: Form;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+const emits = defineEmits(['update:modelValue']);
+const detailData = ref<Form>({
+  eventId: '',
+  event_id: '',
+  event_title: '',
+  event_type: '',
+  event_level: '',
+  event_status: '',
+  event_time: '',
+  address: '',
+  report_time: '',
+  event_source: '',
+  reported_by: '',
+  event_description: '',
+  contact: '',
+  plan_name: '',
+  casualties: '',
+  deaths: '',
+  injuries: '',
+  missing: '',
+  del_flag: ''
+});
+const editCasualtiesDialogState = reactive({
+  show: false,
+  form: {
+    eventId: '',
+    deaths: '',
+    injuries: '',
+    missing: ''
+  }
+});
+// 上报伤亡情况
+const uploadeCasualties = () => {
+  editCasualtiesDialogState.form.eventId = props.eventId;
+  editCasualtiesDialogState.form.deaths = '';
+  editCasualtiesDialogState.form.injuries = '';
+  editCasualtiesDialogState.form.missing = '';
+  editCasualtiesDialogState.show = true;
+};
+const fetchEventDetail = () => {
+  console.log('fetchEventDetail');
+  editCasualtiesDialogState.show = false;
+  getEventDetail({ event_id: props.eventId }).then((res) => {
+    detailData.value = res.data;
+    eventTrackState = res.data.event_status_tracks;
+    eventLevelState.data = res.data.event_level_tracks;
+    summaryFiles.value = res.data.summary_file;
+  });
+};
+
+onMounted(() => {
+  // eventId.value = route.query.event_id as string;
+  fetchEventDetail();
+  getEventDetail(props.eventId);
+  // });
+});
+// 关闭弹窗
+const closeDialog = () => {
+  emits('update:modelValue', false);
+  // detailData.value = { eventId: '' };
+  fetchEventDetail();
+};
+</script>
+
+<style lang="scss" scoped>
+.dialog-wrap {
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  z-index: 2000;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  .overlay {
+    background-color: rgba(0, 0, 0, 0.5);
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: -1;
+    cursor: pointer;
+  }
+  .dialog {
+    width: 5000px;
+    height: 2000px;
+    margin: 0 auto;
+    background-color: #fff;
+    border-radius: 20px;
+  }
+}
+.dialog {
+  padding: 0 50px;
+  .dialog-header {
+    width: 100%;
+    height: 150px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .icon-close {
+      cursor: pointer;
+    }
+  }
+}
+.dialog-content {
+  background-color: #fff;
+  border-radius: 5px;
+  padding: 20px;
+  max-width: 100%;
+  max-height: 88%;
+  overflow: auto; /* 启用滚动 */
+}
+</style>