Răsfoiți Sursa

fix: add icon and Markmap

Jiang, Wim 3 ani în urmă
părinte
comite
ed99172b74
68 a modificat fișierele cu 2118 adăugiri și 213 ștergeri
  1. 131 0
      src/api/incident.ts
  2. 75 0
      src/api/resource.ts
  3. BIN
      src/assets/bg_jgxt.jpg
  4. BIN
      src/assets/card-bg/frame_czfa.png
  5. BIN
      src/assets/card-bg/frame_filter.png
  6. BIN
      src/assets/card-bg/frame_filter_bottom@2x.png
  7. BIN
      src/assets/card-bg/frame_filter_center@2x.png
  8. BIN
      src/assets/card-bg/frame_filter_top@2x.png
  9. BIN
      src/assets/card-bg/frame_information.png
  10. BIN
      src/assets/card-bg/frame_sjxx.png
  11. BIN
      src/assets/card-bg/frame_skjc.png
  12. BIN
      src/assets/card-bg/frame_yjld.png
  13. BIN
      src/assets/card-bg/frame_zhtx.png
  14. BIN
      src/assets/card-bg/frame_zxrz.png
  15. BIN
      src/assets/card-bg/img_dialogue.png
  16. BIN
      src/assets/icons/detail/co_organized@2x.png
  17. BIN
      src/assets/icons/detail/command@2x.png
  18. BIN
      src/assets/icons/detail/dbpt@2x.png
  19. BIN
      src/assets/icons/detail/host@2x.png
  20. BIN
      src/assets/icons/detail/level@2x.png
  21. BIN
      src/assets/icons/detail/rhtx@2x.png
  22. BIN
      src/assets/icons/detail/sjt@2x.png
  23. BIN
      src/assets/icons/detail/sjt_1@2x.png
  24. BIN
      src/assets/icons/detail/source@2x.png
  25. BIN
      src/assets/icons/detail/style@2x.png
  26. BIN
      src/assets/icons/detail/title@2x.png
  27. 14 0
      src/assets/icons/home/dcz.svg
  28. 14 0
      src/assets/icons/home/dpf.svg
  29. BIN
      src/assets/icons/home/icon_map_dcz@2x.png
  30. BIN
      src/assets/icons/home/icon_map_dpf@2x.png
  31. BIN
      src/assets/icons/home/icon_map_location@2x.png
  32. BIN
      src/assets/icons/home/icon_map_spjk@2x.png
  33. BIN
      src/assets/icons/home/icon_map_yjck@2x.png
  34. BIN
      src/assets/icons/home/icon_map_yjcl@2x.png
  35. BIN
      src/assets/icons/home/icon_map_yjdw@2x.png
  36. BIN
      src/assets/icons/home/icon_map_yjsj@2x.png
  37. 14 0
      src/assets/icons/home/spjk.svg
  38. 8 0
      src/assets/icons/home/yjck.svg
  39. 8 0
      src/assets/icons/home/yjcl.svg
  40. 9 0
      src/assets/icons/home/yjdw.svg
  41. 8 0
      src/assets/icons/home/yjsj.svg
  42. 0 9
      src/assets/icons/index.js
  43. 0 22
      src/assets/icons/svgo.yml
  44. 60 0
      src/components/Card/index.scss
  45. 24 6
      src/components/Card/index.tsx
  46. 145 0
      src/components/ChartMap/index.tsx
  47. 27 0
      src/components/MapView/index.tsx
  48. 193 0
      src/components/MarkerMap/constants.ts
  49. 164 0
      src/components/MarkerMap/dialog.ts
  50. 113 0
      src/components/MarkerMap/index.scss
  51. 411 0
      src/components/MarkerMap/index.tsx
  52. 7 4
      src/layout/BaseLayout/index.scss
  53. 7 6
      src/layout/BaseLayout/index.tsx
  54. 1 1
      src/main.ts
  55. 23 20
      src/router/index.ts
  56. 2 2
      src/store/index.ts
  57. 99 0
      src/store/useIncidentStore.ts
  58. 44 0
      src/store/useMarkerStore.ts
  59. 3 11
      src/styles/utils.scss
  60. 48 0
      src/views/HomePage/MessageCard/index.tsx
  61. 33 2
      src/views/HomePage/index.scss
  62. 18 130
      src/views/HomePage/index.tsx
  63. 43 0
      src/views/IncidentDetail/CommandChainCard/index.tsx
  64. 36 0
      src/views/IncidentDetail/EmergencyLinkageCard/index.tsx
  65. 45 0
      src/views/IncidentDetail/IncidentInfoCard/index.tsx
  66. 34 0
      src/views/IncidentDetail/IncidentPlanCard/index.tsx
  67. 226 0
      src/views/IncidentDetail/index.scss
  68. 31 0
      src/views/IncidentDetail/index.tsx

+ 131 - 0
src/api/incident.ts

@@ -0,0 +1,131 @@
+import request from '@/utils/request';
+import { BaseResponse } from './type';
+
+export interface IncidentItem {
+  id?: string; // 非必须
+  type?: number; // 非必须 事件类型
+  level?: number; // 非必须 事件等级
+  addr?: string; // 非必须 地址
+  locations?: string; // 非必须 经纬度
+  createDept?: string; // 非必须 上报部门
+  source?: string; // 非必须 来源
+  des?: string; // 非必须 事件描述
+  pic?: string; // 非必须 图片
+  video?: string; // 非必须 视频
+  expr1?: null; // 非必须
+  expr2?: null; // 非必须
+  exprJson?: null; // 非必须
+  name?: string; // 非必须
+  status?: number; // 非必须 状态
+  madinDept?: string; // 非必须
+  assistDept?: string; // 非必须
+  createBy?: string; // 非必须
+  createTime?: string; // 非必须
+}
+
+export interface IncidentListResponse extends BaseResponse {
+  rows?: IncidentItem[];
+  total?: number;
+  pageSize?: number;
+  pageNum?: number;
+}
+
+export interface GetIncidentListParams {
+  name?: string;
+  type?: 1 | 2 | 3 | 4 | 5;
+  level?: number | string;
+  status?: number | string;
+  'params[beginTime]'?: string; // yyyy-MM-dd
+  'params[endTime]'?: string; // yyyy-MM-dd
+}
+
+/** 事件列表查询 */
+export const getIncidentList = (params: GetIncidentListParams) =>
+  request<IncidentListResponse>('GET', {
+    url: '/zhdd/incident/list',
+    params: params,
+  });
+
+export interface IncidentItemDetailProcessItem {
+  id?: string;
+  incidentId?: string;
+  des?: string;
+  status?: null;
+  createTime?: string;
+  createBy?: null;
+}
+export interface IncidentItemDetailTaskItem {
+  id?: string;
+  incidentId?: string;
+  taskName?: string;
+  taskDes?: string;
+  source?: number;
+  delFlag?: string;
+  createTime?: string;
+  createBy?: null;
+}
+
+export interface IncidentItemDetail {
+  baseInfo?: IncidentItem; // 基本信息
+  process?: IncidentItemDetailProcessItem[]; //处置信息
+  task?: IncidentItemDetailTaskItem[]; // 处置任务
+}
+export interface IncidentItemDetailResponse extends BaseResponse {
+  data: IncidentItemDetail;
+}
+/** 事件详情 */
+export const getIncidentItem = (id: number | string) =>
+  request<IncidentItemDetailResponse>('GET', {
+    url: `/zhdd/incident/${id}`,
+  });
+
+/** 事件接报 */
+export const postIncidentItem = (params: IncidentItem) =>
+  request<BaseResponse>('POST', {
+    url: `/zhdd/incident`,
+    data: params,
+  });
+
+/**
+ * 事件修改
+ * ```
+ * 如果是更改状态的话,只需要传
+ *  { "id":"", "status":xx }
+ * ```
+ */
+export const putIncidentItem = (params: IncidentItem) =>
+  request<BaseResponse>('PUT', {
+    url: `/zhdd/incident`,
+    data: params,
+  });
+
+export interface IncidentTaskParam {
+  incidentId?: string; // 事件id
+  taskName?: string; // 任务名称
+  taskDes?: string; // 任务描述
+}
+
+/** 添加事件任务 TODO:// 什么接口?? */
+export const addIncidentTask = (params: IncidentItem) =>
+  request<BaseResponse>('POST', {
+    url: `/zhdd/incident/task`,
+    data: params,
+  });
+
+/** 删除事件任务 TODO:// 什么接口?? ids --> 1,2,3 ?? */
+export const deleteIncidentTask = (ids: string) =>
+  request<BaseResponse>('DELETE', {
+    url: `/zhdd/incident/task/${ids}`,
+  });
+
+export interface IncidentProcessParam {
+  incidentId?: string; // 事件id
+  des?: string; // 处置内容
+}
+
+/** 添加处置过程 */
+export const AddIncidentProcess = (params: IncidentProcessParam) =>
+  request<BaseResponse>('POST', {
+    url: `/incidentProcess`,
+    data: params,
+  });

+ 75 - 0
src/api/resource.ts

@@ -0,0 +1,75 @@
+import request from '@/utils/request';
+import { BaseResponse } from './type';
+
+export interface ResourceListItem {
+  id?: number;
+  resourceType?: number;
+  name?: string; //名称
+  address?: string; //地址
+  longitude?: string; // 经度
+  latitude?: string; //纬度
+  manageUnit?: string; //管理单位
+  num?: number; // 数量
+  carryGoods?: null; //携带物资(应急时)
+  contactName?: string; //联系人
+  contactPhone?: string; //联系电话
+}
+
+export interface GetResourceListParams {
+  name?: string;
+  manageUnit?: string; // 管理单位
+  resourceType: 1 | 2 | 3; // 资源类型。1-仓库。2-队伍.3-车辆
+}
+
+export interface ResourceListResponse extends BaseResponse {
+  rows?: ResourceListItem[];
+  total?: number;
+  pageSize?: number;
+  pageNum?: number;
+}
+
+export const getResourceList = (params: GetResourceListParams) =>
+  request<ResourceListResponse>('GET', {
+    url: '/zhdd/resource/list',
+    params: params,
+  });
+
+export const deleteResourceItem = (id: number) =>
+  request<BaseResponse>('DELETE', {
+    url: `/zhdd/resource/${id}`,
+  });
+
+export interface ResourceItemDetail extends ResourceListItem {
+  resourceDetailList?: {
+    createBy?: string;
+    createTime?: string;
+    delFlag?: boolean;
+    id?: number;
+    model?: string;
+    name?: string;
+    num?: number;
+    resourceId?: number;
+    size?: string;
+    unit?: string;
+  }[];
+}
+export interface ResourceItemDetailResponse extends BaseResponse {
+  data: ResourceItemDetail;
+}
+
+export const getResourceItem = (id: number) =>
+  request<ResourceItemDetailResponse>('GET', {
+    url: `/zhdd/resource/${id}`,
+  });
+
+export const saveResourceItem = (data: ResourceItemDetail) =>
+  request<BaseResponse>('POST', {
+    url: `/zhdd/resource`,
+    data,
+  });
+
+export const putResourceItem = (data: ResourceItemDetail) =>
+  request<BaseResponse>('PUT', {
+    url: `/zhdd/resource`,
+    data,
+  });

