Procházet zdrojové kódy

事件管理增加

luogang před 1 měsícem
rodič
revize
ba5c2885fa
5 změnil soubory, kde provedl 547 přidání a 157 odebrání
  1. 60 0
      src/api/event/index.ts
  2. 1 1
      src/permission.ts
  3. 161 156
      src/router/index.ts
  4. 324 0
      src/views/eventManage/index.vue
  5. 1 0
      vite.config.ts

+ 60 - 0
src/api/event/index.ts

@@ -0,0 +1,60 @@
+import request from '@/utils/request';
+
+/**
+ * 查询事件列表
+ * @param query
+ * @returns {*}
+ */
+export const listEvent = (query) => {
+    return request({
+        url: '/system/event/list',
+        method: 'get',
+        params: query
+    });
+};
+/**
+ * 查询事件信息详细
+ * @param id
+ */
+export const getEvent = (id) => {
+    return request({
+        url: '/system/event/' + id,
+        method: 'get'
+    });
+};
+
+/**
+ * 新增事件信息
+ * @param data
+ */
+export const addEvent = (data) => {
+    return request({
+        url: '/system/event',
+        method: 'post',
+        data: data
+    });
+};
+
+/**
+ * 修改事件信息
+ * @param data
+ */
+export const updateEvent = (data) => {
+    return request({
+        url: '/system/event',
+        method: 'put',
+        data: data
+    });
+};
+
+/**
+ * 删除事件信息
+ * @param id
+ */
+export const delEvent = (id: string | number | Array<string | number>) => {
+    return request({
+        url: '/system/event/' + id,
+        method: 'delete'
+    });
+};
+

+ 1 - 1
src/permission.ts

@@ -10,7 +10,7 @@ import useSettingsStore from '@/store/modules/settings';
 import usePermissionStore from '@/store/modules/permission';
 
 NProgress.configure({ showSpinner: false });
