Ver código fonte

开启处置管理页面

chauncey 11 meses atrás
pai
commit
541d063bd8

+ 1 - 1
.env.development

@@ -3,7 +3,7 @@ VITE_PORT = 8092
 
 
 # 是否开启mock
-VITE_USE_MOCK = false
+VITE_USE_MOCK = true
 
 # 是否删除console
 VITE_DROP_CONSOLE = true

+ 117 - 49
mock/disaster-control/table.ts

@@ -1,75 +1,143 @@
 import { resultSuccess } from '../_util';
 
-const disasterList = [
+const disposalManagementList = {
+  records: [
+    {
+      id: 1,
+      taskName: '台风“贝碧嘉”灾害损失上报1',
+    },
+    {
+      id: 2,
+      taskName: '台风“贝碧嘉”灾害损失上报2',
+    },
+    {
+      id: 3,
+      taskName: '台风“贝碧嘉”灾害损失上报3',
+    },
+    {
+      id: 4,
+      taskName: '台风“贝碧嘉”灾害损失上报4',
+    },
+    {
+      id: 5,
+      taskName: '台风“贝碧嘉”灾害损失上报5',
+    },
+    {
+      id: 6,
+      taskName: '台风“贝碧嘉”灾害损失上报6',
+    },
+  ],
+  totalRow: 6,
+};
+
+const disposalManagementItemList = [
   {
-    id: 1001,
-    disasterType: '台风',
-    disasterLevel: 'IV级/一般',
-    startTime: '2024-03-20 10:00:00',
-    endTime: '2024-03-20 10:00:00',
-    disasterMaterial: [
+    handleTaskId: 1,
+    id: 1,
+    deptName: '广东省应急管理厅',
+    dueCompleteTime: '2025-05-23 10:00',
+    status: 1,
+    taskStage: 1,
+    reportPrincipals: [
+      {
+        id: 1,
+        realname: '张三',
+        username: '321540',
+      },
       {
-        id: 1001,
-        name: '台风灾害处置材料',
-        type: 'word',
-        link: '',
+        id: 2,
+        realname: '李四',
+        username: '321541',
       },
     ],
-    shouldCompleteTime: '2024-03-20 10:00:00',
-    activeStatus: 1,
-    taskStage: 0,
   },
   {
-    id: 1002,
-    disasterType: '台风',
-    disasterLevel: 'IV级/一般',
-    startTime: '2024-03-20 10:00:00',
-    endTime: '2024-03-20 10:00:00',
-    disasterMaterial: [
+    handleTaskId: 1,
+    id: 2,
+    deptName: '湖北省应急管理厅',
+    dueCompleteTime: '2025-05-23 10:00',
+    status: 2,
+    taskStage: 1,
+    reportPrincipals: [
       {
-        id: 1002,
-        name: '台风灾害处置材料',
-        type: 'ppt',
-        link: '',
-      },
+        id: 1,
+        realname: 'XXX',
+        username: '321540',
+      }
+    ],
+  },
+  {
+    handleTaskId: 1,
+    id: 3,
+    deptName: '海南省应急管理厅',
+    dueCompleteTime: '2025-05-23 10:00',
+    status: 1,
+    taskStage: 2,
+    reportPrincipals: [
+    ],
+  },
+  {
+    handleTaskId: 1,
+    id: 4,
+    deptName: '湖南省应急管理厅',
+    dueCompleteTime: '2025-05-23 10:00',
+    status: 2,
+    taskStage: 3,
+    reportPrincipals: [
       {
-        id: 1003,
-        name: '台讯前期安全检查方案',
-        type: 'excel',
-        link: '',
+        id: 2,
+        realname: '李四',
+        username: '321541',
       },
     ],
-    shouldCompleteTime: '2024-03-20 10:00:00',
-    activeStatus: 0,
-    taskStage: 0,
   },
   {
-    id: 1003,
-    disasterType: '台风',
-    disasterLevel: 'IV级/一般',
-    startTime: '2024-03-20 10:00:00',
-    endTime: '2024-03-20 10:00:00',
-    disasterMaterial: [
+    handleTaskId: 1,
+    id: 5,
+    deptName: '黑龙江省应急管理厅',
+    dueCompleteTime: '2025-05-23 10:00',
+    status: 1,
+    taskStage: 3,
+    reportPrincipals: [
       {
-        id: 1004,
-        name: '台风灾害处置材料',
-        type: 'pdf',
-        link: '',
+        id: 1,
+        realname: '张三',
+        username: '321540',
+      },
+      {
+        id: 2,
+        realname: '李四',
+        username: '321541',
       },
     ],
-    shouldCompleteTime: '2024-03-20 10:00:00',
-    activeStatus: 2,
-    taskStage: 1,
+  },
+  {
+    handleTaskId: 1,
+    id: 6,
+    deptName: '北京市应急管理厅',
+    dueCompleteTime: '2025-05-23 10:00',
+    status: 2,
+    taskStage: 2,
+    reportPrincipals: [
+    ],
   },
 ];
 
 export default [
   {
-    url: '/safety_api/api/admin/disaster/getDisasterList',
-    timeout: 1000,
-    method: 'get',
+    url: '/safety_mock_api/disasterHandle/queryDisasterHandleTaskPage',
+    timeout: 500,
+    method: 'post',
+    response: () => {
+      return resultSuccess(disposalManagementList);
+    },
+  },
+  {
+    url: '/safety_mock_api/disasterHandle/queryDisasterReportTask',
+    timeout: 500,
+    method: 'post',
     response: () => {
-      return resultSuccess(disasterList);
+      return resultSuccess(disposalManagementItemList);
     },
   },
 ];

+ 45 - 45
mock/login/info.ts

@@ -635,49 +635,49 @@ const info = {
   tenantName: '北京租户',
 };
 
-export default [
-  {
-    url: '/safety_api/api/login/info',
-    timeout: 1000,
-    method: 'post',
-    response: () => {
-      return resultSuccess(info);
-    },
-  },
+// export default [
+//   {
+//     url: '/safety_api/api/login/info',
+//     timeout: 1000,
+//     method: 'post',
+//     response: () => {
+//       return resultSuccess(info);
+//     },
+//   },
 
-  {
-    url: '/api/common/queryTentantList',
-    timeout: 1000,
-    method: 'get',
-    response: () => {
-      return resultSuccess([
-        {
-          tenantId: 1,
-          tenantCode: 'comac',
-          tenantName: '上飞公司',
-        },
-      ]);
-    },
-  },
-  {
-    url: '/api/common/initData',
-    timeout: 1000,
-    method: 'get',
-    response: () => {
-      return resultSuccess({
-        isEnableCode: false,
-      });
-    },
-  },
-  {
-    url: '/api/login/auth',
-    timeout: 1000,
-    method: 'post',
-    response: () => {
-      return resultSuccess({
-        satoken: 'ddb094c3-731e-4d11-8ca9-a4b0ba751a8d',
-        tenantId: '4510784fbc3c4ca59238666e5c75c2f8498702880',
-      });
-    },
-  },
-];
+//   {
+//     url: '/api/common/queryTentantList',
+//     timeout: 1000,
+//     method: 'get',
+//     response: () => {
+//       return resultSuccess([
+//         {
+//           tenantId: 1,
+//           tenantCode: 'comac',
+//           tenantName: '上飞公司',
+//         },
+//       ]);
+//     },
+//   },
+//   {
+//     url: '/api/common/initData',
+//     timeout: 1000,
+//     method: 'get',
+//     response: () => {
+//       return resultSuccess({
+//         isEnableCode: false,
+//       });
+//     },
+//   },
+//   {
+//     url: '/api/login/auth',
+//     timeout: 1000,
+//     method: 'post',
+//     response: () => {
+//       return resultSuccess({
+//         satoken: 'ddb094c3-731e-4d11-8ca9-a4b0ba751a8d',
+//         tenantId: '4510784fbc3c4ca59238666e5c75c2f8498702880',
+//       });
+//     },
+//   },
+// ];

+ 10 - 10
mock/login/routers.ts

@@ -312,13 +312,13 @@ const list = [
   },
 ];
 
-export default [
-  {
-    url: '/safety_api/api/admin/menu/getRouters',
-    timeout: 1000,
-    method: 'get',
-    response: () => {
-      return resultSuccess(list);
-    },
-  },
-];
+// export default [
+//   {
+//     url: '/safety_api/api/admin/menu/getRouters',
+//     timeout: 1000,
+//     method: 'get',
+//     response: () => {
+//       return resultSuccess(list);
+//     },
+//   },
+// ];

+ 2 - 2
src/api/auth/dept.ts

@@ -1,5 +1,5 @@
 import { http } from '@/utils/http/axios';
-import { DeptTreeItem , addDeptProps, editDeptProps} from '@/types/dept/type';
+import { DeptTree , addDeptProps, editDeptProps} from '@/types/dept/type';
 
 /**
  * @description: 部门列表
@@ -91,7 +91,7 @@ export function updateDepartments(params:editDeptProps) {
 }
 
 // v4: 获取所有组织
-export function getAllDepartments(): Promise<DeptTreeItem[]> {
+export function getAllDepartments(): Promise<DeptTree> {
   return http.request({
     url: '/admin/dept/queryAllDeptTree',
     method: 'post',

+ 1 - 1
src/api/deptInfo/index.ts

@@ -4,6 +4,6 @@ import { http } from '@/utils/http/axios';
 export function getDeptInfoList() {
   return http.request({
     url: '/admin/dept/queryDeptPullDownList',
-    method: 'get',
+    method: 'get',baseURL: '/safety_api_mock'
   });
 }

+ 23 - 13
src/api/disaster-control/index.ts

@@ -1,16 +1,26 @@
-import { http } from '@/utils/http/axios';
-import type { DisasterControlTableDataResponse } from '@/types/disaster-control';
+import { http } from '@/utils/http/axios/mock';
+import type {
+  DisposalManagementListResponse,
+  DisposalManagementListQuery,
+  DisposalManagementListItemQuery,
+  DisposalManagementTableResponse,
+} from '@/types/disaster-control';
+import type { QueryPageResponse, QueryPageRequest } from '@/types/disaster';
 /**
  * 获取台风灾害管理表格数据
  */
-export function getDisasterControlTableData() {
-  return http.request<DisasterControlTableDataResponse[]>(
-    {
-      url: 'admin/disaster/getDisasterList',
-      method: 'get',
-    },
-    {
-      ignoreTargetTenantId: true,
-    },
-  );
-}
+export const getDisasterControlCollapseData = (query: QueryPageRequest<DisposalManagementListQuery>) => {
+  return http.request<QueryPageResponse<DisposalManagementListResponse>>({
+    url: '/disasterHandle/queryDisasterHandleTaskPage',
+    method: 'post',
+    data: query,
+  });
+};
+
+export const getDisasterControlTableData = (query: DisposalManagementListItemQuery) => {
+  return http.request<DisposalManagementTableResponse[]>({
+    url: '/disasterHandle/queryDisasterReportTask',
+    method: 'post',
+    data: query,
+  });
+};

+ 2 - 2
src/hooks/useTableConfigHook.ts

@@ -12,7 +12,7 @@ import { reactive } from 'vue';
  * @description 用于BasicTable组件的配置
  * @author Chauncey
  */
-const useTableConfig = (columns: TableColumnProps[], options: any = {}) => {
+const useTableConfig = (columns: TableColumnProps[], options: any = {}, isPagination = true) => {
   // 分页配置
   const pagination = reactive<PaginationProps>({
     total: 0,
@@ -23,7 +23,7 @@ const useTableConfig = (columns: TableColumnProps[], options: any = {}) => {
   // 表格配置
   const tableConfig = reactive<BasicTableProps>({
     columns,
-    pagination,
+    pagination: isPagination ? pagination : undefined,
     ...options,
   });
 

+ 23 - 1
src/router/full-routes.ts

@@ -283,7 +283,29 @@ export const disasterPreventionRoute = {
       redirect: '',
     },
     {
-      component: '/disaster/disaster-control/PageDisasterControl',
+      children: [
+        {
+          component: '/disaster/disaster-control/PageDisposalManagement',
+          id: 1038,
+          meta: {
+            activeMenu: '',
+            alwaysShow: false,
+            frameSrc: '',
+            hidden: false,
+            icon: '',
+            isFrame: 0,
+            isRoot: false,
+            noCache: false,
+            query: '',
+            title: '处置管理',
+          },
+          name: 'disaster-control-disposal-management',
+          parentId: 1027,
+          path: 'disposal-management',
+          redirect: '',
+        }
+      ],
+      component: '',
       id: 1032,
       meta: {
         activeMenu: null,

+ 21 - 13
src/types/disaster-control/index.ts

@@ -1,17 +1,25 @@
-export interface DisasterMaterialList {
+import type { UserInfo } from '@/types/push-object';
+export interface DisposalManagementListQuery {
+  reporterId?: number;
+}
+
+export interface DisposalManagementListItemQuery extends DisposalManagementListQuery {
+  handleTaskIds: number[];
+  reportDeptIds?: number[];
+  status?: string;
+  taskStage?: string;
+}
+export interface DisposalManagementListResponse {
   id: number;
-  name: string;
-  type: 'word' | 'excel' | 'ppt' | 'pdf';
-  link: string;
+  taskName: string;
+  tableData: DisposalManagementTableResponse[];
 }
-export interface DisasterControlTableDataResponse {
+
+export interface DisposalManagementTableResponse {
   id: number;
-  disasterType: string;
-  disasterLevel: string;
-  startTime: string;
-  endTime: string;
-  disasterMaterial: DisasterMaterialList[];
-  shouldCompleteTime: string;
-  activeStatus: number;
-  taskStage: number;
+  deptName: string;
+  dueCompleteTime: string;
+  status: string;
+  taskStage: string;
+  reportPrincipals: UserInfo[];
 }

+ 160 - 0
src/utils/http/axios/mock.ts

@@ -0,0 +1,160 @@
+// axios配置 - 简化版
+import { VAxios } from './Axios';
+import { AxiosTransform } from './axiosTransform';
+import axios, { AxiosResponse } from 'axios';
+import { joinTimestamp, formatRequestDate } from './helper';
+import { RequestEnum } from '@/enums/httpEnum';
+import { deepMerge, isUrl } from '@/utils';
+import { setObjToUrlParams } from '@/utils/urlUtils';
+import { isString } from '@/utils/is/index';
+import { RequestOptions, Result, CreateAxiosOptions } from './types';
+import urlJoin from 'url-join';
+
+// 基础URL前缀
+const urlPrefix = 'safety_mock_api';
+
+/**
+ * @description: 数据处理,方便区分多种处理方式
+ */
+const transform: AxiosTransform = {
+  /**
+   * @description: 处理请求数据
+   */
+  transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
+    const {
+      isTransformResponse,
+      isReturnNativeResponse,
+    } = options;
+
+    // 是否返回原生响应头 比如:需要获取响应头时使用该属性
+    if (isReturnNativeResponse) {
+      return res;
+    }
+    // 不进行任何处理,直接返回
+    // 用于页面代码可能需要直接获取code,data,message这些信息时开启
+    if (!isTransformResponse) {
+      return res.data;
+    }
+
+    const { data } = res;
+
+    if (!data) {
+      throw new Error('请求出错,请稍候重试');
+    }
+    
+    const { code, data: result } = data;
+
+    // 请求成功,直接返回结果
+    return result;
+  },
+
+  // 请求之前处理config
+  beforeRequestHook: (config, options) => {
+    const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
+
+    const isUrlStr = isUrl(config.url as string);
+
+    if (!isUrlStr && joinPrefix) {
+      config.url = urlJoin(urlPrefix || '', config.url as string);
+    }
+
+    if (!isUrlStr && apiUrl && isString(apiUrl)) {
+      config.url = `${apiUrl}${config.url}`;
+    }
+    const params = config.params || {};
+    const data = config.data || false;
+    const method = config.method || 'GET';
+    if (method.toUpperCase() === RequestEnum.GET) {
+      if (!isString(params)) {
+        // 给 get 请求加上时间戳参数,避免从缓存中拿数据。
+        config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
+      } else {
+        // 兼容restful风格
+        config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
+        config.params = undefined;
+      }
+    } else {
+      if (!isString(params)) {
+        formatDate && formatRequestDate(params);
+        if (
+          Reflect.has(config, 'data') &&
+          config.data &&
+          (Object.keys(config.data).length > 0 || config.data instanceof FormData)
+        ) {
+          config.data = data;
+          config.params = params;
+        } else {
+          config.data = params;
+          config.params = undefined;
+        }
+        if (joinParamsToUrl) {
+          config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data));
+        }
+      } else {
+        // 兼容restful风格
+        config.url = config.url + params;
+        config.params = undefined;
+      }
+    }
+    return config;
+  },
+
+  /**
+   * @description: 请求拦截器处理
+   */
+  requestInterceptors: (config) => {
+    // 简化版,不处理token和租户ID
+    return config;
+  },
+
+  /**
+   * @description: 响应错误处理
+   */
+  responseInterceptorsCatch: (error: any) => {
+    const { response } = error || {};
+    return Promise.reject(response?.data || error);
+  },
+};
+
+function createAxios(opt?: Partial<CreateAxiosOptions>) {
+  return new VAxios(
+    deepMerge(
+      {
+        timeout: 20 * 1000,
+        // 接口前缀
+        prefixUrl: urlPrefix,
+        headers: { 'Content-Type': 'application/json' },
+        // 数据处理方式
+        transform,
+        // 配置项,下面的选项都可以在独立的接口请求中覆盖
+        requestOptions: {
+          // 默认将prefix 添加到url
+          joinPrefix: true,
+          // 是否返回原生响应头 比如:需要获取响应头时使用该属性
+          isReturnNativeResponse: false,
+          // 需要对返回数据进行处理
+          isTransformResponse: true,
+          // post请求的时候添加参数到url
+          joinParamsToUrl: false,
+          // 格式化提交参数时间
+          formatDate: true,
+          // 接口拼接地址
+          urlPrefix: urlPrefix,
+          //  是否加入时间戳
+          joinTime: true,
+          // 忽略重复请求
+          ignoreCancelToken: true,
+        },
+        withCredentials: false,
+      },
+      opt || {},
+    ),
+  );
+}
+
+export const http = createAxios();
+
+// 简化版,不需要生成headers
+export const getHeaders = () => {
+  return {};
+};

+ 13 - 8
src/views/disaster/components/Search.vue

@@ -3,14 +3,19 @@
     <section class="select-box">
       <div class="select-box--item" v-for="item in searchConfig" :key="item.prop">
         <span>{{ item.label }}</span>
-        <component :is="item.component" v-model="searchData[item.prop]" v-bind="item.componentProps">
-          <el-option
-            v-for="option in item.selectOptions"
-            :key="option.value"
-            :label="option.label"
-            :value="option.value"
-          />
-        </component>
+        <template v-if="item.slot">
+          <slot :name="item.slot" />
+        </template>
+        <template v-else>
+          <component :is="item.component" v-model="searchData[item.prop]" v-bind="item.componentProps">
+            <el-option
+              v-for="option in item.selectOptions"
+              :key="option.value"
+              :label="option.label"
+              :value="option.value"
+            />
+          </component>
+        </template>
       </div>
     </section>
     <section class="search-btn">

+ 0 - 194
src/views/disaster/disaster-control/PageDisasterControl.vue

@@ -1,194 +0,0 @@
-<template>
-  <div class="disaster-precaution-container">
-    <header class="disaster-precaution-container__header">
-      <span class="disaster-precaution-container__title">灾后处置清单</span>
-    </header>
-    <main class="disaster-precaution-container__main">
-      <div class="disaster-control-container">
-        <header class="disaster-control__header">
-          <el-button type="primary" class="disaster-control__header-button" :icon="Plus">创建灾害处置单</el-button>
-          <Search />
-        </header>
-        <BasicTable
-          :table-config="tableConfig"
-          :table-data="tableData"
-          @update:pageSize="handleSizeChange"
-          @update:pageNumber="handleCurrentChange"
-        >
-          <template #disasterMaterial="scope">
-            <div class="disaster-material--div">
-              <div class="disaster-material--div__item" v-for="item in scope.row.disasterMaterial" :key="item.id">
-                <img :src="FILE_TYPE_ICON[item.type as keyof typeof FILE_TYPE_ICON]" alt="" />
-                <el-tooltip :content="item.name" placement="bottom" effect="light">
-                  <span>{{ item.name }}</span>
-                </el-tooltip>
-                <img :src="DownLoadIcon" alt="" class="download-icon" />
-              </div>
-            </div>
-          </template>
-          <template #activeStatus="scope">
-            <div class="active-status--div">
-              <div
-                class="dot"
-                :style="{ backgroundColor: ACTIVE_STATUS_COLOR[scope.row.activeStatus as ACTIVE_STATUS] }"
-              ></div>
-              <span>{{ ACTIVE_STATUS_MAP[scope.row.activeStatus] }}</span>
-            </div>
-          </template>
-          <template #action="scope">
-            <ActionButton text="编辑" v-if="scope.row.activeStatus === ACTIVE_STATUS.NOT_EFFECTIVE" />
-            <ActionButton text="查看" />
-            <ActionButton
-              text="发布"
-              :popconfirm="{
-                title: '确定要发布?',
-              }"
-              v-if="scope.row.activeStatus === ACTIVE_STATUS.NOT_EFFECTIVE"
-            />
-            <ActionButton
-              text="取消发布"
-              :popconfirm="{
-                title: '确定要取消发布?',
-              }"
-              v-else-if="scope.row.activeStatus === ACTIVE_STATUS.ACTIVE"
-            />
-            <ActionButton
-              text="删除"
-              :popconfirm="{
-                title: '确定要删除?',
-              }"
-            />
-          </template>
-        </BasicTable>
-      </div>
-    </main>
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ref, onMounted } from 'vue';
-  import { Plus } from '@element-plus/icons-vue';
-  import DownLoadIcon from '@/assets/svg/download.svg';
-  import Search from './src/components/Search.vue';
-  import BasicTable from '@/components/BasicTable.vue';
-  import ActionButton from '@/components/ActionButton.vue';
-  import type { TableColumnProps } from '@/types/basic-table';
-  import useTableConfig from '@/hooks/useTableConfigHook';
-  import { getDisasterControlTableData } from '@/api/disaster-control';
-  import type { DisasterControlTableDataResponse } from '@/types/disaster-control';
-  import { ACTIVE_STATUS, ACTIVE_STATUS_COLOR, ACTIVE_STATUS_MAP, FILE_TYPE_ICON } from '@/views/disaster/constant';
-  const tableData = ref<DisasterControlTableDataResponse[]>([]);
-  const columns: TableColumnProps[] = [
-    {
-      prop: 'disasterType',
-      label: '灾害类型',
-      align: 'center',
-    },
-    {
-      prop: 'disasterLevel',
-      label: '灾害等级',
-      align: 'center',
-    },
-    {
-      prop: 'startTime',
-      label: '开始时间',
-      align: 'center',
-      width: '200cpx',
-    },
-    {
-      prop: 'endTime',
-      label: '结束时间',
-      align: 'center',
-      width: '200cpx',
-    },
-    {
-      prop: 'disasterMaterial',
-      label: '处置材料',
-      align: 'center',
-      slot: 'disasterMaterial',
-      width: '200cpx',
-    },
-    {
-      prop: 'shouldCompleteTime',
-      label: '应完成时间',
-      align: 'center',
-      width: '200cpx',
-    },
-    {
-      prop: 'activeStatus',
-      label: '生效状态',
-      align: 'center',
-      slot: 'activeStatus',
-      width: '120cpx',
-    },
-    {
-      prop: 'action',
-      label: '操作',
-      align: 'center',
-      slot: 'action',
-      fixed: 'right',
-      width: '250cpx',
-    },
-  ];
-  const options = {
-    emptyText: '暂无数据',
-    height: '60vh',
-    loading: true,
-    stripe: true,
-  };
-  const { tableConfig, pagination } = useTableConfig(columns, options);
-  const handleSizeChange = (value: number) => {
-    pagination.pageSize = value;
-    tableConfig.loading = true;
-    getTableData();
-  };
-  const handleCurrentChange = (value: number) => {
-    pagination.pageNumber = value;
-    tableConfig.loading = true;
-    getTableData();
-  };
-  const getTableData = async () => {
-    const res = await getDisasterControlTableData();
-    tableData.value = res;
-    pagination.total = tableData.value.length;
-    tableConfig.loading = false;
-  };
-  onMounted(() => {
-    getTableData();
-  });
-</script>
-
-<style lang="scss" scoped>
-  @use '../style/disaster.scss' as *;
-  .disaster-control__header-button {
-    font-size: 14cpx;
-    margin-bottom: 20cpx;
-  }
-  .disaster-control-container {
-    display: flex;
-    flex-direction: column;
-    gap: 20cpx;
-  }
-  .disaster-material--div {
-    display: flex;
-    flex-direction: column;
-    gap: 10cpx;
-    img {
-      width: 15cpx;
-      height: 15cpx;
-    }
-    &__item {
-      @include flex-center;
-      gap: 5cpx;
-      span {
-        width: 150cpx;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-    }
-  }
-  .download-icon {
-    cursor: pointer;
-  }
-</style>

+ 215 - 0
src/views/disaster/disaster-control/PageDisposalManagement.vue

@@ -0,0 +1,215 @@
+<template>
+  <div class="disaster-precaution-container">
+    <header class="disaster-precaution-container__header">
+      <span class="disaster-precaution-container__title">灾害处置管理</span>
+    </header>
+    <main class="disaster-precaution-container__main">
+      <div class="disaster-precaution">
+        <header class="disaster-precaution__header">
+          <el-button type="primary" class="disaster-precaution__header--button" :icon="Plus">
+            创建灾害损失记录
+          </el-button>
+          <Search
+            :searchConfig="DISPOSAL_MANAGEMENT_SEARCH_CONFIG"
+            :searchData="searchData"
+            @update:searchData="handleSearch"
+          >
+            <template #reportDeptIds>
+              <el-select v-model="searchData.reportDeptIds" multiple placeholder="请选择上报单位" class="custom-select">
+                <el-option v-for="item in firstLevelDepts" :key="item.id" :label="item.deptName" :value="item.id" />
+              </el-select>
+            </template>
+          </Search>
+        </header>
+        <div class="collapse-container" v-loading="collapseLoading">
+          <div class="empty-container" v-if="collapseList.length === 0">
+            <img :src="Empty" />
+            <span>暂无数据</span>
+          </div>
+          <template v-else>
+            <CollapseItem
+              v-for="item in collapseList"
+              :key="item.id"
+              :name="item.taskName"
+              :defaultOpen="item.id === 1"
+            >
+              <template #main-table>
+                <BasicTable :tableData="item.tableData" :tableConfig="tableConfig">
+                  <template #status="scope">
+                    <div class="active-status--div">
+                      <div
+                        class="dot"
+                        :style="{ backgroundColor: ACTIVE_STATUS_COLOR[scope.row.status as ACTIVE_STATUS] }"
+                      />
+                      <span>{{ ACTIVE_STATUS_MAP[scope.row.status] }}</span>
+                    </div>
+                  </template>
+                  <template #taskStage="scope">
+                    <span>{{ formatTaskStage(scope.row.taskStage) }}</span>
+                  </template>
+                  <template #reportPrincipals="scope">
+                    <p v-for="user in scope.row.reportPrincipals" :key="user.id">
+                      {{ user.realname }}({{ user.username }})
+                    </p>
+                  </template>
+                  <template #action="scope">
+                    <ActionButton text="编辑" v-if="scope.row.status === ACTIVE_STATUS.NOT_EFFECTIVE" />
+                    <ActionButton
+                      text="发布"
+                      :popconfirm="{
+                        title: '确定发布吗?',
+                      }"
+                      v-if="scope.row.status === ACTIVE_STATUS.NOT_EFFECTIVE"
+                    />
+                    <ActionButton
+                      text="撤回"
+                      :popconfirm="{
+                        title: '确定撤回吗?',
+                      }"
+                      v-if="scope.row.taskStage === TASK_STAGE.TO_BE_REPORTED"
+                    />
+                    <ActionButton text="删除" v-if="scope.row.status === ACTIVE_STATUS.NOT_EFFECTIVE" />
+                    <ActionButton text="查看" v-if="scope.row.status === ACTIVE_STATUS.ACTIVE" />
+                  </template>
+                </BasicTable>
+              </template>
+            </CollapseItem>
+          </template>
+        </div>
+        <div class="pagination-container" v-if="collapseList.length > 0">
+          <el-pagination
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :page-sizes="DISASTER_CONTROL_PAGE_SIZE_CONFIG"
+            layout="prev, pager, next, jumper, sizes, total"
+            background
+            :total="total"
+            @size-change="handleSizeChange"
+            @current-change="handleCurrentChange"
+          />
+        </div>
+      </div>
+    </main>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { Plus } from '@element-plus/icons-vue';
+  import Search from '@/views/disaster/components/Search.vue';
+  import CollapseItem from './src/components/CollapseItem.vue';
+  import BasicTable from '@/components/BasicTable.vue';
+  import { DISPOSAL_MANAGEMENT_SEARCH_CONFIG } from './src/config';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import { onMounted, reactive, ref } from 'vue';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import type { DeptTree } from '@/types/dept/type';
+  import { TASK_STAGE_OPTIONS, TASK_STAGE } from './src/constant';
+  import { getDisasterControlCollapseData, getDisasterControlTableData } from '@/api/disaster-control';
+  import { DEFAULT_PAGE_SIZE, DISASTER_CONTROL_PAGE_SIZE_CONFIG } from './src/constant';
+  import type { DisposalManagementListResponse } from '@/types/disaster-control';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import { DISPOSAL_MANAGEMENT_TABLE_COLUMNS, TABLE_OPTIONS } from './src/config';
+  import { ACTIVE_STATUS, ACTIVE_STATUS_COLOR, ACTIVE_STATUS_MAP } from '@/views/disaster/constant';
+  import ActionButton from '@/components/ActionButton.vue';
+  import Empty from 'assets/images/empty@1X.png';
+
+  const firstLevelDepts = ref<DeptTree[]>([]);
+  const searchData = reactive({
+    reportDeptIds: [],
+    status: '',
+    taskStage: '',
+  });
+
+  const currentPage = ref(1);
+  const pageSize = ref(DEFAULT_PAGE_SIZE);
+  const total = ref(0);
+
+  const collapseLoading = ref(false);
+  const collapseList = ref<DisposalManagementListResponse[]>([]);
+
+  const { tableConfig } = useTableConfig(DISPOSAL_MANAGEMENT_TABLE_COLUMNS, TABLE_OPTIONS, false);
+
+  const formatTaskStage = (taskStage: number) => {
+    return TASK_STAGE_OPTIONS.find((item) => item.value === taskStage)?.label;
+  };
+
+  const handleSizeChange = (size: number) => {
+    pageSize.value = size;
+  };
+
+  const handleCurrentChange = (page: number) => {
+    currentPage.value = page;
+  };
+
+  const handleSearch = () => {
+    console.log(searchData);
+  };
+
+  const taskIds = ref<number[]>([]);
+  const getDisposalData = async () => {
+    collapseLoading.value = true;
+    const res = await getDisasterControlCollapseData({
+      pageNumber: currentPage.value,
+      pageSize: pageSize.value,
+      queryParam: {},
+    });
+    collapseList.value = res.records;
+    collapseList.value.forEach((item) => {
+      item.tableData = [];
+    });
+    taskIds.value = res.records.map((item) => item.id);
+    total.value = res.totalRow;
+    collapseLoading.value = false;
+  };
+
+  const getDisposalTableData = async () => {
+    tableConfig.loading = true;
+    const res = await getDisasterControlTableData({
+      handleTaskIds: taskIds.value,
+      ...searchData,
+    });
+    collapseList.value.forEach((item) => {
+      item.tableData = res;
+    });
+    tableConfig.loading = false;
+  };
+
+  onMounted(async () => {
+    const result = await getAllDepartments();
+    await getDisposalData();
+    await getDisposalTableData();
+    firstLevelDepts.value = formatDeptTree(result);
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '../style/disaster.scss' as *;
+  .custom-select {
+    width: 200cpx;
+    :deep(.el-select__selection) {
+      min-height: 24px;
+      max-height: 24px;
+      overflow-y: auto;
+    }
+  }
+  .collapse-container {
+    display: flex;
+    flex-direction: column;
+    gap: 10cpx;
+    width: 100%;
+    height: calc(63vh - 15cpx);
+    max-height: calc(63vh - 15cpx);
+    overflow-y: auto;
+  }
+  .empty-container {
+    width: 100%;
+    height: 100%;
+    @include flex-center;
+    flex-direction: column;
+    gap: 10cpx;
+  }
+  .pagination-container {
+    display: flex;
+    justify-content: flex-end;
+  }
+</style>

+ 72 - 0
src/views/disaster/disaster-control/src/components/CollapseItem.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="collapse-item">
+    <div class="collapse-item__header">
+      <div class="collapse-item__header--left" @click="toggle">
+        <el-icon class="collapse-item__icon">
+          <component :is="isOpen ? ArrowUp : ArrowDown" />
+        </el-icon>
+        <span>{{ name }}</span>
+        <img :src="ViewDocument" alt="查看" class="collapse-item__icon" @click.stop="handleViewDocument" />
+      </div>
+      <div class="collapse-item__header--right">
+        <el-button type="primary">发布</el-button>
+        <el-button type="primary">撤回</el-button>
+        <el-button type="primary">删除</el-button>
+      </div>
+    </div>
+    <div v-show="isOpen" class="collapse-item__main">
+      <slot name="main-table" />
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, onMounted } from 'vue';
+  import { ArrowUp, ArrowDown } from '@element-plus/icons-vue';
+  import ViewDocument from '../svg/view-document.svg';
+  const props = defineProps<{ name: string; defaultOpen?: boolean }>();
+  const isOpen = ref(false);
+  const toggle = () => {
+    isOpen.value = !isOpen.value;
+  };
+  const openFirstCollapseItem = () => {
+    if (!props.defaultOpen) return;
+    isOpen.value = true;
+  };
+  const handleViewDocument = () => {
+    console.log('查看');
+  };
+  onMounted(() => {
+    openFirstCollapseItem();
+  });
+</script>
+
+<style lang="scss" scoped>
+  .collapse-item {
+    width: 100%;
+    background: #f2f8ff;
+    padding: 10cpx;
+    border-radius: 4cpx;
+    margin-bottom: 8px;
+    &__header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 10cpx 14cpx;
+      font-size: 20cpx;
+      font-weight: 550;
+      color: rgba($text-color, 0.85);
+      &--left {
+        @include flex-center;
+        gap: 20cpx;
+        cursor: pointer;
+      }
+    }
+    &__main {
+      padding-top: 20cpx;
+    }
+    &__icon {
+      width: 20cpx;
+    }
+  }
+</style>

+ 0 - 59
src/views/disaster/disaster-control/src/components/Search.vue

@@ -1,59 +0,0 @@
-<template>
-  <div class="search-box">
-    <section class="select-box">
-      <div class="select-box--item">
-        <span>灾害类型:</span>
-        <el-select v-model="selectDisasterType" filterable placeholder="请选择灾害类型">
-          <el-option v-for="item in DISASTER_TYPE" :key="item.value" :label="item.label" :value="item.value" />
-        </el-select>
-      </div>
-      <div class="select-box--item">
-        <span>灾害等级:</span>
-        <el-select v-model="selectDisasterLevel" placeholder="请选择灾害等级">
-          <el-option v-for="item in DISASTER_LEVEL" :key="item.value" :label="item.label" :value="item.value" />
-        </el-select>
-      </div>
-      <div class="select-box--item">
-        <span>生效状态:</span>
-        <el-select v-model="selectStatus" placeholder="请选择状态">
-          <el-option
-            v-for="item in ACTIVE_STATUS_OPTIONS_MANAGEMENT"
-            :key="item.value"
-            :label="item.label"
-            :value="item.value"
-          />
-        </el-select>
-      </div>
-      <!-- <div class="select-box--item">
-        <span>创建时间:</span>
-        <el-date-picker
-          type="datetimerange"
-          range-separator="至"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          format="YYYY-MM-DD HH:mm:ss"
-          date-format="YYYY/MM/DD ddd"
-          time-format="A hh:mm:ss"
-          v-model="dateRange"
-        />
-      </div> -->
-    </section>
-    <section class="search-btn">
-      <el-button type="primary">查询</el-button>
-      <el-button>重置</el-button>
-    </section>
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ref } from 'vue';
-  import { ACTIVE_STATUS_OPTIONS_MANAGEMENT, DISASTER_TYPE, DISASTER_LEVEL } from '@/views/disaster/constant';
-  const selectDisasterType = ref('');
-  const selectDisasterLevel = ref('');
-  const selectStatus = ref('');
-  // const dateRange = ref<string[]>([]);
-</script>
-
-<style lang="scss" scoped>
-  @use '@/views/disaster/style/search.scss' as *;
-</style>

+ 4 - 0
src/views/disaster/disaster-control/src/config/index.ts

@@ -0,0 +1,4 @@
+import { DISPOSAL_MANAGEMENT_SEARCH_CONFIG } from './search';
+import { DISPOSAL_MANAGEMENT_TABLE_COLUMNS, TABLE_OPTIONS } from './table';
+
+export { DISPOSAL_MANAGEMENT_SEARCH_CONFIG, DISPOSAL_MANAGEMENT_TABLE_COLUMNS, TABLE_OPTIONS };

+ 32 - 0
src/views/disaster/disaster-control/src/config/search.ts

@@ -0,0 +1,32 @@
+/**
+ * 灾害处理搜索配置
+ */
+import type { SearchConfig } from '@/views/disaster/types';
+import { ACTIVE_STATUS_OPTIONS } from '@/views/disaster/constant';
+import { TASK_STAGE_OPTIONS } from '../constant';
+
+export const DISPOSAL_MANAGEMENT_SEARCH_CONFIG: SearchConfig[] = [
+  {
+    label: '上报单位',
+    prop: 'reportDeptIds',
+    slot: 'reportDeptIds',
+  },
+  {
+    label: '生效状态',
+    prop: 'status',
+    component: 'ElSelect',
+    selectOptions: ACTIVE_STATUS_OPTIONS,
+    componentProps: {
+      placeholder: '请选择生效状态',
+    },
+  },
+  {
+    label: '任务阶段',
+    prop: 'taskStage',
+    component: 'ElSelect',
+    selectOptions: TASK_STAGE_OPTIONS,
+    componentProps: {
+      placeholder: '请选择任务阶段',
+    },
+  },
+];

+ 66 - 0
src/views/disaster/disaster-control/src/config/table.ts

@@ -0,0 +1,66 @@
+/**
+ * 灾害处置表格配置
+ */
+import type { TableColumnProps } from '@/types/basic-table';
+// 基础表格样式配置
+export const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+  stripe: true,
+  height: '40vh',
+};
+
+// 基础表格列配置项
+const BASIC_TABLE_COLUMNS = {
+  ACTION: {
+    prop: 'action',
+    label: '操作',
+    align: 'center',
+    slot: 'action',
+    fixed: 'right',
+    width: '250cpx',
+  },
+};
+
+// 处置管理表格列配置
+export const DISPOSAL_MANAGEMENT_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    type: 'index',
+    label: '序号',
+    prop: 'index',
+    align: 'center',
+    width: '80cpx',
+  },
+  {
+    label: '上报部门',
+    prop: 'deptName',
+    align: 'center',
+  },
+  {
+    label: '应完成上报时间',
+    prop: 'dueCompleteTime',
+    align: 'center',
+    width: '200cpx',
+  },
+  {
+    label: '任务状态',
+    prop: 'status',
+    align: 'center',
+    slot: 'status',
+    width: '120cpx',
+  },
+  {
+    label: '任务阶段',
+    prop: 'taskStage',
+    align: 'center',
+    slot: 'taskStage',
+  },
+  {
+    label: '上报责任人',
+    prop: 'reportPrincipals',
+    align: 'center',
+    slot: 'reportPrincipals',
+    width: '200cpx',
+  },
+  BASIC_TABLE_COLUMNS.ACTION,
+];