BIN
src/assets/bg_jgxt.jpg


BIN
src/assets/card-bg/frame_czfa.png


BIN
src/assets/card-bg/frame_filter.png


BIN
src/assets/card-bg/frame_filter_bottom@2x.png


BIN
src/assets/card-bg/frame_filter_center@2x.png


BIN
src/assets/card-bg/frame_filter_top@2x.png


BIN
src/assets/card-bg/frame_information.png


BIN
src/assets/card-bg/frame_sjxx.png


BIN
src/assets/card-bg/frame_skjc.png


BIN
src/assets/card-bg/frame_yjld.png


BIN
src/assets/card-bg/frame_zhtx.png


BIN
src/assets/card-bg/frame_zxrz.png


BIN
src/assets/card-bg/img_dialogue.png


BIN
src/assets/icons/detail/co_organized@2x.png


BIN
src/assets/icons/detail/command@2x.png


BIN
src/assets/icons/detail/dbpt@2x.png


BIN
src/assets/icons/detail/host@2x.png


BIN
src/assets/icons/detail/level@2x.png


BIN
src/assets/icons/detail/rhtx@2x.png


BIN
src/assets/icons/detail/sjt@2x.png


BIN
src/assets/icons/detail/sjt_1@2x.png


BIN
src/assets/icons/detail/source@2x.png


BIN
src/assets/icons/detail/style@2x.png


BIN
src/assets/icons/detail/title@2x.png


+ 14 - 0
src/assets/icons/home/dcz.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="25px" viewBox="0 0 24 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>dcz</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="图标" transform="translate(-875.000000, -355.000000)">
+            <g id="dcz" transform="translate(875.000000, 355.000000)">
+                <path d="M12,25 C20,20.7516113 24,16.418278 24,12 C24,5.372583 18.627417,0 12,0 C5.372583,0 0,5.372583 0,12 C0,16.418278 4,20.7516113 12,25 Z" id="椭圆形备份-2" fill="#568CD9"></path>
+                <g id="信息处置" transform="translate(7.000000, 4.000000)" fill="#FFFFFF" fill-rule="nonzero">
+                    <path d="M10.6528688,2.50595407 C10.64945,1.81536373 10.0904821,1.2563958 9.3998918,1.25297704 L7.23923291,1.25297704 C6.89393773,0.490592664 6.13497212,0 5.29737491,0 C4.4597777,0 3.7008121,0.490592646 3.35551694,1.25297704 L1.25297702,1.25297704 C0.562386687,1.25639581 0.00341875271,1.81536373 0,2.50595407 L0,12.2186628 C0.00341877074,12.9092531 0.562386687,13.468221 1.25297702,13.4716398 L4.54353744,13.4716398 L4.95037037,11.6511479 L10.6511594,5.95035886 L10.6511594,2.50595407 L10.6528688,2.50595407 Z M5.2990843,1.34699304 C5.51275705,1.34699304 5.71617352,1.43417153 5.86488976,1.58801591 C6.01189661,1.74186028 6.09223756,1.94869552 6.08369065,2.16236827 C6.09052817,2.60338883 5.74010486,2.96577783 5.30079368,2.97603412 C4.85977312,2.96235906 4.50934358,2.60167947 4.50934358,2.16065889 C4.50764042,1.71963833 4.85806374,1.35895872 5.2990843,1.34699304 Z M7.83580723,7.20504527 L1.57092209,7.20504527 L1.57092209,6.26488516 L7.83751661,6.26488516 L7.83751661,7.20504527 L7.83580723,7.20504527 Z M9.08878427,5.32472502 L1.56750332,5.32472502 L1.56750332,4.38456491 L9.0853655,4.38456491 L9.08878427,5.32472502 L9.08878427,5.32472502 Z M12.1280655,8.01871112 L11.3759374,7.26658302 C11.2870496,7.14863566 11.1485896,7.07855099 10.9998734,7.07855099 C10.8511571,7.07855099 10.7126972,7.14863564 10.6238093,7.26658302 L10.4050084,7.48538391 L5.89394925,11.8716582 L5.4238692,13.7519785 L5.4238692,13.8135162 C5.4238692,13.8442851 5.4238692,13.875054 5.45463806,13.875054 C5.48369756,13.9519761 5.55891037,14.0032576 5.64267009,13.9998388 L5.70591722,13.9998388 L7.58623745,13.4049739 L12.0032806,9.0186996 L12.2220815,8.7998987 C12.3605415,8.54520079 12.3212257,8.22896511 12.1246468,8.01700174 L12.1280655,8.01700174 L12.1280655,8.01871112 Z" id="形状"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 14 - 0
src/assets/icons/home/dpf.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="25px" viewBox="0 0 24 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>dpf</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="图标" transform="translate(-714.000000, -355.000000)">
+            <g id="dpf" transform="translate(714.000000, 355.000000)">
+                <path d="M12,25 C20,20.7516113 24,16.418278 24,12 C24,5.372583 18.627417,0 12,0 C5.372583,0 0,5.372583 0,12 C0,16.418278 4,20.7516113 12,25 Z" id="椭圆形备份" fill="#FAAD14"></path>
+                <g id="任务派发" transform="translate(4.000000, 4.000000)" fill="#FFFFFF" fill-rule="nonzero">
+                    <path d="M13.7852175,0.0877217644 C13.9500111,0.19596966 14.0330018,0.393109071 13.9952437,0.586626129 L11.9952046,12.5861236 C11.9694615,12.7346308 11.8776781,12.8634151 11.745701,12.9362103 C11.6137239,13.0090055 11.455832,13.0179357 11.3164884,12.9604861 L7.77773096,11.5146214 L5.88602121,13.8212251 C5.7949521,13.9395312 5.65197164,14.0060721 5.50281549,13.9995631 C5.44352049,14.001068 5.38455619,13.9902788 5.32963598,13.967875 C5.23261104,13.9339228 5.14913172,13.8695688 5.09160628,13.7843784 C5.03203222,13.7009506 5.00007725,13.6009625 5.00022645,13.498448 L5.00022645,10.7740027 L11.7505425,2.50044391 L3.39813179,9.72829325 L0.312588842,8.46297746 C0.12024905,8.39149485 0.015604412,8.24779271 0.000865730667,8.03260796 C-0.0094513463,7.82405562 0.0723483354,7.6700364 0.251423314,7.5705503 L13.2509403,0.0700353468 C13.3263239,0.0238525026 13.4130933,-0.000391909804 13.5014979,-6.54528937e-17 C13.6029006,-0.00103388473 13.702099,0.0296274144 13.7852175,0.0877217644 Z" id="路径"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

BIN
src/assets/icons/home/icon_map_dcz@2x.png


BIN
src/assets/icons/home/icon_map_dpf@2x.png


BIN
src/assets/icons/home/icon_map_location@2x.png


BIN
src/assets/icons/home/icon_map_spjk@2x.png


BIN
src/assets/icons/home/icon_map_yjck@2x.png


BIN
src/assets/icons/home/icon_map_yjcl@2x.png


BIN
src/assets/icons/home/icon_map_yjdw@2x.png


BIN
src/assets/icons/home/icon_map_yjsj@2x.png


+ 14 - 0
src/assets/icons/home/spjk.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="25px" viewBox="0 0 24 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>spjk</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="图标" transform="translate(-553.000000, -457.000000)">
+            <g id="spjk" transform="translate(553.000000, 457.000000)">
+                <path d="M12,25 C20,20.7516113 24,16.418278 24,12 C24,5.372583 18.627417,0 12,0 C5.372583,0 0,5.372583 0,12 C0,16.418278 4,20.7516113 12,25 Z" id="椭圆形备份-7" fill="#568CD9"></path>
+                <g id="监控" transform="translate(6.000000, 6.000000)" fill="#FFFFFF" fill-rule="nonzero">
+                    <path d="M0,6.29206187 C0,6.09447477 0.148269868,5.99774935 0.329630023,6.07761146 L1.7254668,6.69041784 C1.90682694,6.76996177 2.05509682,6.99650286 2.05509682,7.19408996 L2.05509682,7.99175644 L4.39368817,7.99175644 L4.84549766,7.01527524 L6.22765288,7.65799013 L5.57412,9.07069022 C5.44879128,9.34006045 5.17855008,9.51223525 4.88145152,9.51199991 L2.05446046,9.51199991 L2.05446046,10.3096664 C2.05446046,10.5075716 1.90650877,10.7337946 1.72483044,10.8133385 L0.329630023,11.4267812 C0.148269884,11.506007 0,11.4099179 0,11.2120126 L0,6.29206187 Z M13.1629285,6.34583357 L12.2099923,8.38279437 L10.8466094,7.74898838 L13.1629285,6.34583357 Z M4.24987276,0.0669544068 L13.719736,4.46923332 C14.07832,4.63659373 14.0951833,4.9407697 13.7566443,5.14567486 L9.50995335,7.71844352 C9.17141441,7.92334867 8.60060722,7.95452988 8.24138685,7.78780582 L1.99655249,4.88477078 C1.63730556,4.71689926 1.48140411,4.29016171 1.64783193,3.93024369 L3.29216392,0.414402266 C3.46193835,0.0553692085 3.88938251,-0.0997035569 4.24987276,0.0669544068 Z" id="形状"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

Fișier diff suprimat deoarece este prea mare
+ 8 - 0
src/assets/icons/home/yjck.svg


Fișier diff suprimat deoarece este prea mare
+ 8 - 0
src/assets/icons/home/yjcl.svg


Fișier diff suprimat deoarece este prea mare
+ 9 - 0
src/assets/icons/home/yjdw.svg


Fișier diff suprimat deoarece este prea mare
+ 8 - 0
src/assets/icons/home/yjsj.svg


+ 0 - 9
src/assets/icons/index.js

@@ -1,9 +0,0 @@
-import Vue from 'vue'
-import SvgIcon from '@/components/SvgIcon'// svg component
-
-// register globally
-Vue.component('svg-icon', SvgIcon)
-
-const req = require.context('./svg', false, /\.svg$/)
-const requireAll = requireContext => requireContext.keys().map(requireContext)
-requireAll(req)