-const whiteList = ['/login', '/register', '/social-callback'];
+const whiteList = ['/login', '/register', '/social-callback', '/event'];
 
 router.beforeEach(async (to, from, next) => {
   NProgress.start();

+ 161 - 156
src/router/index.ts

@@ -26,173 +26,178 @@ import Layout from '@/layout/index.vue';
 
 // 公共路由
 export const constantRoutes: RouteRecordRaw[] = [
-    {
-        path: '/redirect',
-        component: Layout,
-        hidden: true,
-        children: [
-            {
-                path: '/redirect/:path(.*)',
-                component: () => import('@/views/redirect/index.vue')
-            }
-        ]
-    },
-    {
-        path: '/social-callback',
-        hidden: true,
-        component: () => import('@/layout/components/SocialCallback/index.vue')
-    },
-    {
-        path: '/login',
-        component: () => import('@/views/login.vue'),
-        hidden: true
-    },
-    {
-        path: '/register',
-        component: () => import('@/views/register.vue'),
-        hidden: true
-    },
-    {
-        path: '/:pathMatch(.*)*',
-        component: () => import('@/views/error/404.vue'),
-        hidden: true
-    },
-    {
-        path: '/401',
-        component: () => import('@/views/error/401.vue'),
-        hidden: true
-    },
-    {
-        path: '',
-        component: Layout,
-        redirect: '/index',
-        children: [
-            {
-                path: '/index',
-                component: () => import('@/views/dashboard/index.vue'),
-                name: 'Index',
-                meta: { title: '首页', icon: 'dashboard', affix: true }
-            }
-        ]
-    },
-    {
-        path: '/user',
-        component: Layout,
-        hidden: true,
-        redirect: 'noredirect',
-        children: [
-            {
-                path: 'profile',
-                component: () => import('@/views/system/user/profile/index.vue'),
-                name: 'Profile',
-                meta: { title: '个人中心', icon: 'user' }
-            }
-        ]
-    }
+  {
+    path: '/redirect',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: '/redirect/:path(.*)',
+        component: () => import('@/views/redirect/index.vue')
+      }
+    ]
+  },
+  {
+    path: '/social-callback',
+    hidden: true,
+    component: () => import('@/layout/components/SocialCallback/index.vue')
+  },
+  {
+    path: '/login',
+    component: () => import('@/views/login.vue'),
+    hidden: true
+  },
+  {
+    path: '/register',
+    component: () => import('@/views/register.vue'),
+    hidden: true
+  },
+  {
+    path: '/:pathMatch(.*)*',
+    component: () => import('@/views/error/404.vue'),
+    hidden: true
+  },
+  {
+    path: '/401',
+    component: () => import('@/views/error/401.vue'),
+    hidden: true
+  },
+  {
+    path: '/event',
+    component: () => import('@/views/eventManage/index.vue'),
+    hidden: true
+  },
+  {
+    path: '',
+    component: Layout,
+    redirect: '/index',
+    children: [
+      {
+        path: '/index',
+        component: () => import('@/views/dashboard/index.vue'),
+        name: 'Index',
+        meta: { title: '首页', icon: 'dashboard', affix: true }
+      }
+    ]
+  },
+  {
+    path: '/user',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'profile',
+        component: () => import('@/views/system/user/profile/index.vue'),
+        name: 'Profile',
+        meta: { title: '个人中心', icon: 'user' }
+      }
+    ]
+  }
 ];
 
 // 动态路由,基于用户权限动态去加载
 export const dynamicRoutes: RouteRecordRaw[] = [
-    {
-        path: '/system/user-auth',
-        component: Layout,
-        hidden: true,
-        permissions: ['system:user:edit'],
-        children: [
-            {
-                path: 'role/:userId(\\d+)',
-                component: () => import('@/views/system/user/authRole.vue'),
-                name: 'AuthRole',
-                meta: { title: '分配角色', activeMenu: '/system/user', icon: '' }
-            }
-        ]
-    },
-    {
-        path: '/system/role-auth',
-        component: Layout,
-        hidden: true,
-        permissions: ['system:role:edit'],
-        children: [
-            {
-                path: 'user/:roleId(\\d+)',
-                component: () => import('@/views/system/role/authUser.vue'),
-                name: 'AuthUser',
-                meta: { title: '分配用户', activeMenu: '/system/role', icon: '' }
-            }
-        ]
-    },
-    {
-        path: '/system/dict-data',
-        component: Layout,
-        hidden: true,
-        permissions: ['system:dict:list'],
-        children: [
-            {
-                path: 'index/:dictId(\\d+)',
-                component: () => import('@/views/system/dict/data.vue'),
-                name: 'Data',
-                meta: { title: '字典数据', activeMenu: '/system/dict', icon: '' }
-            }
-        ]
-    },
-    {
-        path: '/system/oss-config',
-        component: Layout,
-        hidden: true,
-        permissions: ['system:ossConfig:list'],
-        children: [
-            {
-                path: 'index',
-                component: () => import('@/views/system/oss/config.vue'),
-                name: 'OssConfig',
-                meta: { title: '配置管理', activeMenu: '/system/oss', icon: '' }
-            }
-        ]
-    },
-    {
-        path: '/tool/gen-edit',
-        component: Layout,
-        hidden: true,
-        permissions: ['tool:gen:edit'],
-        children: [
-            {
-                path: 'index/:tableId(\\d+)',
-                component: () => import('@/views/tool/gen/editTable.vue'),
-                name: 'GenEdit',
-                meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '', noCache: true }
-            }
-        ]
-    },
-    {
-        path: '/workflow/leaveEdit',
-        component: Layout,
-        hidden: true,
-        permissions: ['workflow:leave:edit'],
-        children: [
-            {
-                path: 'index',
-                component: () => import('@/views/workflow/leave/leaveEdit.vue'),
-                name: 'leaveEdit',
-                meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
-            }
-        ]
-    }
+  {
+    path: '/system/user-auth',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:user:edit'],
+    children: [
+      {
+        path: 'role/:userId(\\d+)',
+        component: () => import('@/views/system/user/authRole.vue'),
+        name: 'AuthRole',
+        meta: { title: '分配角色', activeMenu: '/system/user', icon: '' }
+      }
+    ]
+  },
+  {
+    path: '/system/role-auth',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:role:edit'],
+    children: [
+      {
+        path: 'user/:roleId(\\d+)',
+        component: () => import('@/views/system/role/authUser.vue'),
+        name: 'AuthUser',
+        meta: { title: '分配用户', activeMenu: '/system/role', icon: '' }
+      }
+    ]
+  },
+  {
+    path: '/system/dict-data',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:dict:list'],
+    children: [
+      {
+        path: 'index/:dictId(\\d+)',
+        component: () => import('@/views/system/dict/data.vue'),
+        name: 'Data',
+        meta: { title: '字典数据', activeMenu: '/system/dict', icon: '' }
+      }
+    ]
+  },
+  {
+    path: '/system/oss-config',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:ossConfig:list'],
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/system/oss/config.vue'),
+        name: 'OssConfig',
+        meta: { title: '配置管理', activeMenu: '/system/oss', icon: '' }
+      }
+    ]
+  },
+  {
+    path: '/tool/gen-edit',
+    component: Layout,
+    hidden: true,
+    permissions: ['tool:gen:edit'],
+    children: [
+      {
+        path: 'index/:tableId(\\d+)',
+        component: () => import('@/views/tool/gen/editTable.vue'),
+        name: 'GenEdit',
+        meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/workflow/leaveEdit',
+    component: Layout,
+    hidden: true,
+    permissions: ['workflow:leave:edit'],
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/workflow/leave/leaveEdit.vue'),
+        name: 'leaveEdit',
+        meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
+      }
+    ]
+  }
 ];
 
 /**
  * 创建路由
  */
 const router = createRouter({
-    history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
-    routes: constantRoutes,
-    // 刷新时,滚动条位置还原
-    scrollBehavior(to, from, savedPosition) {
-        if (savedPosition) {
-            return savedPosition;
-        } else {
-            return { top: 0 };
-        }
+  history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
+  routes: constantRoutes,
+  // 刷新时,滚动条位置还原
+  scrollBehavior(to, from, savedPosition) {
+    if (savedPosition) {
+      return savedPosition;
+    } else {
+      return { top: 0 };
     }
+  }
 });
 
 export default router;

+ 324 - 0
src/views/eventManage/index.vue

@@ -0,0 +1,324 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="hover">
+      <el-form ref="queryFormRef" style="margin: 20px 0;" :model="queryParams" :inline="true" label-suffix=":"
+        label-width="90px">
+        <el-form-item label="事件时间" prop="createTime">
+          <el-date-picker v-model="queryParams.createTime" type="daterange" unlink-panels range-separator="-"
+            start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+        </el-form-item>
+        <el-form-item label="事件等级" prop="level">
+          <el-select v-model="queryParams.level" clearable placeholder="请选择事件等级">
+            <el-option v-for="dict in event_level" :key="dict.value" :label="dict.label"
+              :value="dict.value"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="resetQuery">重置</el-button>
+          <el-button type="primary" color="#0F79EE" @click="handleQuery">查询</el-button>
+          <el-button type="success" @click="handleAdd">新增</el-button>
+        </el-form-item>
+      </el-form>
+      <el-table v-loading="loading" :data="tableList">
+        <el-table-column label="事件时间" width="150" prop="createTimeFormat" show-overflow-tooltip />
+        <el-table-column label="发生地点" width="150" prop="addr" show-overflow-tooltip />
+        <el-table-column label="事件详情" width="300">
+          <template #default="{ row }">
+            <div style="display: flex;align-items: center;justify-content: space-between;">
+              <span>{{ row.content || '-' }}</span>
+              <el-image v-if="row.imgUrls" style="width: 35px; height: 35px;cursor: pointer;"
+                :src="row.imgUrls[0].realUrl" :preview-src-list="row.imgUrls.map(item => item.realUrl)" fit="cover"
+                :preview-teleported="true" />
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="事件等级" prop="level">
+          <template #default="scope">
+            <dict-tag :options="event_level" :value="scope.row.level" />
+          </template>
+        </el-table-column>
+        <el-table-column label="建议处置部门" width="150" prop="processingDeptName" show-overflow-tooltip />
+        <el-table-column label="建议措施" width="150" prop="recomEvent" show-overflow-tooltip />
+        <el-table-column label="应急预案" width="150" prop="emergencyPlan" show-overflow-tooltip />
+        <el-table-column label="操作" width="120" class-name="small-padding fixed-width">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="handleUpdate(row)">编辑</el-button>
+            <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getList" />
+    </el-card>
+
+    <!-- 添加或修改对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="550px" append-to-body>
+      <el-form ref="addFormRef" :model="form" label-width="120px" label-suffix=":">
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="事件时间" prop="createTime"
+              :rules="[{ required: true, message: '请选择', trigger: 'change' }]">
+              <el-date-picker v-model="form.createTime" style="width: 100%;" value-format="YYYY-MM-DD HH:mm:ss"
+                type="date" placeholder="请选择" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="发生地点" prop="addr" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
+              <el-input v-model="form.addr" placeholder="请输入" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="事件详情" prop="content" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
+              <el-input v-model="form.content" type="textarea" placeholder="请输入" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="事件等级" prop="level" :rules="[{ required: true, message: '请选择', trigger: 'change' }]">
+              <el-select v-model="form.level" clearable placeholder="请选择">
+                <el-option v-for="dict in event_level" :key="dict.value" :label="dict.label"
+                  :value="dict.value"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="建议处置部门" prop="processingDept"
+              :rules="[{ required: true, message: '请选择', trigger: 'change' }]">
+              <el-tree-select v-model="form.processingDept" :data="deptOptions"
+                :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择"
+                clearable check-strictly />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="建议措施" prop="recomEvent">
+              <el-input v-model="form.recomEvent" type="textarea" placeholder="请输入" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="应急预案" prop="emergencyPlan">
+              <el-input v-model="form.emergencyPlan" type="textarea" placeholder="请输入" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" color="#0F79EE" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Event" lang="ts">
+import { getToken } from '@/utils/auth';
+import { useUserStore } from '@/store/modules/user';
+import { LoginData } from '@/api/types';
+import api from '@/api/system/user';
+import { listEvent, getEvent, addEvent, updateEvent, delEvent } from '@/api/event'
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const userStore = useUserStore();
+const tableList = ref([]);
+const loading = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const addFormRef = ref<ElFormInstance>();
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const shortcuts = [
+  {
+    text: '最近一周',
+    value: () => {
+      const end = new Date()
+      const start = new Date()
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+      return [start, end]
+    },
+  },
+  {
+    text: '最近一个月',
+    value: () => {
+      const end = new Date()
+      const start = new Date()
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+      return [start, end]
+    },
+  },
+  {
+    text: '最近三个月',
+    value: () => {
+      const end = new Date()
+      const start = new Date()
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
+      return [start, end]
+    },
+  },
+]
+const initFormData = {
+  id: undefined,
+  createTime: undefined,
+  addr: undefined,
+  content: undefined,
+  level: undefined,
+  processingDept: undefined,
+  recomEvent: undefined,
+  emergencyPlan: undefined,
+  ext1: '{}',
+  ext2: '{}',
+};
+const data = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    createTime: '',
+    level: ''
+  },
+});
+const deptOptions = ref([]);
+const { event_level } = toRefs<any>(proxy?.useDict('event_level'));
+const { queryParams, form } = toRefs(data);
+const loginForm = ref<LoginData>({
+  tenantId: '000000',
+  username: 'admin',
+  password: 'admin123',
+  rememberMe: false,
+  code: '',
+  uuid: ''
+} as LoginData);
+/** 查询部门下拉树结构 */
+const getTreeSelect = async () => {
+  const res = await api.deptTreeSelect();
+  deptOptions.value = res.data;
+};
+const findLabelById = (treeData, targetId) => {
+  // 遍历当前层级的所有节点
+  for (const node of treeData) {
+    // 直接匹配当前节点 id
+    if (node.id == targetId) {
+      return node.label;
+    }
+    // 递归查找子节点(如果存在子节点)
+    if (node.children && Array.isArray(node.children) && node.children.length > 0) {
+      const foundLabel = findLabelById(node.children, targetId);
+      // 如果子节点中找到则直接返回结果
+      if (foundLabel !== undefined) {
+        return foundLabel;
+      }
+    }
+  }
+  return '';
+}
+function formatDateToChinese(date) {
+  // 将输入转换为 Date 对象(兼容字符串或 Date 类型)
+  const dateObj = typeof date === 'string' ? new Date(date) : date;
+  // 校验日期有效性(避免无效日期如 '2025-02-30')
+  if (isNaN(dateObj.getTime())) {
+    throw new Error('无效的日期输入');
+  }
+  // 补零函数(确保月份/日期为两位,如 6 → '06')
+  const padZero = (num) => num.toString().padStart(2, '0');
+  // 提取年、月、日(注意:月份从 0 开始,需 +1)
+  const year = dateObj.getFullYear();
+  const month = padZero(dateObj.getMonth() + 1); // 月份补零
+  const day = padZero(dateObj.getDate());         // 日期补零
+  // 组合为目标格式
+  return `${year}年${month}月${day}日`;
+}
+/** 查询参数列表 */
+const getList = async () => {
+  loading.value = true;
+  const { createTime, level, ...rest } = queryParams.value
+  const queryData = {
+    level,
+    ...rest,
+    params: {} as any,
+  }
+  if (createTime && createTime.length > 0) {
+    queryData.params.startTime = createTime[0]
+    queryData.params.endTime = createTime[1]
+
+  }
+  const res = await listEvent(queryData);
+  tableList.value = res.rows.map(item => ({
+    ...item,
+    createTimeFormat: formatDateToChinese(item.createTime),
+    processingDeptName: findLabelById(deptOptions.value, item.processingDept),
+  }));
+  total.value = res.total;
+  tableList.value.forEach(async item => {
+    if (item.remark) {
+      // const { code, data } = await listByIds(item.remark)
+      // if (code === 200) {
+      //   item.imgUrls = data.map(item => ({
+      //     ...item,
+      //     realUrl: getImageUrl(item.fileName)
+      //   }))
+      // }
+    }
+  })
+  loading.value = false;
+};
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+/** 表单重置 */
+const reset = () => {
+  form.value = JSON.parse(JSON.stringify(initFormData));
+  addFormRef.value?.resetFields();
+};
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '事件新增';
+}
+/** 修改按钮操作 */
+const handleUpdate = async (row) => {
+  reset();
+  Object.assign(form.value, row);
+  dialog.visible = true;
+  dialog.title = '事件编辑';
+};
+/** 提交按钮 */
+const submitForm = () => {
+  addFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      if (form.value.id) {
+        await updateEvent(form.value);
+      } else {
+        await addEvent(form.value)
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+/** 删除按钮操作 */
+const handleDelete = async (row) => {
+  await proxy?.$modal.confirm('是否确认删除?');
+  await delEvent(row.id)
+  await getList();
+  proxy?.$modal.msgSuccess('删除成功');
+};
+onMounted(async () => {
+  await userStore.login(loginForm.value)
+  await getTreeSelect(); // 初始化部门数据
+  getList();
+});
+</script>
+<style lang="scss" scoped></style>

+ 1 - 0
vite.config.ts

@@ -26,6 +26,7 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
       proxy: {
         [env.VITE_APP_BASE_API]: {
           target: 'http://jdyw.xt.wenhq.top:8083/',
+          // target: 'http://localhost:8080/',
           changeOrigin: true,
           ws: true,
           rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')