+ 28 - 0
src/views/disaster/disaster-control/src/constant.ts

@@ -0,0 +1,28 @@
+/**
+ * 灾害处置的常量
+ */
+import { PAGE_SIZE_CONFIG } from '@/constant/pagination';
+// 任务阶段
+export enum TASK_STAGE {
+  TO_BE_RELEASED = 1,
+  TO_BE_REPORTED,
+  REPORTED,
+}
+
+export const TASK_STAGE_OPTIONS = [
+  {
+    label: '待发布',
+    value: TASK_STAGE.TO_BE_RELEASED,
+  },
+  {
+    label: '待上报',
+    value: TASK_STAGE.TO_BE_REPORTED,
+  },
+  {
+    label: '已上报',
+    value: TASK_STAGE.REPORTED,
+  },
+];
+
+export const DEFAULT_PAGE_SIZE = 10;
+export const DISASTER_CONTROL_PAGE_SIZE_CONFIG = [DEFAULT_PAGE_SIZE, ...PAGE_SIZE_CONFIG];

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
src/views/disaster/disaster-control/src/svg/view-document.svg


+ 1 - 0
src/views/disaster/style/disaster.scss

@@ -31,6 +31,7 @@
   &__main {
     flex: 1;
     padding: 20cpx;
+    overflow-y: auto;
     .disaster-precaution {
       display: flex;
       flex-direction: column;

+ 2 - 1
src/views/disaster/types/index.ts

@@ -16,8 +16,9 @@ export interface FileItem {
 export interface SearchConfig {
   label: string;
   prop: string;
-  component: string;
+  component?: string;
   selectOptions?: any[];
   componentProps?: any;
+  slot?: string;
   [key: string]: any; // 添加索引签名
 }

+ 9 - 0
src/views/disaster/utils/formatDeptTree.ts

@@ -0,0 +1,9 @@
+/**
+ * 格式化部门结构树 将组织部门一级部门提取出来
+ */
+import type { DeptTree } from '@/types/dept/type';
+
+export function formatDeptTree(deptTree: DeptTree) {
+  const firstLevelDepts = deptTree[0].children;
+  return firstLevelDepts;
+}