+ 0 - 22
src/assets/icons/svgo.yml

@@ -1,22 +0,0 @@
-# replace default config
-
-# multipass: true
-# full: true
-
-plugins:
-
-  # - name
-  #
-  # or:
-  # - name: false
-  # - name: true
-  #
-  # or:
-  # - name:
-  #     param1: 1
-  #     param2: 2
-
-- removeAttrs:
-    attrs:
-      - 'fill'
-      - 'fill-rule'

+ 60 - 0
src/components/Card/index.scss

@@ -0,0 +1,60 @@
+@import '../../styles/utils.scss';
+
+.card-container {
+  width: 470px;
+  height: 472px;
+  width: px2rem(470px);
+  height: px2rem(472px);
+  background-position: top center;
+  background-size: cover;
+  z-index: 1;
+  box-sizing: border-box;
+  padding: px2rem(80px) px2rem(30px) px2rem(30px);
+  display: flex;
+  flex-direction: column;
+  color: #fff;
+  > div {
+    flex: 1;
+    overflow-y: auto;
+    &::-webkit-scrollbar {
+      width: 5px;
+    }
+    &::-webkit-scrollbar-track {
+      background: transparent;
+    }
+    &::-webkit-scrollbar-thumb {
+      background: #39d6fe;
+      border-radius: 3px;
+    }
+  }
+  &-bg-message-list {
+    background-image: url('../../assets/card-bg/frame_information.png');
+  }
+  &-bg-incident-info {
+    background-image: url('../../assets/card-bg/frame_sjxx.png');
+  }
+  &-bg-command-chain {
+    background-image: url('../../assets/card-bg/frame_zhtx.png');
+  }
+  &-bg-incident-plan {
+    background-image: url('../../assets/card-bg/frame_czfa.png');
+  }
+  &-bg-emergency-linkage {
+    background-image: url('../../assets/card-bg/frame_yjld.png');
+  }
+
+  &-bg-execution-log {
+    width: 939px;
+    height: 554px;
+    width: px2rem(939px);
+    height: px2rem(554px);
+    background-image: url('../../assets/card-bg/frame_zxrz.png');
+  }
+  &-bg-live-monitoring {
+    width: 939px;
+    height: 390px;
+    width: px2rem(939px);
+    height: px2rem(390px);
+    background-image: url('../../assets/card-bg/frame_skjc.png');
+  }
+}

+ 24 - 6
src/components/Card/index.tsx

@@ -1,11 +1,29 @@
-import { defineComponent } from 'vue-demi';
-
-export type CardType = 'incident-info' | 'command-chain';
+import clsx from 'clsx';
+import { defineComponent, PropType } from 'vue-demi';
+import './index.scss';
+export type CardType =
+  | 'message-list'
+  | 'incident-info'
+  | 'command-chain'
+  | 'incident-plan'
+  | 'emergency-linkage'
+  | 'execution-log'
+  | 'live-monitoring';
 
 export default defineComponent({
   name: 'Card',
-  props: {},
-  setup() {
-    return () => <div class></div>;
+  props: {
+    cardType: {
+      type: String as PropType<CardType>,
+      default: 'incident-info',
+    },
+  },
+  setup(props, ctx) {
+    const className = clsx('card-container', {
+      [`card-container-bg-${props.cardType}`]: props.cardType,
+    });
+    return () => (
+      <div class={className}>{ctx.slots.default && ctx.slots.default()}</div>
+    );
   },
 });

+ 145 - 0
src/components/ChartMap/index.tsx

@@ -0,0 +1,145 @@
+import { ref, reactive, defineComponent, onMounted } from 'vue';
+import { registerMap, use } from 'echarts/core';
+import { CanvasRenderer } from 'echarts/renderers';
+import { MapChart, EffectScatterChart } from 'echarts/charts';
+import {
+  TitleComponent,
+  TooltipComponent,
+  LegendComponent,
+} from 'echarts/components';
+import VChart, { THEME_KEY } from 'vue-echarts';
+import GeoJSon from '@/assets/jiang1_su1_su4_qian1';
+import { ECBasicOption } from 'echarts/types/dist/shared';
+
+use([
+  CanvasRenderer,
+  MapChart,
+  TitleComponent,
+  TooltipComponent,
+  LegendComponent,
+  EffectScatterChart,
+]);
+
+registerMap('SuQian', {
+  geoJSON: GeoJSon,
+  specialAreas: {},
+});
+
+export default defineComponent({
+  name: 'HomePage',
+  provide: {
+    // [THEME_KEY]: 'dark',
+  },
+  setup() {
+    const echart = ref();
+    const option = ref<ECBasicOption>({
+      // title: {
+      //   text: "Traffic Sources",
+      //   left: "center",
+      // },
+      // tooltip: {
+      //   trigger: "item",
+      //   formatter: "{a} <br/>{b} : {c} ({d}%)",
+      // },
+      legend: {
+        orient: 'vertical',
+        left: 'left',
+        data: ['Direct', 'Email', 'Ad Networks', 'Video Ads', 'Search Engines'],
+      },
+      geo: {
+        map: 'SuQian',
+        aspectScale: 0.75, //长宽比
+        zoom: 1.1,
+        roam: false,
+        itemStyle: {
+          normal: {
+            areaColor: '#013C62',
+            shadowColor: '#182f68',
+            shadowOffsetX: 0,
+            shadowOffsetY: 25,
+          },
+          emphasis: {
+            areaColor: '#2AB8FF',
+            borderWidth: 0,
+            color: 'green',
+            label: {
+              show: false,
+            },
+          },
+        },
+      },
+      series: [
+        {
+          type: 'map',
+          map: 'SuQian',
+          roam: false,
+          label: {
+            // show: false,
+            normal: {
+              show: false,
+              textStyle: {
+                color: '#fff',
+              },
+            },
+            emphasis: {
+              // show: false,
+              textStyle: {
+                color: '#fff',
+              },
+            },
+          },
+          itemStyle: {
+            normal: {
+              borderColor: '#2ab8ff',
+              borderWidth: 1.5,
+              areaColor: '#12235c',
+            },
+            emphasis: {
+              areaColor: '#12235c',
+              // borderWidth: 0,
+              color: 'green',
+            },
+            select: {
+              areaColor: '#12235c',
+            },
+          },
+          zoom: 1.2,
+        },
+        {
+          type: 'effectScatter',
+          coordinateSystem: 'geo',
+          showEffectOn: 'render',
+          rippleEffect: {
+            period: 4,
+            scale: 4,
+            brushType: 'fill',
+          },
+          hoverAnimation: true,
+          itemStyle: {
+            normal: {
+              color: '#2ab8ff',
+              shadowBlur: 10,
+              shadowColor: '#333',
+            },
+          },
+          data: [
+            {
+              name: '宿州学院',
+              value: [118.296459, 33.929648],
+            },
+          ],
+          tooltip: {
+            position: [10, 10],
+            formatter: () => {
+              return '<div>123</div>';
+            },
+          },
+        },
+      ],
+    });
+
+    onMounted(() => {});
+
+    return () => <VChart ref={echart} autoresize option={option.value} />;
+  },
+});

+ 27 - 0
src/components/MapView/index.tsx

@@ -0,0 +1,27 @@
+import { defineComponent, onMounted, ref } from 'vue';
+
+let ispro = process.env.NODE_ENV === 'production';
+export default defineComponent({
+  props: {
+    map: Object,
+  },
+  setup(props, ctx) {
+    const mapRef = ref<Element>();
+
+    onMounted(() => {
+      const map = new window.minemap.Map({
+        container: 'map',
+        style: 'https://minedata.cn/service/solu/style/id/12878' /*底图样式*/,
+        center: [118.29564, 33.97441] /*地图中心点*/,
+        zoom: 14 /*地图默认缩放等级*/,
+        pitch: 0 /*地图俯仰角度*/,
+        maxZoom: 17 /*地图最大缩放等级*/,
+        minZoom: 3 /*地图最小缩放等级*/,
+        // projection: 'MERCATOR',
+        logoControl: false,
+      });
+      ctx.emit('update:map', map);
+    });
+    return () => <div id="map" style={{ height: '100%' }} ref={mapRef} />;
+  },
+});

+ 193 - 0
src/components/MarkerMap/constants.ts

@@ -0,0 +1,193 @@
+const ICON_MAP_TYPES = {
+  WARNING_INCIDENT: { name: '预警事件' as const },
+  PENDING_INCIDENT: { name: '待派发事件' as const },
+  PENDING_DISPOSAL_INCIDENT: { name: '待处置事件' as const },
+  TRAFFIC_INFO: { name: '路况信息' as const },
+  VIDEO_SURVEILLANCE: { name: '视频监控' as const },
+  EMERGENCY_VEHICLES: { name: '应急车辆' as const },
+  EMERGENCY_TEAM: { name: '应急队伍' as const },
+  EMERGENCY_WAREHOUSE: { name: '应急仓库' as const },
+};
+
+export const EMERGENCY_TEAM = [
+  {
+    name: '宿迁学院',
+    location: '118.296459,33.929648',
+  },
+  {
+    location: '118.504329,33.903868',
+    name: '宿迁市大兴派出所',
+  },
+  {
+    location: '118.291631,33.884288',
+    name: '明珠派出所',
+  },
+  {
+    location: '118.790799,33.705629',
+    name: '来安派出所',
+  },
+];
+export const WARNING_INCIDENT = [
+  {
+    name: '宿迁学院',
+    location: '118.296459,33.929648',
+  },
+  {
+    location: '118.504329,33.903868',
+    name: '宿迁市大兴派出所',
+  },
+  {
+    location: '118.291631,33.884288',
+    name: '明珠派出所',
+  },
+  {
+    location: '118.790799,33.705629',
+    name: '来安派出所',
+  },
+];
+
+export const PENDING_INCIDENT = [
+  {
+    location: '118.288721,33.951047',
+    name: '宿迁宝龙城市广场',
+  },
+  {
+    location: '118.285469,33.950549',
+    name: '用世水韵城',
+  },
+  {
+    location: '118.290689,33.952558',
+    name: '金鹰天地广场(宿迁店)',
+  },
+  {
+    location: '118.300139,33.955468',
+    name: '苏宁广场',
+  },
+  {
+    location: '118.300149,33.953758',
+    name: '宿迁人民商场',
+  },
+  {
+    location: '118.262051,33.956094',
+    name: '鑫鑫奶站',
+  },
+  {
+    location: '118.27269,33.97476',
+    name: '恒茂商业广场',
+  },
+];
+export const EMERGENCY_VEHICLES = [
+  {
+    location: '118.288721,33.951047',
+    name: '宿迁宝龙城市广场',
+  },
+  {
+    location: '118.285469,33.950549',
+    name: '用世水韵城',
+  },
+  {
+    location: '118.290689,33.952558',
+    name: '金鹰天地广场(宿迁店)',
+  },
+  {
+    location: '118.300139,33.955468',
+    name: '苏宁广场',
+  },
+  {
+    location: '118.300149,33.953758',
+    name: '宿迁人民商场',
+  },
+  {
+    location: '118.262051,33.956094',
+    name: '鑫鑫奶站',
+  },
+  {
+    location: '118.27269,33.97476',
+    name: '恒茂商业广场',
+  },
+];
+export const PENDING_DISPOSAL_INCIDENT = [
+  {
+    name: '宿迁市中心血站',
+    location: '118.273349,33.954288',
+  },
+  {
+    name: '早点中心',
+    location: '118.296279,33.962388',
+  },
+  {
+    name: '宿迁市体育运动中心',
+    location: '118.266809,33.953318',
+  },
+  {
+    name: '宿迁市体育运动中心',
+    location: '118.268309,33.953778',
+  },
+  {
+    name: '宿迁市奥体中心',
+    location: '118.290087,34.004411',
+  },
+  {
+    name: '中心大酒店',
+    location: '118.245129,33.938378',
+  },
+  {
+    name: '中心庄',
+    location: '118.752399,34.118339',
+  },
+  {
+    name: '中心桥',
+    location: '118.404649,33.415169',
+  },
+  {
+    name: '国贸中心',
+    location: '118.270089,33.895378',
+  },
+  {
+    name: '泗洪县中心医院',
+    location: '118.243579,33.460968',
+  },
+];
+
+export const VIDEO_SURVEILLANCE = [
+  {
+    name: '宿迁市第一人民医院',
+    location: '118.269259,33.974288',
+  },
+  {
+    name: '宿迁市宿城区人民医院',
+    location: '118.300829,33.967868',
+  },
+  {
+    name: '宿迁市工人医院(新)',
+    location: '118.268819,33.939198',
+  },
+  {
+    name: '南京鼓楼医院集团宿迁市人民医院',
+    location: '118.296529,33.945168',
+  },
+  {
+    name: '宿迁市儿童医院(旧)',
+    location: '118.295179,33.947398',
+  },
+  {
+    name: '宿迁市儿童医院',
+    location: '118.255491,33.949056',
+  },
+  {
+    name: '宿迁市第一人民医院-药学部',
+    location: '118.268199,33.976638',
+  },
+  {
+    name: '宿迁市钟吾医院',
+    location: '118.275649,33.930958',
+  },
+  {
+    name: '宿迁市骨科医院',
+    location: '118.270939,33.959068',
+  },
+  {
+    name: '钟吾医院-住院大楼',
+    location: '118.275809,33.929618',
+  },
+];

+ 164 - 0
src/components/MarkerMap/dialog.ts

@@ -0,0 +1,164 @@
+import { IncidentItem } from '@/api/incident';
+import { ResourceItemDetail } from '@/api/resource';
+
+export const GET_INCIDENT_DIALOG_HTML = (
+  item: IncidentItem,
+  callback = () => {},
+) => {
+  const el = document.createElement('div');
+  el.innerHTML = `
+<div>
+  <div class="title">事件信息</div>
+  <div class="content">
+    <div><span>标题:</span><span>${item?.name ?? '-'}</span></div>
+    <div><span>来源:</span><span>${item?.source ?? '-'}</span></div>
+    <div><span>类型:</span><span>${item?.type ?? '-'}</span></div>
+    <div><span>时间:</span><span>${item?.createTime ?? '-'}</span></div>
+    <div><span>地点:</span><span>${item?.addr ?? '-'}</span></div>
+    <div><span>描述:</span><span>${item?.des ?? '-'}</span></div>
+  </div>
+  
+  <i class="card-border-bottom-left"></i>
+  <i class="card-border-bottom-right"></i>
+</div>
+`;
+
+  const action = document.createElement('div');
+  action.className = 'action';
+  const button = document.createElement('button');
+  button.className = 'el-button el-button--primary el-button--small';
+  button.innerHTML = '查看';
+  action.appendChild(button);
+  button.addEventListener('click', callback);
+
+  el.appendChild(action);
+
+  return el;
+};
+
+// •	视频打开后显示数据:
+// a.视频点位编号
+// b.视频点位地点
+// c.按钮可以打开一个窗口,然后窗口中显示摄像头拍摄内容 (目前还无该项目数据,暂定为该内容)
+
+export const GET_VIDEO_DIALOG_HTML = ({
+  name,
+  addr,
+  link,
+}: {
+  name: string;
+  addr: string;
+  link: string;
+}) => {
+  const el = document.createElement('div');
+  el.innerHTML = `
+  <div>
+    <div class="title">视频监控信息</div>
+    <div class="content">
+        <div><span>编号:</span><span>${name ?? '-'}</span></div>
+        <div><span>地点:</span><span>${addr ?? '-'}</span></div>
+    </div>
+    <div class="action">
+        <button class="el-button el-button--primary el-button--small" type="button">
+            <span>查看</span>
+        </button>
+    </div>
+    <i class="card-border-bottom-left"></i>
+    <i class="card-border-bottom-right"></i>
+  
+  </div>
+  `;
+  return el;
+};
+
+// •	应急仓库
+// a.应急仓库名称
+// b.应急仓库地点
+// c.应急仓库管理单位
+// d.联系人
+// e.联系电话
+// f.按钮(方式一:跳转应急仓库详情查看物资情况,方式二:在按钮旁边打开一个列表,该列表显示应急仓库的物资情况:物资名称、型号、数量)
+
+export const GET_WAREHOUSE_DIALOG_HTML = (item: ResourceItemDetail) => {
+  const el = document.createElement('div');
+  el.innerHTML = `
+  <div>
+    <div class="title">应急仓库信息</div>
+    <div class="content">
+      <div><span>名称:</span><span>${item.name ?? '-'}</span></div>
+      <div><span>地点:</span><span>${item.address ?? '-'}</span></div>
+      <div><span>管理单位:</span><span>${item.manageUnit ?? '-'}</span></div>
+      <div><span>联系人:</span><span>${item.contactName ?? '-'}</span></div>
+      <div><span>联系方式:</span><span>${item.contactPhone ?? '-'}</span></div>
+    </div>
+    <div class="action">
+      <button class="el-button el-button--primary el-button--small" type="button">
+        <span>查看物资情况</span>
+      </button>
+    </div>
+    <i class="card-border-bottom-left"></i>
+    <i class="card-border-bottom-right"></i>
+  </div>
+  `;
+  return el;
+};
+
+// •	应急车辆
+// a.车牌号
+// b.归属单位
+// c.车辆类型 (应急车辆为实时位置,目前暂无具体数据)
+
+export const GET_VEHICLES_DIALOG_HTML = (item: ResourceItemDetail) => {
+  const el = document.createElement('div');
+  el.innerHTML = `
+    <div>
+      <div class="title">应急车辆信息</div>
+      <div class="content">
+        <div><span>车牌号:</span><span>${item.name ?? '-'}</span></div>
+        <div><span>管理单位:</span><span>${item.manageUnit ?? '-'}</span></div>
+        <div><span>车辆类型:</span><span>-</span></div>
+      </div>
+      <i class="card-border-bottom-left"></i>
+      <i class="card-border-bottom-right"></i>
+    </div>
+  `;
+  return el;
+};
+
+// •	应急队伍
+// a.队伍名称
+// b.管理单位
+// c.携带物资
+// d.人数
+// e. 联系人
+// f.联系电话
+
+export const GET_TEAM_DIALOG_HTML = (item: ResourceItemDetail) => {
+  const el = document.createElement('div');
+  el.innerHTML = `
+<div>
+  <div class="title">应急队伍信息</div>
+  <div class="content">
+    <div><span>队伍名称:</span><span>${item.name ?? '-'}</span></div>
+    <div><span>管理单位:</span><span>${item.manageUnit ?? '-'}</span></div>
+    <div><span>携带物资:</span><span>${item.carryGoods ?? '-'}</span></div>
+    <div><span>人数:</span><span>${item.num ?? '-'}</span></div>
+    <div><span>联系人:</span><span>${item.contactName ?? '-'}</span></div>
+    <div><span>联系方式:</span><span>${item.contactPhone ?? '-'}</span></div>
+  </div>
+  <i class="card-border-bottom-left"></i>
+  <i class="card-border-bottom-right"></i>
+</div>
+  `;
+  return el;
+};
+
+export const renderElement = (image: any) => {
+  const el = document.createElement('div');
+  el.id = 'marker';
+  el.style.backgroundImage = `url(${image})`;
+  el.style.backgroundSize = 'cover';
+  el.style.width = '24px';
+  el.style.height = '25px';
+  return el;
+};

+ 113 - 0
src/components/MarkerMap/index.scss

@@ -0,0 +1,113 @@
+@import '../../styles/utils.scss';
+
+.card-bg-container {
+  box-sizing: border-box;
+  background-image: url('../../assets/card-bg/frame_filter.png');
+  background-position: top center;
+  background-repeat: repeat-y;
+  .card-border-bottom-left,
+  .card-border-bottom-right,
+  &::before,
+  &::after {
+    content: '';
+    position: absolute;
+    width: px2rem(40px);
+    height: px2rem(40px);
+    background-color: transparent;
+    border-left: px2rem(1px) solid #00a3ff;
+    border-top: px2rem(1px) solid #00a3ff;
+    top: 0;
+    left: 0;
+  }
+  .card-border-bottom-right,
+  &::after {
+    border-left: unset;
+    border-right: px2rem(1px) solid #00a3ff;
+    right: 0;
+    left: unset;
+  }
+  .card-border-bottom-left,
+  .card-border-bottom-right {
+    bottom: 0;
+    top: unset;
+    border-bottom: px2rem(1px) solid #00a3ff;
+    border-top: unset;
+  }
+}
+
+.task-map-container {
+  height: 100vh;
+  .minemap-map {
+    .minemap-popup-tip {
+      display: none;
+    }
+    .minemap-popup-content {
+      max-width: unset !important;
+      padding: unset;
+      width: px2rem(332px);
+      width: 332px;
+
+      @extend .card-bg-container;
+
+      background: #010d85;
+      color: #fff;
+
+      .title {
+        padding: 22px 20px 15px;
+        box-sizing: border-box;
+        // background: #003a8c;
+        // border-radius: 1px;
+        font-size: 20px;
+        color: #00e9ff;
+      }
+      .action {
+        margin: 0 24px 24px;
+      }
+      .content {
+        height: max-content;
+        padding: 24px;
+        // background: #fff;
+        > div {
+          display: flex;
+          font-size: 14px;
+          line-height: 20px;
+          margin-bottom: 10px;
+          > span:first-child {
+            min-width: max-content;
+          }
+        }
+      }
+    }
+  }
+  .address-type-card {
+    position: absolute;
+    top: px2rem(83px);
+    right: px2rem(30px);
+    width: max-content;
+    min-width: px2rem(178px);
+    min-height: px2rem(293px);
+    width: max-content;
+    height: max-content;
+    border-radius: 1px;
+    padding: px2rem(27px) px2rem(36px);
+    box-sizing: border-box;
+
+    @extend .card-bg-container;
+
+    .el-checkbox__inner {
+      background-color: transparent;
+    }
+    .el-checkbox__label {
+      color: #fff;
+    }
+    .card-item {
+      display: flex;
+      align-items: center;
+      margin-right: 0;
+      & > span {
+        font-size: 14px;
+        font-weight: 400;
+      }
+    }
+  }
+}

+ 411 - 0
src/components/MarkerMap/index.tsx

@@ -0,0 +1,411 @@
+import {
+  defineComponent,
+  reactive,
+  PropType,
+  onMounted,
+  watchEffect,
+  watch,
+  computed,
+} from 'vue';
+import { useRouter } from 'vue-router';
+import isString from 'lodash/isString';
+import useMarkerStore, { MarkerType } from '@/store/useMarkerStore';
+import MapView from '../MapView';
+
+/** @ts-ignore */
+import icon_map_yjcl from '@/assets/icons/home/yjcl.svg';
+/** @ts-ignore */
+import icon_map_yjsj from '@/assets/icons/home/yjsj.svg';
+/** @ts-ignore */
+import icon_map_yjdw from '@/assets/icons/home/yjdw.svg';
+/** @ts-ignore */
+import icon_map_yjck from '@/assets/icons/home/yjck.svg';
+/** @ts-ignore */
+import icon_map_spjk from '@/assets/icons/home/spjk.svg';
+/** @ts-ignore */
+import icon_map_dcz from '@/assets/icons/home/dcz.svg';
+/** @ts-ignore */
+import icon_map_dpf from '@/assets/icons/home/dpf.svg';
+
+console.log(icon_map_dpf);
+
+import './index.scss';
+import {
+  GET_INCIDENT_DIALOG_HTML,
+  GET_TEAM_DIALOG_HTML,
+  GET_VEHICLES_DIALOG_HTML,
+  GET_VIDEO_DIALOG_HTML,
+  GET_WAREHOUSE_DIALOG_HTML,
+  renderElement,
+} from './dialog';
+
+const MARKER_MAP_TYPES = [
+  // '待派发事件',
+  // '待处置事件',
+  '路况信息',
+  '视频监控',
+  '应急车辆',
+  '应急队伍',
+  '应急仓库',
+  '应急事件',
+] as const;
+
+export type MarkerMapType = typeof MARKER_MAP_TYPES[number];
+
+interface State {
+  map: any;
+  types: MarkerMapType[];
+  trafficLayerIds: any[];
+  timer?: NodeJS.Timer | null;
+  trafficStatus: boolean;
+  markers: MarkerType[];
+  positions: string[];
+  hasTypes: string[];
+}
+
+interface ActionType {
+  type: MarkerMapType;
+  hasActioned: boolean;
+  action: Function;
+  remove: Function;
+}
+
+// 路况信息刷新间隔
+const REFESH_TRAFFIC_TIME = 60000;
+
+export default defineComponent({
+  name: 'MarkerMap',
+  props: {
+    adrsMapTypes: {
+      type: Array as PropType<string[]>,
+      default: MARKER_MAP_TYPES,
+    },
+    readonly: Boolean,
+  },
+  setup(props, ctx) {
+    const state = reactive<State>({
+      map: null,
+      types: [],
+      trafficLayerIds: [],
+      timer: undefined,
+      trafficStatus: false,
+      markers: [],
+      positions: [],
+      hasTypes: [],
+    });
+
+    const store = useMarkerStore();
+    const router = useRouter();
+
+    const actionTypes = computed<ActionType[]>(() => [
+      {
+        type: '路况信息',
+        hasActioned: state.trafficStatus,
+        action: toggleControlTraffic,
+        remove: toggleControlTraffic,
+      },
+      {
+        type: '应急事件',
+        hasActioned: state.hasTypes.includes('应急事件'),
+        action: () =>
+          handleAddMarkers('应急事件', store.pendingIncident, icon_map_dpf),
+        remove: () => handleRemoveMarkers('应急事件', store.pendingIncident),
+      },
+
+      {
+        type: '视频监控',
+        hasActioned: state.hasTypes.includes('视频监控'),
+        action: () =>
+          handleAddMarkers('视频监控', store.videoSurveillance, icon_map_spjk),
+        remove: () => handleRemoveMarkers('视频监控', store.videoSurveillance),
+      },
+      {
+        type: '应急车辆',
+        hasActioned: state.hasTypes.includes('应急车辆'),
+        action: () =>
+          handleAddMarkers('应急车辆', store.emergencyVehicles, icon_map_yjcl),
+        remove: () => handleRemoveMarkers('应急车辆', store.emergencyVehicles),
+      },
+      {
+        type: '应急队伍',
+        hasActioned: state.hasTypes.includes('应急队伍'),
+        action: () =>
+          handleAddMarkers('应急队伍', store.emergencyTeam, icon_map_yjdw),
+        remove: () => handleRemoveMarkers('应急队伍', store.emergencyTeam),
+      },
+      {
+        type: '应急仓库',
+        hasActioned: state.hasTypes.includes('应急仓库'),
+        action: () =>
+          handleAddMarkers('应急仓库', store.emergencyWarehouse, icon_map_yjck),
+        remove: () => handleRemoveMarkers('应急仓库', store.emergencyWarehouse),
+      },
+    ]);
+
+    const getMarkerPopupHTML = (type: MarkerMapType) => {
+      switch (type) {
+        case '应急事件':
+        // case '待派发事件':
+        // case '预警事件':
+        default:
+          return GET_INCIDENT_DIALOG_HTML(
+            {
+              name: '事件名称',
+              addr: 'su qian',
+            },
+            () => router.push('/incidentDetail'),
+          );
+        case '应急仓库':
+          return GET_WAREHOUSE_DIALOG_HTML({
+            name: '应急仓库 111',
+            address: 'suqian',
+            manageUnit: 'xxxx',
+            contactName: '张三',
+            contactPhone: '138 1234 1234',
+          });
+        case '应急车辆':
+          return GET_VEHICLES_DIALOG_HTML({
+            name: '苏A· 12345',
+            manageUnit: 'xxxx',
+          });
+        case '应急队伍':
+          return GET_TEAM_DIALOG_HTML({
+            name: 'Team 1',
+            manageUnit: 'xxxxx',
+            // carryGoods: '水, 饮料',
+            // num: '2',
+            contactName: '张三',
+            contactPhone: '138 1234 1234',
+          });
+
+        case '视频监控':
+          return GET_VIDEO_DIALOG_HTML({
+            name: '12312313',
+            addr: 'su qian',
+            link: '',
+          });
+      }
+    };
+    const updateTrafficSource = () => {
+      if (state.map.getSource('Traffic')) {
+        state.map.removeSource('Traffic');
+      }
+      state.map.addSource('Traffic', {
+        type: 'vector',
+        traffic: true,
+        tiles: [
+          'mineserver://data/dynamic-traffic/ertic?servicetype=0&z={z}&x={x}&y={y}',
+        ],
+      });
+    };
+    const updateTrafficLayerVisibility = (v: 'none' | 'visible') => {
+      state.trafficLayerIds.forEach(function (id) {
+        if (state.map?.getLayer(id)) {
+          state.map?.setLayoutProperty(id, 'visibility', v);
+        }
+      });
+    };
+    const toggleControlTraffic = () => {
+      if (state.trafficStatus) {
+        state.trafficStatus = false;
+        if (state.timer) {
+          clearInterval(state.timer);
+          state.timer = null;
+        }
+        updateTrafficLayerVisibility('none');
+      } else {
+        state.trafficStatus = true;
+        if (state.timer) {
+          clearInterval(state.timer);
+          state.timer = null;
+        }
+        updateTrafficSource();
+        state.timer = setInterval(function () {
+          updateTrafficSource();
+        }, REFESH_TRAFFIC_TIME);
+        updateTrafficLayerVisibility('visible');
+      }
+    };
+    const handleAddMarkers = (
+      type: MarkerMapType,
+      markers: State['markers'],
+      image: any,
+    ) => {
+      state.markers.push(
+        ...markers.map((i) => {
+          const popup = new window.minemap.Popup({
+            anchor: 'left',
+            closeOnClick: false,
+            closeButton: false,
+            offset: [10, 25],
+            maxWidth: 'max-content',
+            // autoPan: true,
+          }).setDOMContent(getMarkerPopupHTML(type));
+          return {
+            location: i.location,
+            popup,
+            marker: new window.minemap.Marker(renderElement(image), {
+              offset: [-25, -25],
+            })
+              .setLngLat({
+                lng: i.location?.split(',')[0],
+                lat: i.location?.split(',')[1],
+              })
+              .setPopup(popup)
+              .addTo(state.map),
+          };
+        }),
+      );
+      state.positions.push(...markers.map((i) => i.location).filter(isString));
+
+      handleFitBounds();
+    };
+    const handleFitBounds = () => {
+      if (state.positions.length === 0) {
+        return;
+      }
+
+      const locations = state.positions.map((i) => i.split(',').map(Number));
+
+      const leftTop = locations.reduce((carry, next) => {
+        if (carry.length === 0) return next;
+        return [Math.min(carry[0], next[0]), Math.max(carry[1], next[1])];
+      }, []);
+
+      const rightBottom = locations.reduce((carry, next) => {
+        if (carry.length === 0) return next;
+        return [Math.max(carry[0], next[0]), Math.min(carry[1], next[1])];
+      }, []);
+
+      const leftTopBounds: number[][] = new window.minemap.LngLat(...leftTop)
+        .toBounds(10000)
+        .toArray();
+
+      const rightBottomBounds: number[][] = new window.minemap.LngLat(
+        ...rightBottom,
+      )
+        .toBounds(10000)
+        .toArray();
+
+      state.map.fitBounds([
+        rightBottomBounds.reduce((carry, next) => {
+          if (carry.length === 0) return next;
+          return [Math.max(carry[0], next[0]), Math.min(carry[1], next[1])];
+        }, []),
+        leftTopBounds.reduce((carry, next) => {
+          if (carry.length === 0) return next;
+          return [Math.min(carry[0], next[0]), Math.max(carry[1], next[1])];
+        }, []),
+      ]);
+    };
+    const handleRemoveMarkers = (
+      type: MarkerMapType,
+      markers: State['markers'],
+    ) => {
+      const locations = markers.map((i) => i.location);
+      state.markers.forEach((m) => {
+        if (locations.includes(m.location)) {
+          m.marker.remove();
+          m.popup.remove();
+          m.popup = null;
+          m.marker = null;
+        }
+      });
+      state.markers = state.markers.filter(
+        (m) => !locations.includes(m.location),
+      );
+      state.positions = state.positions.filter((p) => !locations.includes(p));
+
+      handleFitBounds();
+    };
+    onMounted(() => {
+      window.minemap.util.getJSON(
+        'https://minedata.cn/service/solu/style/id/12878',
+        function (error, data) {
+          data.layers.forEach(function (layer: any) {
+            //判断是否道路线图层
+            if (
+              layer.type == 'line' &&
+              layer.source == 'Traffic' &&
+              layer['source-layer'] == 'Trafficrtic'
+            ) {
+              state.trafficLayerIds.push(layer.id);
+            }
+          });
+        },
+      );
+
+      state.map.on('load', function () {
+        updateTrafficSource();
+
+        //如果底图没有配置路况图层,需要自己手动增加
+        state.map.addLayer({
+          id: 'trafficlines',
+          type: 'line',
+          source: 'Traffic',
+          'source-layer': 'Trafficrtic',
+          layout: {
+            'line-join': 'round',
+            'line-cap': 'round',
+          },
+          paint: {
+            'line-color': {
+              property: 'status',
+              stops: [
+                [0, '#999999'],
+                [1, '#66cc00'],
+                [2, '#ff9900'],
+                [3, '#cc0000'],
+                [4, '#9d0404'],
+              ],
+            },
+            'line-width': {
+              stops: [
+                [5, 1],
+                [18, 3],
+              ],
+              base: 1.2,
+            },
+          },
+        });
+
+        state.trafficLayerIds.push('trafficlines');
+        state.trafficStatus = false;
+        updateTrafficLayerVisibility('none');
+      });
+    });
+    watch(
+      () => state.types,
+      (next) => {
+        actionTypes.value.forEach((item) => {
+          if (next.includes(item.type) && !item.hasActioned) {
+            state.hasTypes.push(item.type);
+            item.action();
+          }
+          if (!next.includes(item.type) && item.hasActioned) {
+            state.hasTypes = state.hasTypes.filter((t) => t !== item.type);
+            item.remove();
+          }
+        });
+      },
+    );
+
+    return () => (
+      <div class="task-map-container">
+        <MapView v-model:map={state.map} />
+        {!props.readonly && (
+          <div class="address-type-card">
+            <el-checkbox-group v-model={state.types}>
+              {props.adrsMapTypes &&
+                props.adrsMapTypes.map((t) => (
+                  <el-checkbox key={t} class="card-item" label={t} />
+                ))}
+            </el-checkbox-group>
+            <i class="card-border-bottom-left"></i>
+            <i class="card-border-bottom-right"></i>
+          </div>
+        )}
+      </div>
+    );
+  },
+});

+ 7 - 4
src/layout/BaseLayout/index.scss

@@ -1,8 +1,8 @@
-@import "../../styles//utils.scss";
+@import '../../styles//utils.scss';
 
 .base-layout-container {
   height: 100vh;
-  background-image: url("../../assets/bg.png");
+  background-image: url('../../assets/bg.png');
   background-repeat: no-repeat;
   background-size: cover;
   background-position: top center;
@@ -12,13 +12,16 @@
     height: 94px;
     height: px2rem(94px);
     background: transparent;
-    background-image: url("../../assets/header.png");
+    background-image: url('../../assets/header.png');
     background-repeat: no-repeat;
     background-size: contain;
+    position: fixed;
+    top: 0;
+    z-index: 1;
   }
   main {
     display: flex;
     flex-direction: column;
-    min-height: calc(100vh - px2rem(94px));
+    min-height: calc(100vh);
   }
 }

+ 7 - 6
src/layout/BaseLayout/index.tsx

@@ -1,17 +1,18 @@
-import { defineComponent } from "vue";
-import { RouterView } from "vue-router";
-/** @ts-ignore */
-import SwitchButton from "@element-plus/icons/lib/SwitchButton.js";
-// import NavBar from './NavBar';
-import "./index.scss";
+import { defineComponent } from 'vue';
+import { RouterView, useRoute, useRouter } from 'vue-router';
+import MarkerMap from '@/components/MarkerMap';
+import './index.scss';
 
 export default defineComponent({
   setup() {
+    const route = useRoute();
+
     return () => (
       <section class="base-layout-container">
         <header class="base-layout-title-contatiner"></header>
         <main>
           <RouterView />
+          <MarkerMap readonly={route.path.includes('incidentDetail')} />
         </main>
       </section>
     );

+ 1 - 1
src/main.ts

@@ -4,7 +4,7 @@ import { createPinia } from 'pinia';
 import App from './App';
 import router from './router';
 // import 'nprogress/nprogress.css';
-// import './styles/element/index.scss';
+import './styles/element/index.scss';
 import './styles/global.scss';
 import '@/utils/flexible';
 

+ 23 - 20
src/router/index.ts

@@ -1,41 +1,44 @@
-import useMainStore from "@/store/useMainStore";
+import useMainStore from '@/store/useMainStore';
 import {
   createRouter,
   createWebHashHistory,
   RouteLocation,
   RouteRecordRaw,
-} from "vue-router";
-import NProgress from "nprogress";
+} from 'vue-router';
+import NProgress from 'nprogress';
 
 const BaseLayout = () =>
-  import(/* webpackChunkName: "BaseLayout" */ "@/layout/BaseLayout");
-const ManagementLayout = () =>
-  import(
-    /* webpackChunkName: "ManagementLayout" */ "@/layout/ManagementLayout"
-  );
+  import(/* webpackChunkName: "BaseLayout" */ '@/layout/BaseLayout');
 
 const routes: RouteRecordRaw[] = [
   {
-    path: "/",
-    name: "Home",
+    path: '/',
+    name: 'Home',
     component: BaseLayout,
-    redirect: "/home",
+    redirect: '/home',
     children: [
       {
-        path: "home",
+        path: 'home',
         component: () =>
-          import(/* webpackChunkName: "home.page" */ "@/views/HomePage"),
-        name: "首页",
-        meta: { iconName: "TableOutlined" },
+          import(/* webpackChunkName: "home.page" */ '@/views/HomePage'),
+        name: '首页',
+        meta: { title: '首页' },
+      },
+      {
+        path: 'incidentDetail',
+        component: () =>
+          import(/* webpackChunkName: "home.page" */ '@/views/IncidentDetail'),
+        name: '事件详情',
+        meta: { title: '事件详情' },
       },
     ],
   },
 
   {
-    path: "/:pathMatch(.*)*",
-    name: "All",
+    path: '/:pathMatch(.*)*',
+    name: 'All',
     redirect: (to: RouteLocation) => {
-      return { path: "/404", query: { from: to.path } };
+      return { path: '/404', query: { from: to.path } };
     },
   },
 ];
@@ -57,8 +60,8 @@ router.beforeEach(() => {
 router.afterEach((to) => {
   NProgress.done();
   document!.title = to.meta.title
-    ? to?.meta?.title + " - 交通运输应急监管系统"
-    : "交通运输应急监管系统";
+    ? to?.meta?.title + ' - 交通运输应急监管系统'
+    : '交通运输应急监管系统';
 });
 
 export default router;

+ 2 - 2
src/store/index.ts

@@ -1,4 +1,4 @@
-import useMainStore from './useMainStore';
-
 export { default as useMainStore } from './useMainStore';
 export { default as useCommonStore } from './useCommonStore';
+export { default as useIncidentStore } from './useIncidentStore';
+export { default as useMarkerStore } from './useMarkerStore';

+ 99 - 0
src/store/useIncidentStore.ts

@@ -0,0 +1,99 @@
+import {
+  getIncidentList,
+  getIncidentItem,
+  GetIncidentListParams,
+  IncidentItemDetail,
+  IncidentListResponse,
+  IncidentItem,
+  putIncidentItem,
+  postIncidentItem,
+} from '@/api/incident';
+import { ElMessage } from 'element-plus';
+import { defineStore } from 'pinia';
+
+export interface IncidentStateType {
+  incidents: IncidentListResponse;
+  loading: boolean;
+  incidentDetail: IncidentItemDetail;
+}
+
+export interface IncidentActionsType {
+  toggleLoading(): void;
+  getIncidentList(params: GetIncidentListParams): void;
+  getIncidentItem(id: number | string): void;
+  putIncidentItem(params: IncidentItem): Promise<boolean | null | undefined>;
+  postIncidentItem(params: IncidentItem): Promise<boolean | null | undefined>;
+}
+
+export default defineStore<
+  'incident',
+  IncidentStateType,
+  {},
+  IncidentActionsType
+>('incident', {
+  state: () => ({
+    incidents: {
+      rows: [],
+      total: 0,
+    },
+    incidentDetail: {
+      baseInfo: {},
+      process: [],
+      task: [],
+    },
+    loading: false,
+  }),
+  actions: {
+    toggleLoading() {
+      this.loading = !this.loading;
+    },
+    async getIncidentList(params) {
+      try {
+        this.toggleLoading();
+        this.incidents = await getIncidentList(params);
+      } finally {
+        this.toggleLoading();
+      }
+    },
+    async getIncidentItem(id) {
+      try {
+        this.toggleLoading();
+        const { data } = await getIncidentItem(id);
+        this.incidentDetail = data;
+      } finally {
+        this.toggleLoading();
+      }
+    },
+
+    async postIncidentItem(params) {
+      try {
+        this.toggleLoading();
+        await postIncidentItem(params);
+        ElMessage.success({ message: '修改成功' });
+        return true;
+      } catch {
+        ElMessage.error({ message: '修改失败' });
+      } finally {
+        this.toggleLoading();
+      }
+    },
+    /** 修改事件状态 */
+    /**  */
+    async putIncidentItem(params) {
+      try {
+        this.toggleLoading();
+        await putIncidentItem(params);
+        if (params.madinDept || params.assistDept) {
+          ElMessage.success({ message: `派发成功` });
+          return true;
+        }
+        ElMessage.success({ message: '修改成功' });
+        return true;
+      } catch {
+        ElMessage.error({ message: '修改失败' });
+      } finally {
+        this.toggleLoading();
+      }
+    },
+  },
+});

+ 44 - 0
src/store/useMarkerStore.ts

@@ -0,0 +1,44 @@
+import {
+  PENDING_DISPOSAL_INCIDENT,
+  PENDING_INCIDENT,
+  EMERGENCY_VEHICLES,
+  VIDEO_SURVEILLANCE,
+  WARNING_INCIDENT,
+  EMERGENCY_TEAM,
+} from '@/components/MarkerMap/constants';
+import { defineStore } from 'pinia';
+
+export interface MarkerType {
+  location?: string;
+  marker?: any;
+  popup?: any;
+  [key: string]: any;
+}
+
+export interface MarkerStateType {
+  warningIncident: MarkerType[];
+  pendingIncident: MarkerType[];
+  pendingDisposalIncident: MarkerType[];
+  videoSurveillance: MarkerType[];
+  emergencyVehicles: MarkerType[];
+  emergencyTeam: MarkerType[];
+  emergencyWarehouse: MarkerType[];
+}
+
+export interface MainActionsType {}
+
+export default defineStore<'marker', MarkerStateType, {}, MainActionsType>(
+  'marker',
+  {
+    state: () => ({
+      warningIncident: WARNING_INCIDENT,
+      pendingIncident: PENDING_INCIDENT,
+      pendingDisposalIncident: PENDING_DISPOSAL_INCIDENT,
+      videoSurveillance: VIDEO_SURVEILLANCE,
+      emergencyVehicles: EMERGENCY_VEHICLES,
+      emergencyTeam: EMERGENCY_TEAM,
+      emergencyWarehouse: [],
+    }),
+    actions: {},
+  },
+);

+ 3 - 11
src/styles/utils.scss

@@ -1,13 +1,5 @@
-@function px2rem($px, $base-font-size: 384px) {
-  @return ($px / $base-font-size) * 1rem;
-}
+@use 'sass:math';
 
-@function px2em($px, $base-font-size: 100px) {
-  @if (unitless($px)) {
-    @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
-    @return px2em($px + 0px); // That may fail.
-  } @else if (unit($px) == em) {
-    @return $px;
-  }
-  @return ($px / $base-font-size) * 1em;
+@function px2rem($px, $base-font-size: 384px) {
+  @return math.div($px, $base-font-size) * 1rem;
 }

+ 48 - 0
src/views/HomePage/MessageCard/index.tsx

@@ -0,0 +1,48 @@
+import { IncidentListResponse } from '@/api/incident';
+import Card from '@/components/Card';
+import { useCommonStore } from '@/store';
+import useIncidentStore from '@/store/useIncidentStore';
+import { computed, defineComponent, PropType } from 'vue-demi';
+
+export default defineComponent({
+  name: 'MessageCard',
+  props: {
+    incidents: Array as PropType<IncidentListResponse>,
+  },
+  setup() {
+    const store = useIncidentStore();
+    const commonStore = useCommonStore();
+
+    return () => (
+      <Card cardType="message-list">
+        <div class="message-card-container">
+          {store.incidents.rows?.map((item, idx) => (
+            <div class="message-item">
+              <div class="title">
+                <span class="index">{idx + 1}、</span>
+                <span>
+                  {item.createTime +
+                    ',在' +
+                    item.addr +
+                    '处发生了' +
+                    (commonStore.globalDict['zhdd_incident_type']?.find(
+                      (i) => i.dictValue.toString() === `${item.type}`,
+                    )?.dictLabel ?? '预警') +
+                    '事件'}
+                </span>
+              </div>
+              <div class="desc">
+                {item.source}
+                {
+                  commonStore.globalDict['zhdd_incident_source']?.find(
+                    (i) => i.dictValue.toString() === `${item.source}`,
+                  )?.dictLabel
+                }
+              </div>
+            </div>
+          ))}
+        </div>
+      </Card>
+    );
+  },
+});

+ 33 - 2
src/views/HomePage/index.scss

@@ -1,4 +1,4 @@
-@import "../../styles/utils.scss";
+@import '../../styles/utils.scss';
 
 .home-page-container {
   flex: 1;
@@ -7,8 +7,39 @@
     width: px2rem(642px);
     height: px2rem(887px);
     position: absolute;
-    top: 0;
+    top: px2rem(38px);
     left: 50%;
     transform: translateX(-50%);
   }
+  .card-container {
+    position: absolute;
+    top: px2rem(83px);
+    &-bg-message-list {
+      left: px2rem(30px);
+      width: px2rem(528px);
+      height: px2rem(448px);
+    }
+  }
+}
+.message-card-container {
+  color: #fff;
+  overflow-y: auto;
+  .message-item {
+    padding-bottom: px2rem(30px);
+    .title {
+      font-size: px2rem(16px);
+      display: flex;
+      align-items: top;
+      padding-bottom: px2rem(13px);
+      .index {
+        width: px2rem(20px);
+        width: 20px;
+      }
+    }
+    .desc {
+      font-size: px2rem(16px);
+      padding-left: px2rem(20px);
+      padding-left: 20px;
+    }
+  }
 }

+ 18 - 130
src/views/HomePage/index.tsx

@@ -1,139 +1,27 @@
-import { ref, reactive, defineComponent } from "vue";
-import { registerMap, use } from "echarts/core";
-import { CanvasRenderer } from "echarts/renderers";
-import { MapChart, EffectScatterChart } from "echarts/charts";
-import {
-  TitleComponent,
-  TooltipComponent,
-  LegendComponent,
-} from "echarts/components";
-import VChart, { THEME_KEY } from "vue-echarts";
-import GeoJSon from "@/assets/jiang1_su1_su4_qian1";
-import "./index.scss";
-
-use([
-  CanvasRenderer,
-  MapChart,
-  TitleComponent,
-  TooltipComponent,
-  LegendComponent,
-  EffectScatterChart,
-]);
-
-registerMap("SuQian", {
-  geoJSON: GeoJSon,
-  specialAreas: {},
-});
+import { onMounted, defineComponent } from 'vue';
+import MessageCard from './MessageCard';
+import './index.scss';
+import { useCommonStore, useIncidentStore } from '@/store';
 
 export default defineComponent({
-  name: "HomePage",
-  provide: {
-    [THEME_KEY]: "dark",
-  },
+  name: 'HomePage',
+  provide: {},
   setup() {
-    const option = ref({
-      // title: {
-      //   text: "Traffic Sources",
-      //   left: "center",
-      // },
-      // tooltip: {
-      //   trigger: "item",
-      //   formatter: "{a} <br/>{b} : {c} ({d}%)",
-      // },
-      legend: {
-        orient: "vertical",
-        left: "left",
-        data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"],
-      },
-      geo: {
-        map: "SuQian",
-        aspectScale: 0.75, //长宽比
-        zoom: 1.1,
-        roam: false,
-        itemStyle: {
-          normal: {
-            areaColor: "#013C62",
-            shadowColor: "#182f68",
-            shadowOffsetX: 0,
-            shadowOffsetY: 25,
-          },
-          emphasis: {
-            areaColor: "#2AB8FF",
-            borderWidth: 0,
-            color: "green",
-            label: {
-              show: false,
-            },
-          },
-        },
-      },
-      series: [
-        {
-          type: "map",
-          map: "SuQian",
-          roam: false,
-          label: {
-            // show: false,
-            normal: {
-              show: false,
-              textStyle: {
-                color: "#fff",
-              },
-            },
-            emphasis: {
-              // show: false,
-              textStyle: {
-                color: "#fff",
-              },
-            },
-          },
-          itemStyle: {
-            normal: {
-              borderColor: "#2ab8ff",
-              borderWidth: 1.5,
-              areaColor: "#12235c",
-            },
-            emphasis: {
-              areaColor: "#12235c",
-              // borderWidth: 0,
-              color: "green",
-            },
-            select: {
-              areaColor: "#12235c",
-            },
-          },
-          zoom: 1.1,
-        },
-        {
-          type: "effectScatter",
-          coordinateSystem: "geo",
-          showEffectOn: "render",
-          rippleEffect: {
-            period: 4,
-            scale: 4,
-            brushType: "fill",
-          },
-          hoverAnimation: true,
-          itemStyle: {
-            normal: {
-              color: "#2ab8ff",
-              shadowBlur: 10,
-              shadowColor: "#333",
-            },
-          },
-          data: [
-            {
-              name: "宿州学院",
-              value: [118.296459, 33.929648],
-            },
-          ],
-        },
-      ],
-    });
+    const store = useIncidentStore();
+    const commonStore = useCommonStore()
 
+    onMounted(() => {
+      commonStore.getGlobalDict('zhdd_incident_source')
+      commonStore.getGlobalDict('zhdd_incident_type')
+      
+      store.getIncidentList({
+        // 事件等级1,2的事件
+        level: 1,
+      });
+    });
     return () => (
       <div class="home-page-container">
-        <VChart option={option.value} />
+        <MessageCard />
       </div>
     );
   },

+ 43 - 0
src/views/IncidentDetail/CommandChainCard/index.tsx

@@ -0,0 +1,43 @@
+import Card from '@/components/Card';
+import { defineComponent } from 'vue-demi';
+// @ts-ignore
+import command from '@/assets/icons/detail/command@2x.png';
+// @ts-ignore
+import co_organized from '@/assets/icons/detail/co_organized@2x.png';
+// @ts-ignore
+import host from '@/assets/icons/detail/host@2x.png';
+import { useIncidentStore } from '@/store';
+
+export default defineComponent({
+  name: 'CommandChainCard',
+  setup() {
+    const store = useIncidentStore();
+
+    return () => (
+      <Card cardType="command-chain">
+        <div class="command-chain-container">
+          <div class="chain-item">
+            <span class="title">指挥部门</span>
+            <span class="dessc">市交通运输局</span>
+            <img class="logo" src={command} alt="" />
+          </div>
+          <div class="chain-item">
+            <span class="title">协办部门</span>
+            <span class="dessc">
+              {store.incidentDetail.baseInfo?.assistDept ?? '应急安全处'}
+            </span>
+            <img class="logo" src={co_organized} alt="" />
+          </div>
+          <div class="chain-item">
+            <span class="title">主办部门</span>
+            <span class="dessc">
+              {store.incidentDetail.baseInfo?.assistDept ??
+                '市公路事业发展中心'}
+            </span>
+            <img class="logo" src={host} alt="" />
+          </div>
+        </div>
+      </Card>
+    );
+  },
+});

+ 36 - 0
src/views/IncidentDetail/EmergencyLinkageCard/index.tsx

@@ -0,0 +1,36 @@
+import Card from '@/components/Card';
+import { defineComponent } from 'vue-demi';
+// @ts-ignore
+import sjt from '@/assets/icons/detail/sjt@2x.png';
+// @ts-ignore
+import sjt_1 from '@/assets/icons/detail/sjt_1@2x.png';
+// @ts-ignore
+import rhtx from '@/assets/icons/detail/rhtx@2x.png';
+// @ts-ignore
+import dbpt from '@/assets/icons/detail/dbpt@2x.png';
+import { useIncidentStore } from '@/store';
+
+export default defineComponent({
+  name: 'EmergencyLinkage',
+  setup() {
+    const store = useIncidentStore();
+
+    return () => (
+      <Card cardType="emergency-linkage">
+        <div class="linkage-chain-container">
+          {[
+            { label: '省交通应急平台', src: sjt_1 },
+            { label: '融合通信平台', src: rhtx },
+            { label: '市交通应急平台', src: sjt },
+            { label: '单兵系统', src: dbpt },
+          ].map((item) => (
+            <div class="linkage-item">
+              <img src={item.src} alt="" />
+              <span>{item.label}</span>
+            </div>
+          ))}
+        </div>
+      </Card>
+    );
+  },
+});

+ 45 - 0
src/views/IncidentDetail/IncidentInfoCard/index.tsx

@@ -0,0 +1,45 @@
+import Card from '@/components/Card';
+import { defineComponent } from 'vue-demi';
+
+// @ts-ignore
+import IconLevel from '@/assets/icons/detail/level@2x.png';
+// @ts-ignore
+import IconSource from '@/assets/icons/detail/source@2x.png';
+// @ts-ignore
+import IconStyle from '@/assets/icons/detail/style@2x.png';
+// @ts-ignore
+import IconTitle from '@/assets/icons/detail/title@2x.png';
+import { useIncidentStore } from '@/store';
+
+const list = [
+  { label: '事件标题', icon: IconTitle, prop: 'name' as const },
+  { label: '事件类型', icon: IconStyle, prop: 'type' as const },
+  { label: '事件来源', icon: IconSource, prop: 'source' as const },
+  { label: '事件等级', icon: IconLevel, prop: 'level' as const },
+];
+
+export default defineComponent({
+  name: 'IncidentInfoCard',
+  setup(props) {
+    const store = useIncidentStore();
+
+    return () => (
+      <Card cardType="incident-info">
+        <div class="info-container">
+          {list.map((item, idx) => (
+            <div class="info-item">
+              <div class="info-item-lebel" data-idx={idx}>
+                <img src={item.icon} alt={item.label} />
+                <span>事件标题</span>
+              </div>
+              <div class="info-item-value">
+                {store.incidentDetail.baseInfo?.[item.prop] ??
+                  item.label + 'xxxx x x '}
+              </div>
+            </div>
+          ))}
+        </div>
+      </Card>
+    );
+  },
+});

+ 34 - 0
src/views/IncidentDetail/IncidentPlanCard/index.tsx

@@ -0,0 +1,34 @@
+import Card from '@/components/Card';
+import { useIncidentStore } from '@/store';
+import { defineComponent, ref } from 'vue-demi';
+
+const lists = [
+  '生成处置方法',
+  '组织应急队伍',
+  '现场救援',
+  '这里处置方案文字这里处置方案文字',
+  '这里处置方案文字这里处置方案文字',
+  '这里处置方案文字这里处置方案文字',
+  '这里处置方案文字这里处置方案文字',
+];
+
+export default defineComponent({
+  name: 'IncidentPlanCard',
+  setup(props) {
+    const store = useIncidentStore();
+
+    const types = ref([]);
+
+    return () => (
+      <Card cardType="incident-plan">
+        <div class="plan-container">
+          <el-checkbox-group v-model={types.value}>
+            {lists.map((t) => (
+              <el-checkbox disabled class="plan-item" label={t} />
+            ))}
+          </el-checkbox-group>
+        </div>
+      </Card>
+    );
+  },
+});

+ 226 - 0
src/views/IncidentDetail/index.scss

@@ -0,0 +1,226 @@
+@use 'sass:math';
+@import '../../styles/utils.scss';
+
+.incident-detail-page-container {
+  flex: 1;
+  position: relative;
+  .echarts {
+    width: px2rem(642px);
+    height: px2rem(887px);
+    position: absolute;
+    top: px2rem(38px);
+    left: 50%;
+    transform: translateX(-50%);
+  }
+  .card-container {
+    position: absolute;
+    top: px2rem(83px);
+
+    &-bg-incident-info {
+      left: px2rem(30px);
+      .info-container {
+        display: flex;
+        flex-direction: column;
+        align-items: start;
+        justify-content: center;
+      }
+      .info-item {
+        display: flex;
+        align-items: center;
+        padding-bottom: px2rem(34px);
+        line-height: px2rem(50px);
+        font-size: px2rem(18px);
+        flex: 1;
+        width: 100%;
+        .info-item-lebel {
+          padding: 0 px2rem(20px);
+          border-top-left-radius: px2rem(50px);
+          border-bottom-left-radius: px2rem(50px);
+          display: flex;
+          align-items: center;
+          img {
+            min-width: 13px;
+            min-height: 13px;
+            width: px2rem(18px);
+            height: px2rem(18px);
+          }
+          span {
+            padding-left: px2rem(13px);
+          }
+          &[data-idx='0'] {
+            background: linear-gradient(to right, #035fd8, #1acfff);
+          }
+          &[data-idx='1'] {
+            background: linear-gradient(to right, #ff8b45, #f6b35c);
+          }
+          &[data-idx='2'] {
+            background: linear-gradient(to right, #8d48e9, #d789ff);
+          }
+          &[data-idx='3'] {
+            background: linear-gradient(to right, #05b382, #48e9c2);
+          }
+        }
+        .info-item-value {
+          flex: 1;
+          padding: 0 px2rem(20px);
+          background-color: #01016b;
+          border-top-right-radius: px2rem(50px);
+          border-bottom-right-radius: px2rem(50px);
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+      }
+    }
+    &-bg-command-chain {
+      left: px2rem(521px);
+      .command-chain-container {
+        position: relative;
+        .chain-item {
+          position: absolute;
+          left: 50%;
+          transform: translateX(-50%);
+          top: px2rem(24px);
+          width: px2rem(165px);
+          height: px2rem(106px);
+          box-sizing: border-box;
+          margin-bottom: calc(px2rem(65px) - 2px);
+
+          border: 2px solid #feb459;
+          border-top-left-radius: 10px;
+          border-top-right-radius: 10px;
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          justify-content: center;
+          .title {
+            font-size: px2rem(18px);
+            color: #feb459;
+            padding-bottom: px2rem(13px);
+          }
+          .logo {
+            position: absolute;
+            width: px2rem(40px);
+            height: px2rem(40px);
+            // min-width: 20px;
+            // min-height: 20px;
+            z-index: 1;
+            bottom: px2rem(-40px);
+            left: 50%;
+            transform: translateX(-50%)
+              translateY(px2rem(math.div(65px, 2)) - px2rem(28px));
+          }
+          &::after {
+            content: '';
+            position: absolute;
+            left: -2px;
+            bottom: calc(px2rem(-65px) - 2px);
+            width: 0;
+            height: 0;
+            border-top: px2rem(65px) solid #feb459;
+            border-left: px2rem(math.div(165px, 2)) solid transparent;
+            border-right: px2rem(math.div(165px, 2)) solid transparent;
+          }
+          &:nth-child(3),
+          &:nth-child(2) {
+            bottom: px2rem(40px);
+            margin-bottom: 0;
+            top: unset;
+            left: px2rem(20px);
+            border-top-left-radius: 0;
+            border-top-right-radius: 0;
+            border-color: #33d3a8;
+            transform: unset;
+            .title {
+              color: #33d3a8;
+            }
+            .logo {
+              bottom: unset;
+              top: px2rem(-40px);
+              left: 50%;
+              transform: translateX(-50%)
+                translateY(px2rem(math.div(-65px, 2)) + px2rem(24px));
+            }
+            border-bottom-left-radius: 10px;
+            border-bottom-right-radius: 10px;
+            &::after {
+              position: absolute;
+              left: -2px;
+              bottom: unset;
+              top: calc(px2rem(-65px) - 2px);
+              border-bottom: px2rem(65px) solid #33d3a8;
+              border-top: unset;
+              border-left: px2rem(math.div(165px, 2)) solid transparent;
+              border-right: px2rem(math.div(165px, 2)) solid transparent;
+            }
+          }
+          &:nth-child(3) {
+            left: unset;
+            right: px2rem(20px);
+            border-color: #1bbbf8;
+            .title {
+              color: #1bbbf8;
+            }
+            &::after {
+              border-bottom-color: #1bbbf8;
+            }
+          }
+        }
+      }
+    }
+    &-bg-incident-plan {
+      top: px2rem(482px + 83px);
+      left: px2rem(30px);
+      .plan-container {
+        .el-checkbox__inner {
+          background-color: transparent;
+        }
+        .el-checkbox__label {
+          color: #fff;
+        }
+        .plan-item {
+          display: flex;
+          align-items: center;
+          margin-right: 0;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          & > span {
+            font-size: px2rem(18px);
+            font-weight: 400;
+          }
+        }
+      }
+    }
+    &-bg-emergency-linkage {
+      top: px2rem(482px + 83px);
+      left: px2rem(521px);
+      .linkage-chain-container {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        .linkage-item {
+          width: 50%;
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          img {
+            width: px2rem(117px);
+            height: px2rem(102px);
+          }
+          span {
+            margin-top: px2rem(17px);
+            color: #87baf8;
+          }
+        }
+      }
+    }
+    &-bg-execution-log {
+      right: px2rem(22px);
+    }
+    &-bg-live-monitoring {
+      top: px2rem(564px + 83px);
+      right: px2rem(22px);
+    }
+  }
+}

+ 31 - 0
src/views/IncidentDetail/index.tsx

@@ -0,0 +1,31 @@
+import { onMounted, reactive, defineComponent } from 'vue';
+import Card from '@/components/Card';
+import './index.scss';
+import IncidentInfoCard from './IncidentInfoCard';
+import CommandChainCard from './CommandChainCard';
+import EmergencyLinkageCard from './EmergencyLinkageCard';
+import IncidentPlanCard from './IncidentPlanCard';
+import { useRoute } from 'vue-router';
+import { useIncidentStore } from '@/store';
+
+export default defineComponent({
+  name: 'IncidentDetail',
+  provide: {},
+  setup() {
+    const store = useIncidentStore();
+    const route = useRoute();
+    onMounted(() => store.getIncidentItem(route.query.id as string));
+    return () => (
+      <div class="incident-detail-page-container">
+        <IncidentInfoCard />
+        <CommandChainCard />
+        <IncidentPlanCard />
+        <EmergencyLinkageCard />
+        <Card cardType="live-monitoring" />
+        <Card cardType="execution-log" />
+        {/* <Map /> */}
+        {/* <MarkerMap /> */}
+      </div>
+    );
+  },
+});

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff