diff --git a/package-lock.json b/package-lock.json
index 0492bc2..2b75ffe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2728,6 +2728,530 @@
         }
       }
     },
+    "@pixi/accessibility": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/accessibility/download/@pixi/accessibility-5.3.3.tgz",
+      "integrity": "sha1-t7qxfjz1619RFHHflDFVpOrfDG4=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/app": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/app/download/@pixi/app-5.3.3.tgz",
+      "integrity": "sha1-Y1fi5azB7RGLf5TBF5zvVc5u1Zw=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3"
+      }
+    },
+    "@pixi/constants": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/constants/download/@pixi/constants-5.3.3.tgz",
+      "integrity": "sha1-+q7S0M42TWf+Pmmsl+nbH2rWwEE="
+    },
+    "@pixi/core": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/core/download/@pixi/core-5.3.3.tgz",
+      "integrity": "sha1-S5c+49GPYyTWMxHooApo7LGZZTI=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/runner": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/ticker": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/display": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/display/download/@pixi/display-5.3.3.tgz",
+      "integrity": "sha1-FGRrNbgLhYYxa+NJXjwOf6YQ9Jk=",
+      "requires": {
+        "@pixi/math": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/extract": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/extract/download/@pixi/extract-5.3.3.tgz",
+      "integrity": "sha1-Wrjil3gj0Op12wA+RdbG1yvCtkI=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/filter-adjustment": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-adjustment/download/@pixi/filter-adjustment-3.1.1.tgz",
+      "integrity": "sha1-KqQQSBBvogBmSN6uWYE7+1XI/lQ="
+    },
+    "@pixi/filter-advanced-bloom": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-advanced-bloom/download/@pixi/filter-advanced-bloom-3.1.1.tgz",
+      "integrity": "sha1-5FuPqL0XqY5iQvqKlsGZSEqCyiI=",
+      "requires": {
+        "@pixi/filter-kawase-blur": "3.1.1"
+      }
+    },
+    "@pixi/filter-alpha": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-alpha/download/@pixi/filter-alpha-5.3.3.tgz",
+      "integrity": "sha1-LT4Q6PQveHpRFegbEyZYObIWJ5c=",
+      "requires": {
+        "@pixi/core": "5.3.3"
+      }
+    },
+    "@pixi/filter-ascii": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-ascii/download/@pixi/filter-ascii-3.1.1.tgz",
+      "integrity": "sha1-7cyOX9CLcplvhI0Yr0dh9Km5J2c="
+    },
+    "@pixi/filter-bevel": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-bevel/download/@pixi/filter-bevel-3.1.1.tgz",
+      "integrity": "sha1-3bc4oxh2hUtON8Pl2p1iLVegPas="
+    },
+    "@pixi/filter-bloom": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-bloom/download/@pixi/filter-bloom-3.1.1.tgz",
+      "integrity": "sha1-TAdwHnk47xtDV3qhSsOkiYML6wo=",
+      "requires": {
+        "@pixi/filter-alpha": "^5.0.0",
+        "@pixi/filter-blur": "^5.0.0"
+      }
+    },
+    "@pixi/filter-blur": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-blur/download/@pixi/filter-blur-5.3.3.tgz",
+      "integrity": "sha1-xTDkADjewXJaOZdTrJf6o0GFWc8=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/settings": "5.3.3"
+      }
+    },
+    "@pixi/filter-bulge-pinch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-bulge-pinch/download/@pixi/filter-bulge-pinch-3.1.1.tgz",
+      "integrity": "sha1-zSpE6YAOdBAcAcntnIhOiQ1Swi8="
+    },
+    "@pixi/filter-color-map": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-color-map/download/@pixi/filter-color-map-3.1.1.tgz",
+      "integrity": "sha1-ZpHE3sDhQkXxpSkE5PRiLvC2j78="
+    },
+    "@pixi/filter-color-matrix": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-color-matrix/download/@pixi/filter-color-matrix-5.3.3.tgz",
+      "integrity": "sha1-wez4OkT2jXi1Q2uSC0WcUiLzc6U=",
+      "requires": {
+        "@pixi/core": "5.3.3"
+      }
+    },
+    "@pixi/filter-color-overlay": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-color-overlay/download/@pixi/filter-color-overlay-3.1.1.tgz",
+      "integrity": "sha1-S/ss9Zq/jhkTohYhpc3juDmXLhs="
+    },
+    "@pixi/filter-color-replace": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-color-replace/download/@pixi/filter-color-replace-3.1.1.tgz",
+      "integrity": "sha1-ZsDd/5/FSOJO9xo6UrqqIHQ0aVw="
+    },
+    "@pixi/filter-convolution": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-convolution/download/@pixi/filter-convolution-3.1.1.tgz",
+      "integrity": "sha1-+AE3rtHgjisn0u9kHeaEfhVWQqk="
+    },
+    "@pixi/filter-cross-hatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-cross-hatch/download/@pixi/filter-cross-hatch-3.1.1.tgz",
+      "integrity": "sha1-cuhH5t5pfGTh31uqquh53BAFRgk="
+    },
+    "@pixi/filter-crt": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-crt/download/@pixi/filter-crt-3.1.1.tgz",
+      "integrity": "sha1-P9dpqFPUOrMi3KEiJdiF+dvCrN4="
+    },
+    "@pixi/filter-displacement": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-displacement/download/@pixi/filter-displacement-5.3.3.tgz",
+      "integrity": "sha1-8lGT9zi5DMdc0Eu7zQrv6eoDevE=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/math": "5.3.3"
+      }
+    },
+    "@pixi/filter-dot": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-dot/download/@pixi/filter-dot-3.1.1.tgz",
+      "integrity": "sha1-hASRWOf8hhkrOi+9o1L45+Mfnqk="
+    },
+    "@pixi/filter-drop-shadow": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-drop-shadow/download/@pixi/filter-drop-shadow-3.1.1.tgz",
+      "integrity": "sha1-r0MTuk8/msmXAsQ4gzPG+/XHSiw=",
+      "requires": {
+        "@pixi/filter-kawase-blur": "3.1.1"
+      }
+    },
+    "@pixi/filter-emboss": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-emboss/download/@pixi/filter-emboss-3.1.1.tgz",
+      "integrity": "sha1-NlfDoagFDcaT2RQDPt1VTvHFfZk="
+    },
+    "@pixi/filter-fxaa": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-fxaa/download/@pixi/filter-fxaa-5.3.3.tgz",
+      "integrity": "sha1-x3AWMdYPSFtuwQUvca+wY3yl8Lg=",
+      "requires": {
+        "@pixi/core": "5.3.3"
+      }
+    },
+    "@pixi/filter-glitch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-glitch/download/@pixi/filter-glitch-3.1.1.tgz",
+      "integrity": "sha1-ldlzQ56eQYFJpbF+/6+X1D/vLlQ="
+    },
+    "@pixi/filter-glow": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-glow/download/@pixi/filter-glow-3.1.1.tgz",
+      "integrity": "sha1-yMQ0h8uoiW68JwhEpcag9PKV6H4=",
+      "requires": {
+        "@pixi/core": "^5.0.0",
+        "@pixi/utils": "^5.0.0"
+      }
+    },
+    "@pixi/filter-godray": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-godray/download/@pixi/filter-godray-3.1.1.tgz",
+      "integrity": "sha1-Tp9oysvEwzC3cn5h8dEIXVkz1Ig="
+    },
+    "@pixi/filter-kawase-blur": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-kawase-blur/download/@pixi/filter-kawase-blur-3.1.1.tgz",
+      "integrity": "sha1-nkGfF78X9G3qMjC6v/mwRJQ17hE="
+    },
+    "@pixi/filter-motion-blur": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-motion-blur/download/@pixi/filter-motion-blur-3.1.1.tgz",
+      "integrity": "sha1-VHJzisFo+Eui3G649LMpxP7o7JY="
+    },
+    "@pixi/filter-multi-color-replace": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-multi-color-replace/download/@pixi/filter-multi-color-replace-3.1.1.tgz",
+      "integrity": "sha1-yBNruGZYUKErxAy3qk3sFdENnME="
+    },
+    "@pixi/filter-noise": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-noise/download/@pixi/filter-noise-5.3.3.tgz",
+      "integrity": "sha1-XYIdn4P5fYPUvlLz7Mfi0G/xwIQ=",
+      "requires": {
+        "@pixi/core": "5.3.3"
+      }
+    },
+    "@pixi/filter-old-film": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-old-film/download/@pixi/filter-old-film-3.1.1.tgz",
+      "integrity": "sha1-iNa4o7pz9H7jit1cioTdRy0SE6A="
+    },
+    "@pixi/filter-outline": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-outline/download/@pixi/filter-outline-3.1.1.tgz",
+      "integrity": "sha1-4UYUMT5SrhraLal87qMiXulCn0E="
+    },
+    "@pixi/filter-pixelate": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-pixelate/download/@pixi/filter-pixelate-3.1.1.tgz",
+      "integrity": "sha1-iXYXH3mtRSY3ZRzVM0tKYIMj/fQ="
+    },
+    "@pixi/filter-radial-blur": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-radial-blur/download/@pixi/filter-radial-blur-3.1.1.tgz",
+      "integrity": "sha1-08bXdYP9UA8QJ0rfelui+8l5Ko4="
+    },
+    "@pixi/filter-reflection": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-reflection/download/@pixi/filter-reflection-3.1.1.tgz",
+      "integrity": "sha1-Nw/iR6iYEmvAjkKPaVRrhbF0qsQ="
+    },
+    "@pixi/filter-rgb-split": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-rgb-split/download/@pixi/filter-rgb-split-3.1.1.tgz",
+      "integrity": "sha1-C50fvn4ea47CRzP5tUcaZ049hnk="
+    },
+    "@pixi/filter-shockwave": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-shockwave/download/@pixi/filter-shockwave-3.1.1.tgz",
+      "integrity": "sha1-dX5/fVbd8bH2X/iy7uH6nMTHfsU="
+    },
+    "@pixi/filter-simple-lightmap": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-simple-lightmap/download/@pixi/filter-simple-lightmap-3.1.1.tgz",
+      "integrity": "sha1-T+Rjxo3dptW4SrXKYPaGTXkTPVc="
+    },
+    "@pixi/filter-tilt-shift": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-tilt-shift/download/@pixi/filter-tilt-shift-3.1.1.tgz",
+      "integrity": "sha1-J/0XVhjqzZWRgbOV1sRYFYYQIcg="
+    },
+    "@pixi/filter-twist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-twist/download/@pixi/filter-twist-3.1.1.tgz",
+      "integrity": "sha1-9ccHL4TstI+8h+yitDz7aUB0p6I="
+    },
+    "@pixi/filter-zoom-blur": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/@pixi/filter-zoom-blur/download/@pixi/filter-zoom-blur-3.1.1.tgz",
+      "integrity": "sha1-D6tdHAVi698VJKRUx41/EpvMjmQ="
+    },
+    "@pixi/graphics": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/graphics/download/@pixi/graphics-5.3.3.tgz",
+      "integrity": "sha1-z69aCpSoEfc1nCCHVUfBQJXx7Ow=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/sprite": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/interaction": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/interaction/download/@pixi/interaction-5.3.3.tgz",
+      "integrity": "sha1-BzSOfSW45nRz7VT2eevoSrnuBAA=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/ticker": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/loaders": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/loaders/download/@pixi/loaders-5.3.3.tgz",
+      "integrity": "sha1-1BXyX5r2TZeBDkWcqiwKyktqG3w=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/utils": "5.3.3",
+        "resource-loader": "^3.0.1"
+      }
+    },
+    "@pixi/math": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/math/download/@pixi/math-5.3.3.tgz",
+      "integrity": "sha1-XUDTb6FwHhlQg624S93y9kIML0w="
+    },
+    "@pixi/mesh": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/mesh/download/@pixi/mesh-5.3.3.tgz",
+      "integrity": "sha1-8K3wNiwY5udka3q6zOxH0wTLtAU=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/mesh-extras": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/mesh-extras/download/@pixi/mesh-extras-5.3.3.tgz",
+      "integrity": "sha1-mccS/bGwqdtm/ZWnbeJjYacFWrQ=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/mesh": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/mixin-cache-as-bitmap": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/mixin-cache-as-bitmap/download/@pixi/mixin-cache-as-bitmap-5.3.3.tgz",
+      "integrity": "sha1-ysai7PO3L7rlirNleZg2Ddvac4I=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/sprite": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/mixin-get-child-by-name": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/mixin-get-child-by-name/download/@pixi/mixin-get-child-by-name-5.3.3.tgz",
+      "integrity": "sha1-go3Jp76uYDZI6+LMtnUXxxN7/xk=",
+      "requires": {
+        "@pixi/display": "5.3.3"
+      }
+    },
+    "@pixi/mixin-get-global-position": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/mixin-get-global-position/download/@pixi/mixin-get-global-position-5.3.3.tgz",
+      "integrity": "sha1-VwCwN5Tlsh9hwBWu2nM8PLYl/HU=",
+      "requires": {
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3"
+      }
+    },
+    "@pixi/particles": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/particles/download/@pixi/particles-5.3.3.tgz",
+      "integrity": "sha1-Pp0tMX1s0Ro3NoMN+9TMDDoQgsg=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/polyfill": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/polyfill/download/@pixi/polyfill-5.3.3.tgz",
+      "integrity": "sha1-TQBQsLt1p7UYQfe/7EwpJDpgW+c=",
+      "requires": {
+        "es6-promise-polyfill": "^1.2.0",
+        "object-assign": "^4.1.1"
+      }
+    },
+    "@pixi/prepare": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/prepare/download/@pixi/prepare-5.3.3.tgz",
+      "integrity": "sha1-o0Zuz1JWpcP7m4alVdsXzHLVTIc=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/graphics": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/text": "5.3.3",
+        "@pixi/ticker": "5.3.3"
+      }
+    },
+    "@pixi/runner": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/runner/download/@pixi/runner-5.3.3.tgz",
+      "integrity": "sha1-efs1sSYg13JMZfSnqlBxkOqCWsA="
+    },
+    "@pixi/settings": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/settings/download/@pixi/settings-5.3.3.tgz",
+      "integrity": "sha1-P/X4r8g3bRLHYnvgQ+wxfroTnc0=",
+      "requires": {
+        "ismobilejs": "^1.1.0"
+      }
+    },
+    "@pixi/sprite": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/sprite/download/@pixi/sprite-5.3.3.tgz",
+      "integrity": "sha1-FoHV/QpyVYG/7jycLEkFN7+NIeo=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/sprite-animated": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/sprite-animated/download/@pixi/sprite-animated-5.3.3.tgz",
+      "integrity": "sha1-8klJrgSu/5/0TiJUS8i38zbVIJ4=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/sprite": "5.3.3",
+        "@pixi/ticker": "5.3.3"
+      }
+    },
+    "@pixi/sprite-tiling": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/sprite-tiling/download/@pixi/sprite-tiling-5.3.3.tgz",
+      "integrity": "sha1-1zBiVre/bxPBgepKLZWQX1rmm50=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/sprite": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/spritesheet": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/spritesheet/download/@pixi/spritesheet-5.3.3.tgz",
+      "integrity": "sha1-4wdADQr+Sqbh2NdWpRnjkXBrXzU=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/loaders": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/text": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/text/download/@pixi/text-5.3.3.tgz",
+      "integrity": "sha1-1vwAxSvAVEUK5D4tXG987c7p7NI=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/sprite": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/text-bitmap": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/text-bitmap/download/@pixi/text-bitmap-5.3.3.tgz",
+      "integrity": "sha1-DWWEc9bgLOWY93nCB8QjM3QeFb0=",
+      "requires": {
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/loaders": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/mesh": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/text": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
+    "@pixi/ticker": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/ticker/download/@pixi/ticker-5.3.3.tgz",
+      "integrity": "sha1-qHZthBeHn//XUHF13oaYBa7iXrI=",
+      "requires": {
+        "@pixi/settings": "5.3.3"
+      }
+    },
+    "@pixi/utils": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/@pixi/utils/download/@pixi/utils-5.3.3.tgz",
+      "integrity": "sha1-UlMh87sA4+AB40ECCj7e6UzA0Ao=",
+      "requires": {
+        "@pixi/constants": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "earcut": "^2.1.5",
+        "eventemitter3": "^3.1.0",
+        "url": "^0.11.0"
+      },
+      "dependencies": {
+        "eventemitter3": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npm.taobao.org/eventemitter3/download/eventemitter3-3.1.2.tgz",
+          "integrity": "sha1-LT1I+cNGaY/Og6hdfWZOmFNd9uc="
+        }
+      }
+    },
     "@protobufjs/aspromise": {
       "version": "1.1.2",
       "resolved": "https://registry.npm.taobao.org/@protobufjs/aspromise/download/@protobufjs/aspromise-1.1.2.tgz",
@@ -5315,6 +5839,11 @@
         "https-proxy-agent": "^2.2.1"
       }
     },
+    "bson-objectid": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npm.taobao.org/bson-objectid/download/bson-objectid-1.3.1.tgz",
+      "integrity": "sha1-EeTOTDQZFh/TiBE3gbtiwd+840s="
+    },
     "buffer": {
       "version": "4.9.2",
       "resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.2.tgz?cache=0&sync_timestamp=1573257749794&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-4.9.2.tgz",
@@ -6912,6 +7441,14 @@
       "resolved": "https://registry.npm.taobao.org/dom-storage/download/dom-storage-2.1.0.tgz",
       "integrity": "sha1-APuGi8kgE1fqJDx7z9MwTB406jk="
     },
+    "dom7": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npm.taobao.org/dom7/download/dom7-2.1.5.tgz?cache=0&sync_timestamp=1604920902564&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdom7%2Fdownload%2Fdom7-2.1.5.tgz",
+      "integrity": "sha1-p5QRAXgAsx2EAAcM2uu/ySwfY3c=",
+      "requires": {
+        "ssr-window": "^2.0.0"
+      }
+    },
     "domain-browser": {
       "version": "1.2.0",
       "resolved": "https://registry.npm.taobao.org/domain-browser/download/domain-browser-1.2.0.tgz",
@@ -7247,6 +7784,11 @@
       "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=",
       "dev": true
     },
+    "es6-promise-polyfill": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npm.taobao.org/es6-promise-polyfill/download/es6-promise-polyfill-1.2.0.tgz",
+      "integrity": "sha1-84kl8jyz4+jObNqP93T867sJDN4="
+    },
     "es6-promisify": {
       "version": "5.0.0",
       "resolved": "https://registry.npm.taobao.org/es6-promisify/download/es6-promisify-5.0.0.tgz",
@@ -9495,6 +10037,11 @@
       "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
       "dev": true
     },
+    "ismobilejs": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npm.taobao.org/ismobilejs/download/ismobilejs-1.1.1.tgz",
+      "integrity": "sha1-xWygro5Sskyg8iul7zIVot27qg4="
+    },
     "isobject": {
       "version": "3.0.1",
       "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-3.0.1.tgz",
@@ -9975,8 +10522,7 @@
             "ansi-regex": {
               "version": "2.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "aproba": {
               "version": "1.2.0",
@@ -10185,14 +10731,12 @@
             "minimist": {
               "version": "0.0.8",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "minipass": {
               "version": "2.9.0",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.2",
                 "yallist": "^3.0.0"
@@ -10211,7 +10755,6 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -10400,8 +10943,7 @@
             "safe-buffer": {
               "version": "5.1.2",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "safer-buffer": {
               "version": "2.1.2",
@@ -10457,7 +10999,6 @@
               "version": "3.0.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "ansi-regex": "^2.0.0"
               }
@@ -10501,14 +11042,12 @@
             "wrappy": {
               "version": "1.0.2",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "yallist": {
               "version": "3.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             }
           }
         },
@@ -11182,6 +11721,11 @@
         }
       }
     },
+    "mini-signals": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npm.taobao.org/mini-signals/download/mini-signals-1.2.0.tgz",
+      "integrity": "sha1-RbCAE8X65RokqhqTXNMXye1yHXQ="
+    },
     "minimalistic-assert": {
       "version": "1.0.1",
       "resolved": "https://registry.npm.taobao.org/minimalistic-assert/download/minimalistic-assert-1.0.1.tgz",
@@ -12207,6 +12751,11 @@
         "json-parse-better-errors": "^1.0.1"
       }
     },
+    "parse-uri": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npm.taobao.org/parse-uri/download/parse-uri-1.0.3.tgz",
+      "integrity": "sha1-88JKdJB6TjV8F0HpbKn6rez9bbU="
+    },
     "parse5": {
       "version": "4.0.0",
       "resolved": "https://registry.npm.taobao.org/parse5/download/parse5-4.0.0.tgz?cache=0&sync_timestamp=1573036762880&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse5%2Fdownload%2Fparse5-4.0.0.tgz",
@@ -12359,6 +12908,86 @@
         "pinkie": "^2.0.0"
       }
     },
+    "pixi-filters": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/pixi-filters/download/pixi-filters-3.1.1.tgz",
+      "integrity": "sha1-mvXDcCjze+MYLnyf65WZYLwBvj8=",
+      "requires": {
+        "@pixi/filter-adjustment": "3.1.1",
+        "@pixi/filter-advanced-bloom": "3.1.1",
+        "@pixi/filter-ascii": "3.1.1",
+        "@pixi/filter-bevel": "3.1.1",
+        "@pixi/filter-bloom": "3.1.1",
+        "@pixi/filter-bulge-pinch": "3.1.1",
+        "@pixi/filter-color-map": "3.1.1",
+        "@pixi/filter-color-overlay": "3.1.1",
+        "@pixi/filter-color-replace": "3.1.1",
+        "@pixi/filter-convolution": "3.1.1",
+        "@pixi/filter-cross-hatch": "3.1.1",
+        "@pixi/filter-crt": "3.1.1",
+        "@pixi/filter-dot": "3.1.1",
+        "@pixi/filter-drop-shadow": "3.1.1",
+        "@pixi/filter-emboss": "3.1.1",
+        "@pixi/filter-glitch": "3.1.1",
+        "@pixi/filter-glow": "3.1.1",
+        "@pixi/filter-godray": "3.1.1",
+        "@pixi/filter-kawase-blur": "3.1.1",
+        "@pixi/filter-motion-blur": "3.1.1",
+        "@pixi/filter-multi-color-replace": "3.1.1",
+        "@pixi/filter-old-film": "3.1.1",
+        "@pixi/filter-outline": "3.1.1",
+        "@pixi/filter-pixelate": "3.1.1",
+        "@pixi/filter-radial-blur": "3.1.1",
+        "@pixi/filter-reflection": "3.1.1",
+        "@pixi/filter-rgb-split": "3.1.1",
+        "@pixi/filter-shockwave": "3.1.1",
+        "@pixi/filter-simple-lightmap": "3.1.1",
+        "@pixi/filter-tilt-shift": "3.1.1",
+        "@pixi/filter-twist": "3.1.1",
+        "@pixi/filter-zoom-blur": "3.1.1"
+      }
+    },
+    "pixi.js": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npm.taobao.org/pixi.js/download/pixi.js-5.3.3.tgz",
+      "integrity": "sha1-bjJqUlQvSs2X6j+Fk8sK6uUC35o=",
+      "requires": {
+        "@pixi/accessibility": "5.3.3",
+        "@pixi/app": "5.3.3",
+        "@pixi/constants": "5.3.3",
+        "@pixi/core": "5.3.3",
+        "@pixi/display": "5.3.3",
+        "@pixi/extract": "5.3.3",
+        "@pixi/filter-alpha": "5.3.3",
+        "@pixi/filter-blur": "5.3.3",
+        "@pixi/filter-color-matrix": "5.3.3",
+        "@pixi/filter-displacement": "5.3.3",
+        "@pixi/filter-fxaa": "5.3.3",
+        "@pixi/filter-noise": "5.3.3",
+        "@pixi/graphics": "5.3.3",
+        "@pixi/interaction": "5.3.3",
+        "@pixi/loaders": "5.3.3",
+        "@pixi/math": "5.3.3",
+        "@pixi/mesh": "5.3.3",
+        "@pixi/mesh-extras": "5.3.3",
+        "@pixi/mixin-cache-as-bitmap": "5.3.3",
+        "@pixi/mixin-get-child-by-name": "5.3.3",
+        "@pixi/mixin-get-global-position": "5.3.3",
+        "@pixi/particles": "5.3.3",
+        "@pixi/polyfill": "5.3.3",
+        "@pixi/prepare": "5.3.3",
+        "@pixi/runner": "5.3.3",
+        "@pixi/settings": "5.3.3",
+        "@pixi/sprite": "5.3.3",
+        "@pixi/sprite-animated": "5.3.3",
+        "@pixi/sprite-tiling": "5.3.3",
+        "@pixi/spritesheet": "5.3.3",
+        "@pixi/text": "5.3.3",
+        "@pixi/text-bitmap": "5.3.3",
+        "@pixi/ticker": "5.3.3",
+        "@pixi/utils": "5.3.3"
+      }
+    },
     "pkg-dir": {
       "version": "3.0.0",
       "resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-3.0.0.tgz",
@@ -13333,8 +13962,7 @@
     "querystring": {
       "version": "0.2.0",
       "resolved": "https://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz",
-      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
-      "dev": true
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
     },
     "querystring-es3": {
       "version": "0.2.1",
@@ -13696,6 +14324,15 @@
       "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
       "dev": true
     },
+    "resource-loader": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npm.taobao.org/resource-loader/download/resource-loader-3.0.1.tgz",
+      "integrity": "sha1-MzVbtUIeKZT1lFS7x/bb/43wbUc=",
+      "requires": {
+        "mini-signals": "^1.2.0",
+        "parse-uri": "^1.0.0"
+      }
+    },
     "restore-cursor": {
       "version": "3.1.0",
       "resolved": "https://registry.npm.taobao.org/restore-cursor/download/restore-cursor-3.1.0.tgz",
@@ -14732,6 +15369,11 @@
         "tweetnacl": "~0.14.0"
       }
     },
+    "ssr-window": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npm.taobao.org/ssr-window/download/ssr-window-2.0.0.tgz?cache=0&sync_timestamp=1604919709590&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fssr-window%2Fdownload%2Fssr-window-2.0.0.tgz",
+      "integrity": "sha1-mMMBrvmVIzF/jWlhjwAQeRCW78Q="
+    },
     "ssri": {
       "version": "7.1.0",
       "resolved": "https://registry.npm.taobao.org/ssri/download/ssri-7.1.0.tgz?cache=0&sync_timestamp=1571961490394&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fssri%2Fdownload%2Fssri-7.1.0.tgz",
@@ -15038,6 +15680,15 @@
         "util.promisify": "~1.0.0"
       }
     },
+    "swiper": {
+      "version": "5.4.5",
+      "resolved": "https://registry.npm.taobao.org/swiper/download/swiper-5.4.5.tgz?cache=0&sync_timestamp=1607518868843&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fswiper%2Fdownload%2Fswiper-5.4.5.tgz",
+      "integrity": "sha1-o1D2VL9oQm27ZReTgkklUS0iPA8=",
+      "requires": {
+        "dom7": "^2.1.5",
+        "ssr-window": "^2.0.0"
+      }
+    },
     "symbol-observable": {
       "version": "1.2.0",
       "resolved": "https://registry.npm.taobao.org/symbol-observable/download/symbol-observable-1.2.0.tgz",
@@ -15724,7 +16375,6 @@
       "version": "0.11.0",
       "resolved": "https://registry.npm.taobao.org/url/download/url-0.11.0.tgz",
       "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
-      "dev": true,
       "requires": {
         "punycode": "1.3.2",
         "querystring": "0.2.0"
@@ -15733,8 +16383,7 @@
         "punycode": {
           "version": "1.3.2",
           "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz",
-          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
-          "dev": true
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
         }
       }
     },
@@ -15901,6 +16550,11 @@
         "extsprintf": "^1.2.0"
       }
     },
+    "viewerjs": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npm.taobao.org/viewerjs/download/viewerjs-1.9.0.tgz",
+      "integrity": "sha1-bfr1REDDsvdpG4Vma6bSdwjtlZI="
+    },
     "vm-browserify": {
       "version": "1.1.2",
       "resolved": "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz",
@@ -16236,8 +16890,7 @@
             "minimist": {
               "version": "0.0.8",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "minipass": {
               "version": "2.9.0",
@@ -16350,8 +17003,7 @@
             "number-is-nan": {
               "version": "1.0.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "object-assign": {
               "version": "4.1.1",
@@ -16933,8 +17585,7 @@
             "ansi-regex": {
               "version": "2.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "aproba": {
               "version": "1.2.0",
@@ -16989,7 +17640,8 @@
             "console-control-strings": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "core-util-is": {
               "version": "1.0.2",
@@ -17106,7 +17758,8 @@
             "inherits": {
               "version": "2.0.4",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "ini": {
               "version": "1.3.5",
@@ -17148,6 +17801,7 @@
               "version": "2.9.0",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.2",
                 "yallist": "^3.0.0"
@@ -17166,6 +17820,7 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -17268,6 +17923,7 @@
               "version": "1.4.0",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "wrappy": "1"
               }
@@ -17389,6 +18045,7 @@
               "version": "1.0.2",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "code-point-at": "^1.0.0",
                 "is-fullwidth-code-point": "^1.0.0",
@@ -17408,7 +18065,6 @@
               "version": "3.0.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "ansi-regex": "^2.0.0"
               }
diff --git a/package.json b/package.json
index a5c6b81..99030e3 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,11 @@
     "ngx-perfect-scrollbar": "^8.0.0",
     "rxjs": "~6.5.4",
     "tslib": "^1.10.0",
+    "viewerjs": "^1.6.2",
+    "swiper": "^5.3.7",
+    "pixi-filters": "^3.1.1",
+    "pixi.js": "^5.3.2",
+    "bson-objectid": "^1.3.1",
     "zone.js": "~0.10.2"
   },
   "devDependencies": {
diff --git a/src/app/canvas-share-data.service.ts b/src/app/canvas-share-data.service.ts
new file mode 100644
index 0000000..6c915cf
--- /dev/null
+++ b/src/app/canvas-share-data.service.ts
@@ -0,0 +1,732 @@
+import { Injectable } from '@angular/core';
+import {ReplaySubject} from 'rxjs';
+import { Observable } from 'rxjs';
+import { GameMode } from './working-area/model/gameMode';
+@Injectable({
+  providedIn: 'root'
+})
+export class CanvasShareDataService {
+  constructor() { }
+  private _sendMessage: ReplaySubject<any> = new ReplaySubject<any>(1);
+  GameMode: any;
+
+  isChange = false; // 数据 是否改动
+
+  selectTemplateData: any; // 选择当前 模板数据
+
+  // 总平面图/建筑 楼层
+  selectStorey: any = {area: '', details: ''}; // 选择当前 楼层 数据
+  originalcompanyBuildingData: any; // 单位/建筑 数据
+  originaleveryStoreyData: any; // 总平面图/楼层/区域 楼层数据
+  // 总平面图/建筑 楼层
+
+  // 处置 节点
+  allDisposalNode: any = []; // 所有 处置节点
+  allNodeMarkers: any; // 灾情 标签信息
+  selectPanelPoint: DisposalNodeData = new DisposalNodeData();
+  selectPanelPointBaseData: any = {description: '', notes: '', weather: '', airTemperature: '', windDirection: '', windScale: ''}; // 当前 数据节点 所对应的 天气,详情 数据节点
+  // 处置 节点
+  /**
+  * 游戏模式
+  */
+  gameMode: GameMode = GameMode.BasicInformation;
+
+  facilityAssetsName = new Map<string, string>([
+    [ '消防水池', '消防水池'],
+    [ '疏散楼梯', '疏散楼梯'],
+    [ '消防电梯', '消防电梯'],
+    [ '避难区域', '避难区域'],
+    [ '安全出口', '安全出口'],
+    [ '地上消火栓', '室外消火栓' ],
+    [ '地下消火栓', '室外消火栓' ],
+    [ '室内消火栓', '室内消火栓' ],
+    [ '供水管网', '供水管网'],
+    [ '湿式自动喷淋系统', '湿式自动喷淋系统'],
+    [ '水幕系统', '水幕系统' ],
+    [ '消防泵房', '消防泵房'],
+    [ '水泵接合器(地上)', '水泵接合器'],
+    [ '水泵接合器(地下)', '水泵接合器'],
+    [ '水泵接合器(墙壁)', '水泵接合器'],
+    [ '消防水泵房', '消防水泵房'],
+    [ '箱式消火栓', '箱式消火栓'],
+    [ '固定水炮', '消防水炮' ],
+    [ '消防水罐', '储水罐'],
+    [ '消防水罐2', '储水罐'],
+    [ '卧式水罐', '储水罐'],
+    [ '消防泵', '水泵' ],
+    [ '泡沫泵', '水泵' ],
+    [ '泡沫泵房', '泡沫站'],
+    [ '泡沫栓', '泡沫栓' ],
+    [ '泡沫枪', '泡沫枪'],
+    [ '泡沫发生器', '泡沫发生器' ],
+    [ '消防管网', '消防管网'],
+    [ 'DCS控制室', 'DCS控制室']
+  ]);
+
+  /** * 向其他组件发送信息 *
+  * @param message 需要发送的信息 * @returns {Observavle<any>} */
+  public sendMessage(message: any) {
+    this._sendMessage.next(message);
+  }
+  public getMessage(): Observable <any> {
+    return this._sendMessage.asObservable();
+  }
+
+  // 处置节点 筛选出 匹配数据 匹配不到 return undefined
+  findDisposalNode(parentId: string= null, name: string= null) {
+    if (parentId && name) { // 匹配 父id, name
+      const returnData = this.allDisposalNode.find(item => item.parentId === parentId && item.name === name);
+      return returnData;
+    } else { // 匹配 id
+      const returnData = this.allDisposalNode.find(item => item.id === parentId);
+      return returnData;
+    }
+  }
+  /**
+   * 获取单位毗邻信息
+   */
+  public getCompanyAdjoinInfo(): CompanyAdjoinInfo[] {
+    const list: CompanyAdjoinInfo[] = [];
+    Object.keys(this.originalcompanyBuildingData.data).forEach((key) => {
+      const item = this.originalcompanyBuildingData.data[key];
+      if (item.Name === '毗邻') {
+        const adjoin = new CompanyAdjoinInfo();
+        adjoin.AssetId = item.Id;
+        adjoin.Id = '';
+        adjoin.ImageUrls = [];
+        adjoin.CompanyId = sessionStorage.getItem('companyId');
+        item.PropertyInfos.forEach(element => {
+          if (element.PropertyName === '方向') {
+            adjoin.Direction = Number(element.PropertyValue);
+          } else if (element.PropertyName === '名称/编号') {
+            adjoin.Name = element.PropertyValue;
+          } else if (element.PropertyType === PropertyType.Image) {
+            adjoin.ImageUrls.push(element.PropertyValue);
+          }
+        });
+        list.push(adjoin);
+      }
+    });
+    return list;
+  }
+  /**
+   * 获取建筑毗邻信息
+   */
+  public getBuildingAdjoinInfo(): BuildingAdjoinInfo[] {
+    const list: BuildingAdjoinInfo[] = [];
+    Object.keys(this.originalcompanyBuildingData.data).forEach((key) => {
+      const item = this.originalcompanyBuildingData.data[key];
+      if (item.Name === '毗邻') {
+        const adjoin = new BuildingAdjoinInfo();
+        adjoin.AssetId = item.Id;
+        adjoin.Id = '';
+        adjoin.BuildingId = this.selectStorey.buildingId;
+        adjoin.ImageUrls = [];
+        item.PropertyInfos.forEach(element => {
+          if (element.PropertyName === '方向') {
+            adjoin.Direction = Number(element.PropertyValue);
+          } else if (element.PropertyName === '名称/编号') {
+            adjoin.Name = element.PropertyValue;
+          } else if (element.PropertyType === PropertyType.Image) {
+            adjoin.ImageUrls.push(element.PropertyValue);
+          }
+        });
+        list.push(adjoin);
+      }
+    });
+    return list;
+  }
+  /**
+   * 获取单位重点部位
+   */
+  public getCompanyImportantLocations(): CompanyImportantLocationInfo[] {
+    const list: CompanyImportantLocationInfo[] = [];
+    Object.keys(this.originalcompanyBuildingData.data).forEach((key) => {
+      const item = this.originalcompanyBuildingData.data[key];
+      if (item.Name === '重点部位') {
+        const important = new CompanyImportantLocationInfo();
+        important.AssetId = item.Id;
+        important.Id = '';
+        important.ImageUrls = [];
+        important.CompanyId = sessionStorage.getItem('companyId');
+        item.PropertyInfos.forEach(element => {
+          if (element.PropertyName === '名称/编号') {
+            important.Name = element.PropertyValue;
+          } else if (element.PropertyType === PropertyType.Image) {
+            important.ImageUrls.push(element.PropertyValue);
+          } else if (element.PropertyName === '主要危险性') {
+            important.Hazards = element.PropertyValue;
+          } else if (element.PropertyName === '使用性质') {
+            important.Nature = element.PropertyValue;
+          } else if (element.PropertyName === '所在位置') {
+            important.Position = element.PropertyValue;
+          } else if (element.PropertyName === '建筑结构') {
+            important.Structure = element.PropertyValue;
+          }
+        });
+        list.push(important);
+      }
+    });
+    return list;
+  }
+  /**
+   * 获取建筑重点部位
+   */
+  public getBuildingImportantLocations(): BuildingImportantLocationInfo[] {
+    const list: BuildingImportantLocationInfo[] = [];
+    Object.keys(this.originalcompanyBuildingData.data).forEach((key) => {
+      const item = this.originalcompanyBuildingData.data[key];
+      if (item.Name === '重点部位') {
+        const important = new BuildingImportantLocationInfo();
+        important.AssetId = item.Id;
+        important.Id = '';
+        important.ImageUrls = [];
+        important.BuildingId = this.selectStorey.buildingId;
+        item.PropertyInfos.forEach(element => {
+          if (element.PropertyName === '名称/编号') {
+            important.Name = element.PropertyValue;
+          } else if (element.PropertyType === PropertyType.Image) {
+            important.ImageUrls.push(element.PropertyValue);
+          } else if (element.PropertyName === '主要危险性') {
+            important.Hazards = element.PropertyValue;
+          } else if (element.PropertyName === '使用性质') {
+            important.Nature = element.PropertyValue;
+          } else if (element.PropertyName === '所在位置') {
+            important.Position = element.PropertyValue;
+          } else if (element.PropertyName === '建筑结构') {
+            important.Structure = element.PropertyValue;
+          }
+        });
+        list.push(important);
+      }
+    });
+    return list;
+  }
+  /**
+   * 获取单位消防设施
+   */
+  public getAllCompanyFacilityAssetInfo(): CompanyFacilityAssetInfo[] {
+    const list: CompanyFacilityAssetInfo[] = [];
+    Object.keys(this.originalcompanyBuildingData.data).forEach((key) => {
+      const item = this.originalcompanyBuildingData.data[key];
+      if (this.facilityAssetsName.has(item.Name)) {
+        const facility = new CompanyFacilityAssetInfo();
+        facility.CompanyId = sessionStorage.getItem('companyId');
+        facility.AssetId = item.Id;
+        facility.Id = '';
+        facility.Name = this.facilityAssetsName.get(item.Name);
+        facility.AssetName = item.Name;
+        facility.PropertyInfos = item.PropertyInfos;
+        facility.SitePlanId = item.FloorId;
+        list.push(facility);
+      }
+    });
+    return list;
+  }
+  /**
+   * 获取建筑消防设施
+   */
+  public getAllBuildingFacilityAssetInfo(): BuildingFacilityAssetInfo[] {
+    const list: BuildingFacilityAssetInfo[] = [];
+    Object.keys(this.originalcompanyBuildingData.data).forEach((key) => {
+      const item = this.originalcompanyBuildingData.data[key];
+      if (this.facilityAssetsName.has(item.Name)) {
+        const facility = new BuildingFacilityAssetInfo();
+        facility.BuildingId = this.selectStorey.buildingId;
+        facility.AssetId = item.Id;
+        facility.Id = '';
+        facility.Name = this.facilityAssetsName.get(item.Name);
+        facility.AssetName = item.Name;
+        facility.PropertyInfos = item.PropertyInfos;
+        facility.BuildingAreaId = item.FloorId;
+        list.push(facility);
+      }
+    });
+    return list;
+  }
+  /**
+   * 反序列化对象
+   * @param json 字符串
+   */
+  public deserialize<T>(json: any): T {
+    const obj: T = JSON.parse(
+      json,
+      (_, val) => {
+        if (val === null) { return null; }
+        if (Array.isArray(val) || typeof val !== 'object') {
+          return val;
+        }
+        return Object.entries(val).reduce((a, [key, val]) => {
+          const count = key.length;
+          if (count > 1) {
+            a[key[0].toUpperCase() + key.substring(1, count)] = val;
+          } else {
+            a[key] = val;
+          }
+          return a;
+        }, {});
+      }
+    );
+    return obj;
+  }
+}
+
+/**
+ * 单位毗邻
+ */
+export class CompanyAdjoinInfo {
+  public CompanyId: string;
+  public Id: string;
+  public Name: string;
+  public Direction: number;
+  public ImageUrls: string[] = [];
+  public AssetId: string;
+}
+/**
+ * 建筑毗邻
+ */
+export class BuildingAdjoinInfo {
+  public  BuildingId: string;
+  public Id: string;
+  public Name: string;
+  public Direction: number;
+  public ImageUrls: string[];
+  public AssetId: string;
+}
+/**
+ * 建筑重点部位
+ */
+export class BuildingImportantLocationInfo {
+  public BuildingId: string;
+  public Id: string;
+  public Name: string;
+  public Position: string;
+  public Structure: string;
+  public Nature: string;
+  public Hazards: string;
+  public ImageUrls: string[];
+  public AssetId: string;
+}
+/**
+ * 单位重点部位
+ */
+export class CompanyImportantLocationInfo {
+  public CompanyId: string;
+  public Id: string;
+  public Name: string;
+  public Position: string;
+  public Structure: string;
+  public Nature: string;
+  public Hazards: string;
+  public ImageUrls: string[];
+  public AssetId: string;
+}
+/**
+ * 单位消防素材信息
+ */
+export class CompanyFacilityAssetInfo {
+  public Id: string;
+  public Name: string;
+  public AssetName: string;
+  public PropertyInfos: string;
+  public AssetId: string;
+  public CompanyId: string;
+  public SitePlanId: string;
+}
+/**
+ * 建筑消防素材信息
+ */
+export class BuildingFacilityAssetInfo {
+  public Id: string;
+  public Name: string;
+  public AssetName: string;
+  public PropertyInfos: string;
+  public AssetId: string;
+  public BuildingId: string;
+  public BuildingAreaId: string;
+}
+/**
+ * 属性
+ */
+export class PropertyInfo {
+  // 标记位
+  public Tag: string;
+  // 属性书序
+  public Order: number;
+  // 是否启用
+  public Enabled: boolean;
+  // 是否可见
+  public Visible: boolean;
+  // 必填
+  public Required: boolean;
+  // 验证规则名称
+  public RuleName: string;
+  // 验证规则值
+  public RuleValue: string;
+  // 物理单位
+  public PhysicalUnit: string;
+  // 属性名称
+  public PropertyName: string;
+  // 属性类型
+  public PropertyType: PropertyType;
+  // 属性值
+  public PropertyValue: string;
+}
+/**
+ * 属性类型。
+ */
+export enum PropertyType {
+  // 单行文本。
+  SingleText,
+  // 多行文本。
+  MultipleText,
+  // 数值。
+  Numeric,
+  // 图片。
+  Image,
+  // 图片数值,专用于描述图片数量。
+  ImageNumeric,
+  // 方向
+  Direction,
+  // 布尔类型。
+  Boolean,
+  // 供给区域
+  SupplyArea,
+  // 供给类型
+  SupplyType
+}
+/**
+ * 处置节点
+ */
+export class DisposalNode {
+  /**
+   * 编号
+   */
+  public Id: string;
+  /**
+   * 名称
+   */
+  public Name: string;
+  /**
+   * 等级
+   */
+  public Level: number;
+  /**
+   * 排序
+   */
+  public Order: number;
+  /**
+   * 详情
+   */
+  public Description: string;
+  /**
+   * 注意事项
+   */
+  public Notes: string;
+  /**
+   * 天气
+   */
+  public Weather: string;
+  /**
+   * 气温
+   */
+  public AirTemperature?: number;
+  /**
+   * 风向
+   */
+  public WindDirection: Direction;
+  /**
+   * 风力等级
+   */
+  public WindScale: WindScale;
+  /**
+   * 图片名称
+   */
+  public ImageNames: string[];
+  /**
+   * 图片地址
+   */
+  public ImageUrls: string[];
+  /**
+   * 父节点编号
+   */
+  public ParentId: string;
+  /**
+   * 灾情编号
+   */
+  public DisasterId: string;
+  /**
+   * 预案组件编号
+   */
+  public PlanComponentId: string;
+  /**
+   * 单位编号
+   */
+  public CompanyId: string;
+  /**
+   * 总平面图编号
+   */
+  public SitePlanId: string;
+  /**
+   * 建筑编号
+   */
+  public BuildingId: string;
+  /**
+   * 建筑区域编号
+   */
+  public BuildingAreaId: string;
+}
+/**
+ * 方向。
+ */
+export enum Direction {
+    /**
+     * 东
+     */
+    East,
+    /**
+     * 西
+     */
+    West,
+    /**
+     * 南
+     */
+    South,
+    /**
+     * 北
+     */
+    North,
+    /**
+     * 东南
+     */
+    Southeast,
+    /**
+     * 西南
+     */
+    Southwest,
+    /**
+     * 东北
+     */
+    Northeast,
+    /**
+     * 西北
+     */
+    Northwest
+}
+/**
+ * 风力等级
+ */
+export enum WindScale {
+    WS0,
+    WS1,
+    WS2,
+    WS3,
+    WS4,
+    WS5,
+    WS6,
+    WS7,
+    WS8,
+    WS9,
+    WS10,
+    WS11,
+    WS12,
+    WS13,
+    WS14,
+    WS15,
+    WS16,
+    WS17,
+    WS18
+}
+/**
+ * 处置节点数据
+ */
+export class DisposalNodeData {
+  /**
+   * 编号
+   */
+  public Id: string;
+  /**
+   * 数据
+   */
+  public Data: any;
+  /**
+   * 版本号
+   */
+  public Version: string;
+  /**
+   * 处置节点编号
+   */
+  public DisposalNodeId: string;
+  /**
+   * 预案组件编号
+   */
+  public PlanComponentId: string;
+}
+/**
+ * 楼层节点数据
+ */
+export class FloorNodeData {
+  /**
+   * 存量
+   */
+  public Stock: Map<string, AssetData> = new Map<string, AssetData>();
+  /**
+   * 增量
+   */
+  public Increment: Map<string, AssetData> = new Map<string, AssetData>();
+  /**
+   * 用户定义的增量。
+   */
+  public DefinedIncrement: Map<string, AssetData> = new Map<string, AssetData>();
+}
+/**
+ * 素材数据
+ */
+export class AssetData {
+      /// <summary>
+    /// 模板编号
+    /// </summary>
+  public TemplateId: string;
+    /// <summary>
+    /// 编号
+    /// </summary>
+  public Id: string;
+    /// <summary>
+    /// 名称
+    /// </summary>
+  public Name: string;
+    /// <summary>
+    /// 角度
+    /// </summary>
+  public Angle: number;
+    /// <summary>
+    /// 颜色
+    /// </summary>
+  public Color: string;
+    /// <summary>
+    /// 坐标
+    /// </summary>
+  public Point: PIXI.Point;
+    /// <summary>
+    /// 宽度
+    /// </summary>
+  public Width: number;
+    /// <summary>
+    /// 高度
+    /// </summary>
+  public Height: number;
+    /// <summary>
+    /// 是否启用
+    /// </summary>
+  public Enabled: boolean;
+    /// <summary>
+    /// 填充方式
+    /// </summary>
+  public FillMode: FillMode;
+    /// <summary>
+    /// 图片地址
+    /// </summary>
+  public ImageUrl: string;
+    /// <summary>
+    /// 是否固定大小
+    /// </summary>
+  public  FixedSize: boolean;
+    /// <summary>
+    /// 点路径
+    /// </summary>
+  public MultiPoint: PIXI.Point[];
+    /// <summary>
+    /// 建筑ID
+    /// </summary>
+  public BuildingId: string;
+    /// <summary>
+    /// 单位ID
+    /// </summary>
+  public CompanyId: string;
+    /// <summary>
+    /// 楼层编号
+    /// </summary>
+  public FloorId: string;
+    /// <summary>
+    /// 楼层名称
+    /// </summary>
+  public FloorName: string;
+    /// <summary>
+    /// 消防要素编号
+    /// </summary>
+  public FireElementId: string;
+    /// <summary>
+    /// 属性列表
+    /// </summary>
+  public PropertyInfos: PropertyInfo[];
+    /// <summary>
+    /// 交互方式
+    /// </summary>
+  public InteractiveMode: InteractiveMode;
+    /// <summary>
+    /// 是否来自建筑
+    /// </summary>
+    public IsFromBuilding: boolean;
+    /// <summary>
+    /// 渲染方式。
+    /// </summary>
+  public DrawMode: ImageType;
+    /// <summary>
+    /// 9宫格边框数值。
+    /// </summary>
+  public Border: Border;
+    /// <summary>
+    /// 厚度。
+    /// </summary>
+  public Thickness: number;
+    /// <summary>
+    /// 素材类型
+    /// </summary>
+  public GameMode: GameMode;
+}
+/**
+ * 填充模式
+ */
+export enum FillMode {
+  Color,
+  Image
+}
+/**
+ * 交互方式
+ */
+export enum InteractiveMode {
+  /**
+   * 单点。
+   */
+  Single,
+  /**
+   * 多点不闭合。
+   */
+  Multiple,
+  /**
+   * 多点闭合。
+   */
+  MultipleClosed
+}
+/**
+ * 图片显示类型
+ */
+export enum ImageType {
+  Simple = 0,
+  Sliced = 1,
+  Tiled = 2,
+  Filled = 3
+}
+/**
+ * 边框
+ */
+export class Border {
+
+  public  x: number;
+
+  public  y: number;
+
+  public z: number;
+
+  public w: number;
+}
diff --git a/src/app/ui/collection-tools/addDisposalNode.html b/src/app/ui/collection-tools/addDisposalNode.html
new file mode 100644
index 0000000..27654e8
--- /dev/null
+++ b/src/app/ui/collection-tools/addDisposalNode.html
@@ -0,0 +1,24 @@
+<div class="functionalDomainContent">
+  <div mat-dialog-title>
+    <label *ngIf="!data.parentId">新建处置节点</label>
+    <label *ngIf="data.parentId">新建节点</label>
+  </div>
+
+  <form (ngSubmit)="onSubmit(form.value)" #form="ngForm" class="example-container">
+
+    <div class="keyMargin">
+      <mat-form-field>
+        <input matInput name="name" required ngModel placeholder="名称">
+      </mat-form-field>
+    </div>
+
+    <div class="submitBottom">
+      <button mat-raised-button color="primary" type="submit" [disabled]="!form.form.valid">
+          确定
+      </button>
+      <button mat-raised-button mat-dialog-close>取消</button>
+    </div>
+
+  </form>
+
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/addPlaneFigure.html b/src/app/ui/collection-tools/addPlaneFigure.html
new file mode 100644
index 0000000..930bf81
--- /dev/null
+++ b/src/app/ui/collection-tools/addPlaneFigure.html
@@ -0,0 +1,38 @@
+<div class="functionalDomainContent">
+  <div mat-dialog-title>
+    <label *ngIf="!data.isBuilding">新建平面图</label>
+    <label *ngIf="data.isBuilding">新建楼层/区域</label>
+  </div>
+
+  <form (ngSubmit)="onSubmit(form.value)" #form="ngForm" class="example-container">
+
+    <div class="keyMargin">
+      <mat-form-field>
+        <input matInput name="name" required ngModel placeholder="名称">
+      </mat-form-field>
+    </div>
+
+    <div class="keyMargin" *ngIf="data.isBuilding">
+      <mat-checkbox name="isRefugeStorey"  [(ngModel)]="checked">是否为避难层</mat-checkbox>
+    </div>
+
+    <div class="keyMargin">
+      <mat-form-field>
+        <input matInput name="area" type="number" required ngModel placeholder="面积 (平方米)">
+      </mat-form-field>
+    </div>
+
+    <div class="keyMargin">
+      <textarea name="details" ngModel placeholder="详情"></textarea>
+    </div>
+
+    <div class="submitBottom">
+      <button mat-raised-button color="primary" type="submit" [disabled]="!form.form.valid">
+          确定
+      </button>
+      <button mat-raised-button mat-dialog-close>取消</button>
+    </div>
+
+  </form>
+
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/collection-tools.component.html b/src/app/ui/collection-tools/collection-tools.component.html
new file mode 100644
index 0000000..a7798e7
--- /dev/null
+++ b/src/app/ui/collection-tools/collection-tools.component.html
@@ -0,0 +1,453 @@
+<div class="content">
+  <!-- header头部 -->
+  <div class="header">
+    <button mat-button (click)="toggle()">
+      <mat-icon style="vertical-align: middle;">toc</mat-icon>
+    </button>
+    <button mat-button (click)="toggle2()">
+      <mat-icon style="vertical-align: middle;">list</mat-icon>
+    </button>
+    <span style="color: gray;margin-right: 10px;margin-left: 10px;">图标大小</span>
+    <mat-form-field style="margin-top: 3px;">
+      <mat-select (selectionChange)='iconScale()' [(ngModel)]="selected">
+        <mat-option value="1">正常</mat-option>
+        <mat-option value="2">放大2倍</mat-option>
+        <mat-option value="4">放大4倍</mat-option>
+      </mat-select>
+    </mat-form-field>
+    <button (click)="copyAsset()" class="copytobutn" mat-button title="复制" style="margin:0 5px;" *ngIf="isEditPattern">
+      <mat-icon style="padding-bottom: 7px;">library_books</mat-icon>
+    </button>
+
+    <button (click)="pasteAsset()" class="copytobutn" mat-button title="粘贴" *ngIf="isEditPattern">
+      <mat-icon style="padding-bottom: 7px;">screen_share</mat-icon>
+    </button>
+
+    <span  [ngClass]="{'icongray': !basicInfo}" title="基本信息名称显示/隐藏" style="margin-right:20px;user-select: none;margin-left: 22px;" class="nameShow" (click)="basicInfoClick()">
+      基本信息名称
+      <mat-icon>visibility</mat-icon>
+    </span>
+    <span [ngClass]="{'icongray': !wantToWork}" *ngIf="!pattern" title="想定作业名称显示/隐藏" style="user-select: none;" class="nameShow" (click)="wantToWorkClick()">
+      想定作业名称
+      <mat-icon>visibility</mat-icon>
+    </span>
+    <div class="patternSwitch">
+      <span (click)="baseInfo()" [ngClass]="{'selectedPattern': pattern}">
+        基本信息编辑
+      </span>
+      <span *ngIf="isSixbtn" (click)="wantWork()" [ngClass]="{'selectedPattern': !pattern}">
+        想定作业编辑
+      </span>
+    </div>
+    <span style="position: absolute;right: 60px;cursor: pointer;">
+      <mat-icon title="查看/编辑模式" (click)="lookpat()" *ngIf="isEditPattern">tv</mat-icon>
+      <mat-icon title="查看/编辑模式" (click)="editpat()" *ngIf="!isEditPattern && isxxx">create</mat-icon>
+      <mat-icon *ngIf="isEditPattern " style="margin-left: 12px;" title="保存" (click)="saveSite()">description</mat-icon>
+    </span>
+  </div>
+
+  <!-- 头部操作栏 -->
+  <div class="headerOperate">
+    <button mat-button (click)="checkedBuilding({name:'总平面图'},-1)"
+      [ngClass]="{'buildingbtnchecked': checkedBuildingIndex==-1}">
+      <span>总平面图</span>
+      <span *ngIf="isEditPattern && !pattern">
+        <img src="../../../assets/images/fire.png" *ngIf="sitePlanIcon.fire==2">
+        <img src="../../../assets/images/noFire.png" *ngIf="sitePlanIcon.fire==1">
+        <img src="../../../assets/images/force.png" *ngIf="sitePlanIcon.force==2">
+        <img src="../../../assets/images/noForce.png" *ngIf="sitePlanIcon.force==1">
+      </span>
+    </button>
+    <button mat-button *ngFor="let item of allBuildings;let key = index" (click)="checkedBuilding(item,key)" class="bigeditdeletebtn"
+      [ngClass]="{'buildingbtnchecked': checkedBuildingIndex==key}">
+      <span>{{item.name}}</span>
+      <span *ngIf="isEditPattern && !pattern">
+        <img src="../../../assets/images/fire.png" *ngIf="item.fire && item.fire==2">
+        <img src="../../../assets/images/noFire.png" *ngIf="item.fire && item.fire==1">
+        <img src="../../../assets/images/force.png" *ngIf="item.force && item.force==2">
+        <img src="../../../assets/images/noForce.png" *ngIf="item.force && item.force==1">
+      </span>
+      <span class="editdeletebtn" *ngIf="isEditPattern && pattern">
+        <mat-icon (click)="editBuilding($event,item)"
+          style="font-size: 23px;vertical-align:sub;margin-left: 6px;color: rgb(26, 194, 26);">create</mat-icon>
+        <mat-icon (click)="deleteBuilding($event,item)"
+          style="font-size: 23px;vertical-align:sub;color: rgb(224, 51, 51);">delete</mat-icon>
+      </span>
+    </button>
+    <!-- <button (click)="yyy()">yyyyy</button> -->
+    <button mat-button (click)="createBuilding()" *ngIf="isEditPattern && pattern">
+      <span style="font-size: 24px;">+</span>
+    </button>
+    <div class="bigBox" *ngIf="!pattern">
+      <div class="weatherBox" [ngClass]="{'opened': weatherBtn,'close': !weatherBtn}">
+        <mat-icon class="openbtn" *ngIf="weatherBtn" (click)="weatherBtnHidden()">keyboard_arrow_right</mat-icon>
+        <mat-icon class="openbtn" *ngIf="!weatherBtn" (click)="weatherBtnShow()">keyboard_arrow_left</mat-icon>
+        <span class="name">天气</span>
+        <input [(ngModel)]="canvasData.selectPanelPointBaseData.weather" type="text" placeholder="最多输入10字节" maxlength="10">
+        <span class="name">气温</span>
+        <div style="display: inline-block;position: relative;">
+          <input [(ngModel)]="canvasData.selectPanelPointBaseData.airTemperature" style="width: 120px;" type="number" value="0" oninput="if(value.length>2)value=value.slice(0,2)">
+          <span style="position: absolute;right:17px;color: #9c9fa5;">℃</span>
+        </div>
+        <span class="name">风力</span>
+        <select [(ngModel)]="canvasData.selectPanelPointBaseData.windScale">
+          <option value ="0">0(无风)</option>
+          <option value ="1">1(软风)</option>
+          <option value ="2">2(轻风)</option>
+          <option value ="3">3(微风)</option>
+          <option value ="4">4(和风)</option>
+          <option value ="5">5(清风)</option>
+          <option value ="6">6(强风)</option>
+        </select>
+        <span class="name">风向</span>
+        <select [(ngModel)]="canvasData.selectPanelPointBaseData.windDirection">
+          <option value ="0">东</option>
+          <option value ="1">西</option>
+          <option value ="2">南</option>
+          <option value ="3">北</option>
+          <option value ="4">东南</option>
+          <option value ="5">西南</option>
+          <option value ="6">东北</option>
+          <option value ="7">西北</option>
+        </select>
+      </div>
+    </div>
+  </div>
+
+  <!--功能区 -->
+  <div class="functionalDomain">
+    <div class='functionalDomainContent' id="functionalDomainContent">
+
+      <!-- H5Canvas -->
+      <app-working-area #canvas [init]='this'></app-working-area>
+      <!-- H5Canvas -->
+      <div id="leftDiv" class='functionalDomainLeft publicCss' [ngClass]="{'togglePanel': toggleExpandPanel==true,'scenarioAssignment': !pattern}" style="user-select: none;">
+        <div class="leftDragDiv"  (mousedown)="leftDivMouseDown($event)"></div>
+        <!-- 平面图 -->
+        <div class="planarGraph">
+          <div class="planarGraphHeader" (click)='togglePlanarGraph()'>
+            <mat-icon *ngIf="togglePlane">keyboard_arrow_up</mat-icon>
+            <mat-icon *ngIf="!togglePlane">keyboard_arrow_down</mat-icon>
+            <label class="overflowText" style="font-weight: 550;">平面图</label>
+            <label class="hover" *ngIf="isEditPattern && pattern">
+              <mat-icon (click)='foundPanel($event)'>add</mat-icon>
+            </label>
+          </div>
+          <div [hidden]="!togglePlane" >
+              <div class="sitePlanContent" *ngFor="let item of sitePlanData;let key = index" [ngClass]="{'isRefugeStorey':item.isRefugeStorey==true,'selectSitePlan': selectSitePlanIndex==key}" (click)='selectSitePlan(item,key)'>
+              <label class="overflowText" style="display:inline-block; max-width: 180px;"><mat-icon *ngIf="!item.imageUrl" class="matIcons">broken_image</mat-icon>   {{item.name}}</label>
+              <a href="javascript:;" class="fireForce" *ngIf="isEditPattern && !pattern">
+                <img src="../../../assets/images/fire.png" *ngIf="item.fire && item.fire==2">
+                <img src="../../../assets/images/noFire.png" *ngIf="item.fire && item.fire==1">
+                <img src="../../../assets/images/force.png" *ngIf="item.force && item.force==2">
+                <img src="../../../assets/images/noForce.png" *ngIf="item.force && item.force==1">
+              </a>
+              <a href="javascript:;" class="a-upload" *ngIf="selectSitePlanIndex==key && isEditPattern && pattern" title="替换底图" >
+                <input type="file" (change)='replaceBaseMap($event,item)' accept="image/*">
+                <mat-icon class="matIcons">photo_size_select_actual</mat-icon>
+              </a>
+              <a href="javascript:;" id="a-uploadImg" *ngIf="selectSitePlanIndex==key && !item.imageUrl && isEditPattern" title="上传底图" >
+                <input type="file" (change)='replaceBaseMap($event,item)' accept="image/*">
+                <img src="../../../assets/images/upload.jpg">
+              </a>
+              <!-- 右边定位操作栏 -->
+              <div id="rightOperate" *ngIf="selectSitePlanIndex==key && item.imageUrl && isEditPattern" (click)="$event.stopPropagation();">
+                <p class="functionButton">
+                  <mat-icon class="functionIcon bigFunctionIcon" title="上移" (click)='moveUp(item,key)'>keyboard_arrow_up</mat-icon>
+                </p>
+                <p class="functionButton">
+                  <mat-icon class="functionIcon" title="编辑属性" (click)='editPlaneData(item)'>edit</mat-icon>
+                  <mat-icon class="functionIcon" title="删除" (click)="deletePlaneData(item)">delete</mat-icon>
+                </p>
+                <p class="functionButton">
+                  <mat-icon class="functionIcon" title="旋转底图" (click)='revolveImg(item)'>cached</mat-icon>
+                  <mat-icon class="functionIcon" title="复制" (click)="duplicateLayer(item)">library_books</mat-icon>
+                </p>
+                <p class="functionButton">
+                  <mat-icon class="functionIcon bigFunctionIcon" title="下移" (click)='moveDown(item,key)'>keyboard_arrow_down</mat-icon>
+                </p>
+              </div>
+              <!-- 右边定位操作栏 -->
+            </div>
+          </div>
+        </div>
+
+        <!-- 素材库 -->
+        <div id="materialBank" *ngIf="isEditPattern" [ngClass]="{'selectEditMode': pattern}">
+          <div class="planarGraphHeader" (click)='toggleMaterial()'>
+            <mat-icon *ngIf="toggleMaterialBank">keyboard_arrow_up</mat-icon>
+            <mat-icon *ngIf="!toggleMaterialBank">keyboard_arrow_down</mat-icon>
+            <label class="overflowText" style="font-weight: 550;">素材库</label>
+          </div>
+          <div [hidden]="!toggleMaterialBank" [ngClass]="{'materialBankDIV': pattern}">
+            <mat-accordion *ngFor="let item of allLibrary" id="panelLibrary">
+              <mat-expansion-panel (opened)='opened(item)'>
+                <mat-expansion-panel-header>
+                  <label class="text">{{item.name}}</label>
+                </mat-expansion-panel-header>
+
+                <div class="panelLibraryFlex">
+                  <div class="imgBox" *ngFor="let items of item.images;let key = index" [title]="items.name"
+                    (click)='selectImg(item,items,key)'
+                    [ngClass]="{'selectImg': selectLibrary==item.name && selectImageIndex==key}">
+                    <img [src]="items.imageUrl" onerror="javascript:this.src='../../../assets/images/noImg.png'">
+                    <p class="overflowText">{{items.name}}</p>
+                  </div>
+                </div>
+
+              </mat-expansion-panel>
+            </mat-accordion>
+          </div>
+        </div>
+        <!-- 素材库 -->
+        <!-- 处置预案 -->
+        <div class="handlePlan" *ngIf="!pattern">
+          <div class="planarGraphHeader" (click)='toggleHandlePlan()'>
+            <mat-icon *ngIf="toggleHandlePlans">keyboard_arrow_up</mat-icon>
+            <mat-icon *ngIf="!toggleHandlePlans">keyboard_arrow_down</mat-icon>
+            <label class="overflowText" style="font-weight: 550;">处置预案</label>
+            <label style="margin-left: 45px;" *ngIf="isEditPattern">
+              <mat-icon style="color: #c2a40ce8;" title="计算差异" (click)='countValue($event)'>flash_on</mat-icon>
+              <mat-icon style="margin-left: 3px;" title="新建空节点" (click)='addPanelPoint($event,null,treeData)'>add</mat-icon>
+            </label>
+          </div>
+          <div [hidden]="!toggleHandlePlans">
+            
+            <nz-tree #nzTreeComponent [nzData]="treeData" nzBlockNode nzDraggable (nzOnDrop)="nzEvent($event)" [nzTreeTemplate]="nzTreeTemplate" [nzBeforeDrop]="beforeDrop" [nzExpandedKeys]="defaultExpandedKeys"></nz-tree>
+            <ng-template #nzTreeTemplate let-node let-origin="origin">
+              <div id="terrNodePublic" (click)='selectanelPoint(node.origin)' [ngClass]="{'selectanelPoint': selectDisposalNode==node.origin.id}">
+                <label title="{{node.title}}" class="overflowText textNode">{{node.title}}</label>
+                <div class="planIconDiv" *ngIf="isEditPattern">
+                  <mat-icon  *ngIf="!node.origin.sitePlanId && !node.origin.buildingAreaId" (click)='editPanelPoint($event,node)'>edit</mat-icon>
+                  <mat-icon *ngIf="node.level===0" (click)='addPanelPoint($event,node.origin,null)'>add</mat-icon>
+                  <mat-icon  *ngIf="!node.origin.sitePlanId && !node.origin.buildingAreaId" (click)='copyPanelPoint($event,node,treeData)'>library_books</mat-icon>
+                  <mat-icon (click)='deletePanelPoint($event,node.origin)'>delete_forever</mat-icon>
+                </div>
+              </div>
+            </ng-template>
+
+          </div>
+        </div>
+        <!-- 处置预案 -->
+
+      </div>
+
+      <div id="rightDiv" class="functionalDomainRight publicCss " [ngClass]="{'togglePanel2': toggleExpandPanelRight==true}" style="user-select: none;">
+        <!-- 右侧div鼠标拖动div -->
+        <div style="width: 3px;height: 100%;position: absolute;left: 0;cursor: e-resize;z-index: 1000;" (mousedown)="rightDivMouseDown($event)"></div>
+        <!-- 属性 -->
+        <div [ngClass]="{'forbidden': !isEditPattern}" id="property" class="property" style="height: 50%;background-color: white;">
+          <div class="title">
+            <div>
+              <span style="user-select: none">属性</span>
+            </div>
+          </div>
+          <!-- 平面图属性 -->
+          <div class="siteproperty" style="user-select: none" *ngIf="isShowProperty && isShowAttribute">
+            <p>面积(平方米)</p>
+            <div class="siteproperty_size">{{canvasData.selectStorey.area}}</div>
+            <p>详情</p>
+            <div class="siteproperty_size">
+              {{canvasData.selectStorey.details}}
+            </div>
+          </div>
+          <!-- 素材属性 -->
+          <div class="assetsproperty" style="user-select: none"  *ngIf="isShowProperty && !isShowAttribute">
+            <h3 style="text-align: center;font-weight: 900;">{{assetName}}</h3>
+
+            <div *ngIf="canvasAssetObj.InteractiveMode == 0">
+              <p>宽度(像素)</p>
+              <input type="text" class="biginput" [(ngModel)]="assetWidth" (input)="assetWidthIunput()" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+              <p>高度(像素)</p>
+              <input type="text" class="biginput" [(ngModel)]="assetHeight" (input)="assetHeightIunput()" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+              <p>角度</p>
+              <div style="width: 100%;display: flex;vertical-align: top;height: 22px;">
+                <input type="number" class="smallinput" [(ngModel)]="sliderValue"
+                  oninput="if(value>360)value=360;if(value<0)value=0;" (input)="assetAngleIunput()" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+                <mat-slider color="primary" min="0" max="360" step="1" style="bottom: 12px;left: 2px;width: 70%;"
+                  [(ngModel)]="sliderValue" (change)="assetAngleIunput()"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)"></mat-slider>
+              </div>
+            </div>
+            <!-- 如果是多点连线 -->
+            <div *ngIf="canvasAssetObj.InteractiveMode == 1">
+              <p>厚度</p>
+              <div style="width: 100%;display: flex;vertical-align: top;height: 22px;">
+                <input type="number" class="smallinput" [(ngModel)]="sliderValueThickness"
+                  oninput="if(value>999)value=999;if(value<0)value=0;" (input)="assetThicknessIunput()" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+                <mat-slider color="primary" min="0" max="999" step="1" style="bottom: 12px;left: 2px;width: 70%;"
+                  [(ngModel)]="sliderValueThickness" (change)="assetThicknessIunput()"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)"></mat-slider>
+              </div>
+            </div>
+
+            <p style="margin-top: 4px;margin-bottom: 0px;">是否高亮</p>
+            <div>
+              <input class="input" [(ngModel)]="isHighLight" type="checkbox"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+              <span style="font-size: 14px;">选中高亮</span>
+            </div>
+            <div class="colorBigDiv" *ngIf="canvasAssetObj.FillMode == 0">
+              <div class="colorBigTemplateDiv">
+                <span>颜色</span>
+                <div class="colorTemplateDiv" [style]="{'background-color':selectedcolor}">
+
+                </div>
+              </div>
+              <div class="colorDiv" *ngIf="isEditPattern && pattern && canvasAssetObj.GameMode == 0"> 
+                <ul>
+                    <li (click)="selectcolor(item,key)" class="colorLi" *ngFor="let item of colors,let key=index" [style]="{'background-color':item}"></li>
+                </ul>
+              </div>
+              <span style="color: #9c9fa5;font-size: 14px;">透明度</span>
+              <mat-slider color="primary" min="0" max="100%" step="1" style="left: 1px; width: 55%;min-width: 90px;"
+                [(ngModel)]="colorDivSliderValue" (change)="colorDivSliderChange()"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)"></mat-slider>
+              <span style="color: #9c9fa5;font-size: 12px;">{{colorDivSliderValue}}%</span>
+            </div>
+            <div *ngFor="let item of PropertyInfos;index as key ">
+              <!-- 单行文本 -->
+              <div *ngIf="item.PropertyType == 0">
+                <p>{{item.PropertyName}}<span style="font-size: 14px;" *ngIf="item.PhysicalUnit">({{item.PhysicalUnit}})</span></p>
+                <input type="text" class="biginput" [value]="item.PropertyValue" (input)="assetInputChange(item,$event)"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+              </div>
+              <!-- 多行文本 -->
+              <div *ngIf="item.PropertyType == 1">
+                <p>{{item.PropertyName}}<span style="font-size: 14px;" *ngIf="item.PhysicalUnit">({{item.PhysicalUnit}})</span></p>
+                <textarea class="textarea" name="" id=""  [value]="item.PropertyValue" (input)="assetInputChange(item,$event)"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)"></textarea>
+              </div>
+              <!-- 数值 -->
+              <div *ngIf="item.PropertyType == 2">
+                <p>{{item.PropertyName}}<span style="font-size: 14px;" *ngIf="item.PhysicalUnit">({{item.PhysicalUnit}})</span></p>
+                <input type="number" class="biginput" [value]="item.PropertyValue" (input)="assetInputChange(item,$event)" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+              </div>
+              <!-- 图片数量 -->
+              <div *ngIf="item.PropertyType == 4" style="height: 140px;">
+                <div style="position: relative;width: 100%;height: 21px;margin: 1px 0;">
+                  <p style="width: 40%;display: inline-block;">{{item.PropertyName}}</p>
+                  <span style="width: 26%;text-align:right;font-size: 13px;">{{imagesArr.length ? imagesArr.length : 0}} / {{item.PropertyValue}}</span>
+                  <input  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)" *ngIf="isImgNumCss" accept="image/*" (change)="selectFile($event)" type="file" style="width: 33%;position: absolute;right: 10px;top: 1px;opacity: 0;z-index: 100;cursor: pointer;height: 21px;">
+                  <div style="width: 33%;height: 21px;line-height: 21px;text-align: center;position: absolute;right: 10px;top: 1px;z-index: 99;border: 1px solid rgb(208, 211, 214);border-radius: 2px;font-size: 13px;cursor: pointer;" (click)="imgNumBeyond()">添加</div>
+                  
+                  
+                  <div style="position: relative;;width: 89%;border:1px solid rgb(208, 211, 214);height: 100px;margin: 6px auto;" class="swiper-container">
+                    <div id="viewerjs" class="swiper-wrapper" [ngClass]="{'noImgCss': imagesArr.length == 0}">
+                      <div class="swiper-slide" style="text-align: center;" *ngFor="let img of imagesArr"><img [src]="img.PropertyValue + '?x-oss-process=image/resize,m_fixed,h_100,w_100'" alt="" [attr.data-original]="img.PropertyValue"></div>
+                    </div>
+                    <!-- 如果需要导航按钮 -->
+                    <div class="swiper-button-next"></div>
+                    <div class="swiper-button-prev"></div>
+                    <span style="position: absolute;right: 2px;top: 2px;cursor: pointer;z-index: 200;" *ngIf="isEditPattern">
+                      <mat-icon class="hoverred" (click)="deleteImg()">delete</mat-icon>
+                    </span>
+                  </div>
+                </div>
+              </div>
+              <!-- 方向 -->
+              <div *ngIf="item.PropertyType == 5" class="selectDiv">
+                <p style="display: inline-block;">{{item.PropertyName}}</p>
+                <select (change)="direction(item,$event)"  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+                  <option value ="0" [selected]="item.PropertyValue ==0">东</option>
+                  <option value ="1" [selected]="item.PropertyValue ==1">西</option>
+                  <option value ="2" [selected]="item.PropertyValue ==2">南</option>
+                  <option value ="3" [selected]="item.PropertyValue ==3">北</option>
+                  <option value ="4" [selected]="item.PropertyValue ==4">东南</option>
+                  <option value ="5" [selected]="item.PropertyValue ==5">西南</option>
+                  <option value ="6" [selected]="item.PropertyValue ==6">东北</option>
+                  <option value ="7" [selected]="item.PropertyValue ==7">西北</option>
+                </select>
+              </div>
+              <!-- 布尔值 是1或否0 -->
+              <div *ngIf="item.PropertyType == 6">
+                <p>{{item.PropertyName}}</p>
+                <input  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)" [checked]="item.PropertyValue == 1" class="input" type="radio" name="radio" (click)="assetRadioChange(item,'1')"><span>是</span>
+                <input  [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)" [checked]="item.PropertyValue == 0" class="input" type="radio" name="radio" (click)="assetRadioChange(item,'0')"><span>否</span>
+              </div>
+              <!-- 供给区域 -->
+              <div *ngIf="item.PropertyType == 7" class="selectDiv">
+                <p style="display: inline-block;">{{item.PropertyName}}</p>
+                <select (change)="supplyArea(item,$event)" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+                  <option value ="0" [selected]="item.PropertyValue ==0">全部</option>
+                  <option value ="1" [selected]="item.PropertyValue ==1">高区</option>
+                  <option value ="2" [selected]="item.PropertyValue ==2">中区</option>
+                  <option value ="3" [selected]="item.PropertyValue ==3">低区</option>
+                  <option value ="4" [selected]="item.PropertyValue ==4">高中区</option>
+                  <option value ="5" [selected]="item.PropertyValue ==5">高低区</option>
+                  <option value ="6" [selected]="item.PropertyValue ==6">中低区</option>
+                </select>
+              </div>
+              <!-- 供给类型 -->
+              <div *ngIf="item.PropertyType == 8" class="selectDiv">
+                <p style="display: inline-block;">{{item.PropertyName}}</p>
+                <select (change)="supplyType(item,$event)" value="4" [disabled]="!isEditPattern || (canvasAssetObj.GameMode == 0 && !pattern)">
+                  <option value ="0" [selected]="item.PropertyValue ==0">消火栓</option>
+                  <option value ="1" [selected]="item.PropertyValue ==1">喷淋</option>
+                  <option value ="2" [selected]="item.PropertyValue ==2">水幕</option>
+                  <option value ="3" [selected]="item.PropertyValue ==3">泡沫</option>
+                  <option value ="4" [selected]="item.PropertyValue ==4">消防</option>
+                </select>
+              </div>
+          </div>
+        </div>
+        
+        </div>
+        <!-- 消防要素 -->
+        <div id="firecategories" class="firecategories" style="height: 50%;">
+          <!-- 素材属性div鼠标拖动div -->
+          <div style="height:3px;width: 100%;position: absolute;top: 0;cursor: n-resize;z-index: 1000;" (mousedown)="firecategoriesDivMouseDown($event)"></div>
+          
+          <div class="title">
+            <div>
+              <span style="user-select: none">消防要素</span>
+            </div>
+          </div>
+
+          <div class="firecategoriesTree">
+            <!-- 消防列表树写在这里 -->
+            <mat-tree [dataSource]="dataSource" [treeControl]="treeControl" cdkDropList  [cdkDropListData]="dataSource" (cdkDropListDropped)="drop($event)">
+              
+              <mat-tree-node cdkDrag cdkDragDisabled="false" [ngClass]="{'isLookPattern': !node.isLookPattern && !isEditPattern}" *matTreeNodeDef="let node;" matTreeNodePadding cdkTreeNodePaddingIndent='26' (click)="clickTreeNode(node)" class="treeNode">
+                <button mat-icon-button disabled></button>
+                <span title="{{node.name}}" [ngClass]="{'treeText': !node.isTemplate}">
+                  {{node.name}}      
+                </span> 
+                <span  *ngIf="node.isTemplate">({{node.children.length}})</span>
+                <span class="isLookCss" (click)="clickLookItem(node)"><mat-icon [ngClass]="{'icongray': node.isLook == false}">visibility</mat-icon></span>
+              </mat-tree-node>
+            
+            
+              <mat-tree-node cdkDrag cdkDragDisabled="false" [ngClass]="{'isLookPattern': !node.isLookPattern && !isEditPattern}" *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding (click)="clickTreeNode(node)"class="treeNode" >
+                <button mat-icon-button 
+                        matTreeNodeToggle
+                        [attr.aria-label]="'toggle ' + node.name">
+                  <mat-icon class="mat-icon-rtl-mirror">
+                    {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
+                  </mat-icon>
+                </button>
+                <span title="{{node.name}}" [ngClass]="{'treeText': !node.isTemplate}">
+                  {{node.name}}      
+                </span>   
+                <span  *ngIf="node.isTemplate && node.isNewElement">({{node.children.length}})</span>
+                <span class="isLookCss" (click)="clickLookItem(node)"><mat-icon [ngClass]="{'icongray': node.isLook == false}">visibility</mat-icon></span>
+              </mat-tree-node>
+             
+            </mat-tree>
+          </div>
+        </div>
+      </div>
+
+      <div id="bottomDiv" class="bottomCss" *ngIf="!pattern">
+        <div class="dragDiv" (mousedown)="bottomDivMouseDown($event)"></div>
+        <div class="title">
+          <div (click)="details()" [ngClass]="{'detailsAndattentBtn': detailsAndattentBtn}">
+            节点详情
+          </div>
+          <div (click)="attent()" [ngClass]="{'detailsAndattentBtn': !detailsAndattentBtn}">
+            注意事项
+          </div>
+        </div>
+        <div class="body">
+          <textarea [disabled]="!isEditPattern" *ngIf="detailsAndattentBtn" name="" id="" rows="10" [(ngModel)]="canvasData.selectPanelPointBaseData.description"></textarea>
+          <textarea [disabled]="!isEditPattern" *ngIf="!detailsAndattentBtn" name="" id="" rows="10" [(ngModel)]="canvasData.selectPanelPointBaseData.nodes"></textarea>
+        </div>
+      </div>
+    </div>
+  </div>
+
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/collection-tools.component.scss b/src/app/ui/collection-tools/collection-tools.component.scss
new file mode 100644
index 0000000..19402a6
--- /dev/null
+++ b/src/app/ui/collection-tools/collection-tools.component.scss
@@ -0,0 +1,445 @@
+@import './panel.scss';
+.icongray{
+  color: #D9D0DC;
+}
+.content {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  box-sizing: border-box;
+  padding: 1px;
+  display: flex;
+  flex-direction: column;
+
+  .buildingbtnchecked{
+    background-color: #34A6FD;
+    color: white;
+  }
+}
+
+//header头部
+.header {
+  position: relative;
+  flex: 5%;
+  display: flex;
+  align-items:center;
+  min-height: 40px;
+  background-color: #fff;
+  .nameShow{
+    cursor: pointer;
+    user-select: none;
+  }
+  .copytobutn{
+    width: 33px;
+    min-width: 33px;
+    display: flex;
+    justify-content: center;
+  }
+  font-size: 18px;
+  mat-icon{
+    font-size: 26px;
+    vertical-align: text-top;
+  }
+  span{
+    height: 24px;
+    line-height: 24px;
+  }
+  .patternSwitch{
+    position: absolute;
+    right: 140px;
+    span{
+      font-size: 18px;
+      cursor: pointer;
+      margin: 0 3px;
+      display: inline-block;
+      border: 1px solid gray;
+      border-radius: 3px;
+      padding: 0 5px;
+    }
+    .selectedPattern{
+      background-color: #2196f3;
+      color: white;
+    }
+  }
+}
+
+//头部操作栏
+.headerOperate {
+  img {
+    width: 24px; 
+    height: 24px;
+    vertical-align: middle;
+    margin-left: 1px;
+  }
+  span{
+    font-size: 18px;
+  }
+  flex: 5%;
+  display: flex;
+  align-items:center;
+  min-height: 40px;
+  box-sizing: border-box;
+  margin: 3px 0;
+  background-color: white;
+  button{
+    border: 0.5px solid rgb(208, 211, 214);
+    margin: 0 2px;
+  }
+  .editdeletebtn{
+    display: none;
+  }
+  .bigeditdeletebtn:hover{
+    .editdeletebtn{
+      display: inline-block;
+    }
+  }
+}
+
+//功能区
+// icon统一样式
+.mat-icon {
+  cursor:pointer;
+  vertical-align: middle;
+}
+//左右两侧功能栏 统一样式
+.publicCss {
+  border-radius: 5px;
+  position: absolute;
+  height: 100%;
+  top: 0;
+}
+.functionalDomain {
+  flex: 90%;
+  overflow: hidden;
+  .functionalDomainContent {
+    position: relative;
+    width: 100%;
+    height: 100%;
+  }
+  .functionalDomainLeft {
+    background-color: #fff;
+    display: flex;
+    flex-direction: column;
+    margin-left: 0px;
+    transition: margin-left 0.5s;
+    min-width: 235px;
+    border: 1px solid #E6EAEE;
+    width: 235px;
+    left: 0;
+    z-index: 111;
+    .leftDragDiv{
+      position: absolute;
+      right: 0;
+      height: 100%;
+      width: 3px;
+      z-index: 1000;
+      cursor: e-resize;
+    }
+  }
+  .functionalDomainRight {
+    z-index: 1001;
+    margin-right: 0px;
+    transition:  margin-right 0.5s;
+    border: 1px solid #464646;
+    width: 235px;
+    right: 0;
+    
+  }
+    //右边导航栏显示隐藏
+  .togglePanel2 {
+    margin-right: -2000px;
+    transition:  margin-right 1s;
+  }
+  //左侧导航栏显示隐藏
+  .togglePanel {
+    margin-left: -2000px;
+    transition: margin-left 1s;
+  }
+
+}
+
+//右边操作栏
+.title{
+  width: 100%;
+  height: 35px;
+  background-color: #464646;
+  div{
+    width: 50%;
+    height: 35px;
+    line-height: 35px;
+    background-color: #595959;
+    border-radius: 5px;
+    span{
+      color: white;
+      font-size: 14px;
+      font-weight: 400;
+      padding-left: 5px;
+    }
+  }
+}
+
+
+//右侧属性
+.property{
+  display: flex;
+  flex-flow: column;
+  .siteproperty{
+    height: 100%;
+    overflow-y: auto;
+    p{
+      color: #9c9fa5;
+      padding-left: 5px;
+    }
+    .siteproperty_size{
+      background-color: #e3e3e3;
+      width: 93%;
+      margin: 0 auto;
+      border-radius: 3px;
+      min-height: 21px;
+    }
+    .rightAttribute{
+      width: 12%;
+      height: 99.5%;
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      border: 2px solid #464646;
+    }
+  }
+  .assetsproperty{
+    overflow-y: auto;
+    height: 100%;
+    p{
+      color: #9c9fa5;
+      margin:1px 0 3px 8px;
+      font-size: 14px;
+    }
+    span{
+      font-size: 15px;
+    }
+    input{
+      height: 18px;
+    }
+    .biginput{
+      display: block;
+      width: 88%;
+      margin: 0 auto;
+    }
+    .smallinput{
+      display: block;
+      width: 19%;
+      margin-left: 8px;
+    }
+    .textarea{
+      display: block;
+      width: 88%;
+      height: 50px;
+      margin: 0 auto;
+    }
+    .swiper-button-next{
+      right: 6px;
+    }
+    .swiper-button-prev{
+      left: 6px;
+    }
+    .swiper-container{
+      // --swiper-theme-color: #ff6600;/* 设置Swiper风格 */
+      // --swiper-navigation-color: #00ff33;/* 单独设置按钮颜色 */
+      --swiper-navigation-size:20px;/* 设置按钮大小 */
+    }
+    .hoverred:hover{
+      color: rgb(187, 28, 28);
+    }
+    .selectDiv{
+      height: 21px;
+      position: relative;
+      margin-bottom: 5px;
+      select{
+        width: 98px;
+        height: 22px;
+        vertical-align: middle;
+        position: absolute;
+        right: 10px;
+        top: 1px;
+        border: 1px solid rgb(208, 211, 214);
+        border-radius: 2px;
+      }
+    }
+    .colorBigDiv{
+      width: 88%;
+      margin-left: 8px;
+      .colorBigTemplateDiv{
+        span{
+          color: #9c9fa5;
+          font-size: 14px;
+          height: 26px;
+          line-height: 26px;
+        }
+        .colorTemplateDiv{
+          width: 65%;
+          height: 22px;
+          display: inline-block;
+          vertical-align: middle;
+          margin-left: 26px;
+        }
+      }
+      
+      .colorDiv{
+        .colorLi{
+          width: 24px;
+          height: 24px;
+          list-style: none;
+          float: left;
+          border: 2px solid white;
+        }
+        .coloractive{
+          border: 2px solid black;
+        }
+      }
+    }
+      
+  }
+}
+//右侧消防要素
+.firecategories{
+  position: relative;
+  display: flex;
+  flex-flow: column;
+  .firecategoriesTree{
+    overflow-y: auto;
+    height: 100%;
+    mat-tree-node{
+      position: relative;
+    }
+    .isLookCss{
+      position: absolute;
+      right: 6px;
+    }
+  }
+}
+
+// 解决轮播图蓝框问题
+div:focus {
+  outline: none;
+}
+
+//没有图片时显示无图片背景图
+.noImgCss{
+  background: url(../../../assets/images/noImg.png) no-repeat center center;
+  background-size: 88% 100%;/*按比例缩放*/
+}
+.input{
+  width: 18px;
+  height: 18px;
+  vertical-align: middle;
+  margin-left: 9px;
+  margin-right: 3px;
+}
+
+
+// tree
+.mat-tree-node{
+  min-height: 0;
+  height: 32px;
+  line-height: 32px;
+  font-size: 13px;
+  cursor: pointer;
+}
+.treeNode:hover{
+  background-color: #ccebf8;
+}
+.isLookPattern{
+  display: none;
+}
+.treeText{
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  display: inline-block;
+  width: 65px;
+}
+
+.bigBox{
+  width: 700px;
+  height: 40px;
+  position: absolute;
+  overflow: hidden;
+  right: 0;
+}
+.weatherBox{
+  height: 40px;
+  width: 700px;
+  line-height: 40px;
+  position: absolute;
+  right: 0;
+  transition: right linear .5s;
+  .openbtn{
+    font-size: 45px;
+    height: 45px;
+    width: 40px;
+  }
+  .name{
+    font-size: 16px;
+    vertical-align: middle;
+    margin-left: 3px;
+  }
+  input{
+    width: 140px;
+    height: 22px;
+    margin-left: 3px;
+  }
+  select{
+    width: 96px;
+    height: 25px;
+    margin-left: 3px;
+    vertical-align: middle;
+  }
+}
+.open{
+  right: 0px;
+}
+.close{
+  right:-622px;
+}
+.bottomCss{
+  position: absolute;
+  left: 232px;
+  right: 0px;
+  bottom: 0;
+  height: 158px;
+  width: auto;
+  z-index: 100;
+  background-color: white;
+  border: 1px solid #464646;
+  .dragDiv{
+    width: 100%;
+    height: 3px;
+    position: absolute;
+    top: 0;
+    z-index: 1000;
+    cursor: n-resize;
+  }
+  .title{
+    height: 35px;
+    background-color: #464646;
+    div{
+      background-color: #464646;
+      float: left;
+      width: 80px;
+      color: white;
+      font-size: 13px;
+      padding-left: 5px;
+      cursor: pointer;
+    }
+    .detailsAndattentBtn{
+      background-color: #595959;
+    }
+  }
+  .body{
+    textarea{
+      width: 100%;
+      border-radius: 0px;
+   }   
+  }
+}
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/collection-tools.component.spec.ts b/src/app/ui/collection-tools/collection-tools.component.spec.ts
new file mode 100644
index 0000000..acc7d5e
--- /dev/null
+++ b/src/app/ui/collection-tools/collection-tools.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CollectionToolsComponent } from './collection-tools.component';
+
+describe('CollectionToolsComponent', () => {
+  let component: CollectionToolsComponent;
+  let fixture: ComponentFixture<CollectionToolsComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ CollectionToolsComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CollectionToolsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/ui/collection-tools/collection-tools.component.ts b/src/app/ui/collection-tools/collection-tools.component.ts
new file mode 100644
index 0000000..a5b4984
--- /dev/null
+++ b/src/app/ui/collection-tools/collection-tools.component.ts
@@ -0,0 +1,2362 @@
+import { Component, OnInit, Inject, ViewChild,ElementRef,Renderer2, ViewContainerRef } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
+import {leftFunctionalDomainComponent,editPlaneFigureComponent,editDisposalNodeComponent,addDisposalNodeComponent} from './leftFunctionalDomain'
+import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
+import {FlatTreeControl} from '@angular/cdk/tree';
+import {WorkingAreaComponent} from '../../working-area/working-area.component'
+import {CanvasShareDataService, DisposalNodeData} from '../../canvas-share-data.service' //引入服务
+import Viewer from 'viewerjs';
+import Swiper from 'swiper';
+import { saveOneDialog } from './save';
+import { NzFormatBeforeDropEvent, NzFormatEmitEvent,NzTreeComponent } from 'ng-zorro-antd/tree';
+import { Observable, of } from 'rxjs';
+import { delay } from 'rxjs/operators';
+import { windows } from 'src/app/interface';
+import { GameMode } from 'src/app/working-area/model/gameMode';
+import { ActivatedRoute, Router } from '@angular/router';
+
+
+
+@Component({
+  selector: 'app-collection-tools',
+  templateUrl: './collection-tools.component.html',
+  styleUrls: ['./collection-tools.component.scss']
+})
+export class CollectionToolsComponent implements OnInit {
+
+  @ViewChild('canvas',{static: true}) canvas:WorkingAreaComponent; //父组件中获得子组件的引用
+
+  constructor(private http:HttpClient,public dialog: MatDialog,public snackBar: MatSnackBar,private element: ElementRef,public canvasData: CanvasShareDataService,private router:Router,private route:ActivatedRoute) { }
+  @ViewChild('nzTreeComponent', { static: false }) nzTreeComponent!: NzTreeComponent;
+  // tree配置
+  private _transformer = (node, level: number) => {//要给渲染节点传那些属性参数
+    return {
+      expandable: !!node.children && node.children.length > 0,
+      name: node.name || node.Name,
+      level: level,
+      id: node.id || node.Id,
+      children:node.children,
+      isTemplate:node.isTemplate,
+      isNewElement:node.isNewElement,
+      isLook:node.isLook,
+      isLookPattern:node.isLookPattern || null
+    };
+  }
+  treeControl = new FlatTreeControl<any>(node => node.level, node => node.expandable);
+  
+  treeFlattener = new MatTreeFlattener(this._transformer, node => node.level, node => node.expandable, node => node.children);
+  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
+  hasChild = (_: number, node: any) => node.expandable;
+
+  colors = ['#076eec','#4dd0e1','#00ee76','#ffff00','#eeb422',
+            '#FF6A6A','#ff0000','#ff6eb4','#00bfff','#54ff9f',
+            '#009688','#836fff','#ff8c00','#ee00ee','#ffa07a',
+            '#00C500','#00ffff','#6495ed','#ffdAb9','#AA00FF']
+  selected = "1" //图标大小选择框
+  
+  allBuildings //该单位所有建筑
+  beforeOneCheckedBuilding:any = {name:"总平面图"}; //当前点击选择的建筑
+  checkedBuildingIndex:number = -1 //当前点击选择的建筑index
+  isEditPat:boolean =  true //当前是否是编辑模式
+
+  assetName:String//素材名称
+  assetWidth:number//素材宽度
+  assetHeight:number//素材高度
+  sliderValue:number = 0//角度滑竿的值
+  sliderValueThickness:number = 0//厚度滑竿的值
+  isHighLight:boolean = false//是否高亮选择框
+  PropertyInfos = [] //去除图片链接真正用于循环的内容
+  imagesArrNum //素材属性图片数量上限
+  imagesArr = [] //属性中的图片链接集合
+  clickedIndex //点击图片时的索引值
+
+  //传入素材对象,设置右侧属性栏内容
+  canvasAssetObj //传入的素材属性对象
+  isImgNumCss = false //控制上传文件input显隐
+  mySwiper:any //轮播图实例
+
+  gallery//viewerJs实例
+  //设置属性框
+  setAssetsProperty(obj){
+    //初始化viewerJs实例
+    setTimeout(() => {
+      obj.PropertyInfos.forEach(item => {
+        if(item.PropertyType == 4){
+          this.gallery = new Viewer(document.getElementById('viewerjs'),{
+            url: 'data-original'
+          });
+        }
+      })
+    }, 0);
+    
+    //从颜色中取出透明度
+    let color = obj.Color
+    let strh = color.substring(color.length -2,color.length)
+    let opacity = Math.round(parseInt(strh,16)/255 * 100) 
+    this.colorDivSliderValue = opacity
+
+    this.isShowProperty = true
+    this.isShowAttribute = false
+    let _this = this
+    this.imagesArr = []
+    this.imagesArrNum = ""
+
+    this.canvasAssetObj = obj //将选中素材对象存到本地变量
+
+    this.assetName = obj.Name
+    this.assetWidth = obj.Width
+    this.assetHeight = obj.Height
+    this.sliderValue = obj.Angle
+    this.selectedcolor = obj.Color
+    this.PropertyInfos = obj.PropertyInfos
+    this.sliderValueThickness = obj.Thickness
+    this.PropertyInfos.forEach(item => {
+      if(item.PropertyType == 3){ //如果是图片链接类型
+        this.imagesArr.push(item)
+      }
+      if(item.PropertyType == 4){//图片数值上线
+        this.imagesArrNum = item.PropertyValue
+      }
+    })
+
+    //如果存在图片则加载轮播图
+    if(this.imagesArr.length){
+      setTimeout(() => {
+        this.mySwiper = new Swiper('.swiper-container',{
+          loop: false,
+          // grabCursor: true,
+          // 如果需要前进后退按钮
+          navigation: {
+            nextEl: '.swiper-button-next',
+            prevEl: '.swiper-button-prev',
+          },
+          on:{
+            click: function(){
+              _this.clickedIndex = this.clickedIndex
+            },
+          }
+        });
+      }, 0);
+    }
+    //判断此时图片数量是否达到上限
+    if(this.imagesArr.length < this.imagesArrNum){//如果不超出
+      this.isImgNumCss = true
+    }else{
+      this.isImgNumCss = false
+    }
+  }
+  pattern:boolean = true//默认为基本信息编辑
+  
+
+  yyy(){
+    console.log(this.canvasData.selectPanelPoint)
+  }
+  //基本信息编辑模式
+  baseInfo(){
+    if (!this.pattern) {
+      this.basicInfo = true
+      this.canvas.setNameVisible(this.basicInfo,1)
+
+      this.pattern = true
+      this.canvasData.gameMode = GameMode.BasicInformation
+      this.canvasData.selectPanelPoint = new DisposalNodeData();
+      this.mateDeleteCustomize()
+      this.getAllLibrary()
+      this.checkedBuildingIndex = -1
+      this.getSitePlan() //总平面图一层
+    }
+  }
+
+  //想定作业编辑模式
+  wantWork(){
+    if (this.pattern) {
+      //让基本信息图标显示
+      this.basicInfo = false
+      this.canvas.setNameVisible(this.basicInfo,0)
+
+      this.pattern = false
+      this.canvasData.gameMode = GameMode.Assignment
+      this.getAllLibrary('plan')
+      this.getDisposalNode()
+    }
+  }
+
+  //ngzorro tree 拖拽
+  nzEvent(event: NzFormatEmitEvent): void {
+    if(this.isDrag){
+      let parentId 
+      if(this.pos == 0){
+        parentId = event.node.key
+      }else{
+        if(event.node.level == 0){
+          parentId = null
+        }else{
+          parentId = event.node.origin.parentId
+        }
+      }
+     
+
+      let orders = {}
+      let originalData = JSON.parse(JSON.stringify( this.canvasData.allDisposalNode || [] )) //tree原始数据
+      let targetNodeData = []//拖动移入节点的数据,用于遍历求出放在该数组的第几位
+
+      //找到需要重新排序的数组
+      if(this.pos == 0){
+        originalData.forEach(item => {
+          if(item.parentId == event.node.key){
+            targetNodeData.push(item)
+          }
+        })
+      }else{
+        if(event.node.origin.parentId){//如果拖动目标为非一级节点
+          originalData.forEach(item => {
+            if(item.parentId == event.node.origin.parentId){
+              targetNodeData.push(item)
+            }
+          })
+        }else{//如果拖动目标为一级节点
+          originalData.forEach(item => {
+            if(!item.parentId){
+              targetNodeData.push(item)
+            }
+          })
+        }
+      }
+      
+      
+      let idArr = []
+      targetNodeData.forEach(i => {
+        idArr.push(i.id)
+      })
+      console.log(6666666666666,event);
+      if(this.pos == 0 && event.node.origin.children.length == 1){
+        console.log("移入,没有兄弟")
+        let key  = event.dragNode.key
+        orders[key] = 0
+        parentId = event.node.key
+      }else{
+        console.log("移入,多个兄弟")
+        let array = []
+        targetNodeData.forEach(item => {
+          if(item.id != event.dragNode.key){ //将拖动项先移除掉
+            array.push(item)
+          }
+        })
+        if(event.dragNode.isEnd[event.dragNode.isEnd.length - 1]){ //如果移入到最后一个
+          console.log("最后")
+          array.push(event.dragNode.origin)
+        }else if(event.dragNode.isStart[event.dragNode.isStart.length - 1]){//如果移入到第一个   
+          console.log("第一")
+          array.unshift(event.dragNode.origin)
+        }else{//如果移入中间位置
+          console.log("中间")
+          array.splice(event.node.origin.order, 0, event.dragNode.origin)
+        }
+        array.forEach((item,key) => {
+          orders[item.id] = key
+        })
+      }
+      
+      let obj ={
+        id : event.dragNode.origin.id,
+        parentId : parentId,
+        orders : orders
+      }
+      
+      this.http.put("/api/DisposalNodes/Sort",obj).subscribe(data => {
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('排序成功','确定',config)
+        this.refurbishTreeData()
+      })
+    }
+  }
+    
+  isDrag //是否可以拖动
+  pos//放置位置
+  beforeDrop = (arg: NzFormatBeforeDropEvent) => {
+    if(arg.dragNode.origin.isDataNode && arg.node.level === 0){//如果为数据节点则不允许拖到一级节点
+      const config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('数据节点不允许拖拽到一级节点','确定',config)
+      this.isDrag = false
+      return of(false);
+    }else if(!arg.dragNode.origin.isDataNode && arg.node.level === 2){
+      const config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('处置节点不允许拖拽到三级节点','确定',config)
+      this.isDrag = false
+      return of(false);
+    }else{
+      this.isDrag = true
+      this.pos = arg.pos
+      return of(true)
+    }
+  }
+  //ngzorro tree 拖拽
+
+  //天气栏目
+  weatherBtn = true
+  weatherBtnShow(){
+    this.weatherBtn = !this.weatherBtn
+  }
+  weatherBtnHidden(){
+    this.weatherBtn = !this.weatherBtn
+  }
+
+  //底部切换按钮div
+  detailsAndattentBtn = true
+  //节点详情
+  details(){
+    this.detailsAndattentBtn = true
+  }
+  //注意事项
+  attent(){
+    this.detailsAndattentBtn = false
+  }
+
+  //消防要素div边框高度调节
+  firecategoriesDivMouseDown(e){
+    document.onmousemove = (ev) => {
+      let bodyHeight = document.body.clientHeight //网页宽度
+      let maxHeight = this.element.nativeElement.querySelector('#rightDiv').clientHeight - 35  //最大宽度
+      if(bodyHeight - ev.clientY >= maxHeight){
+        this.element.nativeElement.querySelector('#firecategories').style.height = maxHeight+ 'px' 
+        this.element.nativeElement.querySelector('#property').style.height = 35+ 'px' 
+      }else{
+        this.element.nativeElement.querySelector('#firecategories').style.height = (bodyHeight - ev.clientY) + 'px'  ;
+        this.element.nativeElement.querySelector('#property').style.height = (this.element.nativeElement.querySelector('#rightDiv').clientHeight - this.element.nativeElement.querySelector('#firecategories').clientHeight) + 'px' 
+      }
+    }
+    document.onmouseup = () => {
+      document.onmousemove = null;
+      document.onmouseup = null;
+    }
+  }
+
+  //左侧div边框宽度调节
+  leftDivMouseDown(e){
+    document.onmousemove = (ev) => {
+      let bodyWidth = document.body.clientWidth //网页宽度
+      let maxWidth = bodyWidth - 260 - this.element.nativeElement.querySelector('#rightDiv').clientWidth  //最大宽度
+      if(ev.clientX - 240 >= maxWidth){
+        this.element.nativeElement.querySelector('#leftDiv').style.width = maxWidth + 'px'
+      }else{
+        this.element.nativeElement.querySelector('#leftDiv').style.width = ev.clientX - 260 + 'px';
+      }
+    }
+    document.onmouseup = () => {
+      document.onmousemove = null;
+      document.onmouseup = null;
+    }
+  }
+
+  //底部div高度调节
+  bottomDivMouseDown(e){
+    document.onmousemove = (ev) => {
+      let bodyHeight = document.body.clientHeight //网页高度
+      let maxHeight = this.element.nativeElement.querySelector('#rightDiv').clientHeight  //最大高度
+      if(bodyHeight - ev.clientY >= maxHeight){
+        this.element.nativeElement.querySelector('#bottomDiv').style.height = maxHeight + 'px' 
+      }else{
+        this.element.nativeElement.querySelector('#bottomDiv').style.height = (bodyHeight - ev.clientY) + 'px'  ;
+      }
+    }
+    document.onmouseup = () => {
+      document.onmousemove = null;
+      document.onmouseup = null;
+    }
+  }
+
+  //右侧div边框宽度调节
+  rightDivMouseDown(e){
+    document.onmousemove = (ev) => {
+      let bodyWidth = document.body.clientWidth //网页宽度
+      let maxWidth = bodyWidth - 240 - this.element.nativeElement.querySelector('#leftDiv').clientWidth  //最大宽度
+      if(bodyWidth - ev.clientX >= maxWidth){
+        this.element.nativeElement.querySelector('#rightDiv').style.width = maxWidth + 'px'
+      }else{
+        this.element.nativeElement.querySelector('#rightDiv').style.width = bodyWidth - ev.clientX + 'px';
+      }
+    }
+    document.onmouseup = () => {
+      document.onmousemove = null;
+      document.onmouseup = null;
+    }
+  }
+
+  //放大图标
+  iconScale(){
+    let number = Number(this.selected)
+    this.canvas.setIconScale(number)
+  }
+
+  //素材宽度输入框改变
+  assetWidthIunput(){
+    this.canvasAssetObj.Width = this.assetWidth
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+
+  //素材高度输入框改变
+  assetHeightIunput(){
+    this.canvasAssetObj.Height = this.assetHeight
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+
+  //素材角度输入框改变
+  assetAngleIunput(){
+    this.canvasAssetObj.Angle = this.sliderValue
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+
+  //素材厚度输入框改变
+  assetThicknessIunput(){
+    this.canvasAssetObj.Thickness = this.sliderValueThickness
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+
+  //素材是否高亮改变----->本地操作行为
+  assetHighLightIunput(){
+
+  }
+
+  //动态属性素材input框值改变
+  assetInputChange(i,e){
+    let index = this.canvasAssetObj.PropertyInfos.findIndex((item)=>{
+      return i.PropertyName == item.PropertyName
+    })
+    this.canvasAssetObj.PropertyInfos[index].PropertyValue = e.target.value
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+
+  //动态属性素材布尔值框改变radio
+  assetRadioChange(i,boolean){
+    let index = this.canvasAssetObj.PropertyInfos.findIndex((item)=>{
+      return i.PropertyName == item.PropertyName
+    })
+    this.canvasAssetObj.PropertyInfos[index].PropertyValue = boolean
+    this.canvasData.isChange = true
+  }
+
+  colorIndex//默认素材颜色
+  selectedcolor//点击选择的颜色
+  //选择素材颜色
+  selectcolor(item,key){
+    //在当前透明度基础上改变颜色
+    this.selectedcolor = item + this.selectedcolor.substring(this.selectedcolor.length-2)
+    this.canvasAssetObj.Color = this.selectedcolor
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+  //颜色选择滑竿的值
+  colorDivSliderValue
+  colorDivSliderChange(){
+    let colorOpacity =  Math.round(255 * this.colorDivSliderValue * 0.01) 
+    //根据滑竿值改变16进制颜色后两位
+    function replacepos(text,start,stop,replacetext){
+   	 let mystr = text.substring(0,stop-1)+replacetext+text.substring(stop+1);
+   	 return mystr;
+    }
+    this.selectedcolor = replacepos(this.selectedcolor,7,8,colorOpacity.toString(16))
+    this.canvasAssetObj.Color = this.selectedcolor
+    this.canvasData.isChange = true
+    this.canvas.refreshIcon(this.canvasAssetObj.Id)
+  }
+
+  //查看图片详情
+  lookImg(){
+    const dialogRef = this.dialog.open(ViewDetailss, {//调用open方法打开对话框并且携带参数过去
+      data: {imagesArr:this.imagesArr,index:this.clickedIndex}
+    });
+    dialogRef.afterClosed().subscribe(data=>{ }); 
+  }
+
+  //上传素材图片
+  selectFile(e){
+    let imgFile = e.target.files[0] || null //上传的文件
+    this.startUploading(imgFile)
+  }
+  objectName:any //上传对象名
+  startUploading (imgFile) {
+    let _this = this
+    let file = imgFile || null //获取上传的文件
+    let fileSize = file.size || null //上传文件的总大小
+    let shardSize = 5 * 1024 * 1024 //5MB一个分片
+    let companyId = sessionStorage.getItem("companyId")
+    if (file && fileSize <= shardSize) { //上传文件<=5MB时
+      let formData = new FormData()
+      formData.append("file",file)
+      this.http.post(`api/Objects/WebPlan2D/${companyId}`,formData).subscribe((data:any)=>{
+        this.objectName = data.objectName
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('上传成功','确定',config)
+
+        //在原始素材对象和需要循环图片的对象中分别push最新上传的图片
+        let imgObj = {
+          "Tag": null,
+          "Order": 0,
+          "Enabled": false,
+          "Visible": false,
+          "Required": false,
+          "RuleName": null,
+          "RuleValue": null,
+          "PhysicalUnit": null,
+          "PropertyName": "图片",
+          "PropertyType": 3,
+          "PropertyValue": "/api/Objects/WebPlan2D/" + this.objectName
+        }
+        
+        this.imagesArr.push(imgObj)
+        this.canvasAssetObj.PropertyInfos.push(imgObj)
+
+        setTimeout(() => {
+          this.mySwiper = new Swiper('.swiper-container',{
+            loop: false,
+            // grabCursor: true,
+            // 如果需要前进后退按钮
+            navigation: {
+              nextEl: '.swiper-button-next',
+              prevEl: '.swiper-button-prev',
+            },
+            on:{
+              click: function(){
+                _this.clickedIndex = this.clickedIndex
+              },
+            }
+          });
+
+          this.mySwiper.slideTo(this.imagesArr.length - 1)
+          this.gallery.update()
+        }, 0);
+        
+        //判断上传素材属性图片是否超出数量  超出数量则隐藏input框
+        if(this.imagesArr.length < this.imagesArrNum){//不超出input才会显示
+          this.isImgNumCss = true
+        }else{
+          this.isImgNumCss = false
+        }
+        
+        this.canvasData.isChange = true
+      })
+    } else if (file && fileSize>shardSize) { //上传文件>5MB时,分块上传
+      let config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('上传图片文件不允许大于5mb','确定',config);
+    }
+
+  }
+
+  //不能上传图片提示
+  imgNumBeyond(){
+    const config = new MatSnackBarConfig();
+    config.verticalPosition = 'top';
+    config.duration = 3000
+    this.snackBar.open('图片数量已达上限','确定',config);
+  }
+
+  //删除素材属性图片
+  deleteImg(){
+    if(this.imagesArr.length == 0){
+      const config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('没有可删除的图片,请先上传','确定',config)
+    }else{
+      // 在素材原始对象中将删除的图片去掉
+      this.canvasAssetObj.PropertyInfos = [...this.canvasAssetObj.PropertyInfos.filter((item)=>{
+        return  item.PropertyValue != this.imagesArr[this.mySwiper.activeIndex].PropertyValue
+      })]
+      //在图片循环数组中将图片去掉
+      this.imagesArr.splice(this.mySwiper.activeIndex, 1);
+      //更新swiper视图
+      setTimeout(() => {
+        this.mySwiper.update();
+        this.gallery.update()
+      }, 0);
+
+      //将上传的input框显示出来
+      this.isImgNumCss = true;
+      //清除图片缓存
+      if((<HTMLInputElement>document.getElementById('inputimg'))){
+        (<HTMLInputElement>document.getElementById('inputimg')).value = null //清空input框缓存
+      }
+
+      this.canvasData.isChange = true
+    }
+  }
+
+  //动态属性方向select选择框
+  direction(i,e){
+    let index = this.canvasAssetObj.PropertyInfos.findIndex((item)=>{
+      return i.PropertyName == item.PropertyName
+    })
+    this.canvasAssetObj.PropertyInfos[index].PropertyValue = e.target.value
+    this.canvasData.isChange = true
+  }
+
+  //动态属性供给区域select选择框
+  supplyArea(i,e){
+    let index = this.canvasAssetObj.PropertyInfos.findIndex((item)=>{
+      return i.PropertyName == item.PropertyName
+    })
+    this.canvasAssetObj.PropertyInfos[index].PropertyValue = e.target.value
+    this.canvasData.isChange = true
+  }
+
+  //动态属性供给类型select选择框
+  supplyType(i,e){
+    let index = this.canvasAssetObj.PropertyInfos.findIndex((item)=>{
+      return i.PropertyName == item.PropertyName
+    })
+    this.canvasAssetObj.PropertyInfos[index].PropertyValue = e.target.value
+    this.canvasData.isChange = true
+  }
+
+
+  isSixShow = true  
+  isSixbtn = true //控制想定作业编辑按钮
+  isxxx = true //控制查看编辑模式的编辑模式按钮
+
+  ngOnInit(): void {
+
+    if(this.router.url.indexOf("keyUnit/viewunitinfoplan") == -1 && this.router.url.indexOf("keyUnit/viewunitinfo") != -1 || this.router.url.indexOf("keyUnit/editplaninfo")!= -1){
+      this.isSixbtn = false
+    }
+
+    if(!this.isSixbtn && sessionStorage.getItem("six") == "edit"){
+      this.isSixShow = true
+    }
+    if(!this.isSixbtn && sessionStorage.getItem("six") == "look"){
+      this.isEditPattern = false
+      this.isxxx = false
+    }
+
+    if (sessionStorage.getItem('editable') == "0") {
+      this.isEditPattern = false
+      this.isxxx = false
+    }
+
+    this.getAllLibrary() //获取素材库
+    this.getAllBuildings() //获取所有建筑
+    this.getAllFirePlan() //获取当前单位灾情
+
+    let that = this
+    window.setTimeout(()=>{
+      document.getElementById("functionalDomainContent").oncontextmenu = function (event) {
+        // that.canvas.cancelPaint()
+        that.selectImageIndex = -1
+        event.preventDefault();
+      };
+    })
+    
+    this.canvasData.getMessage().subscribe((message: any)=>{
+      if(message == "send a message"){
+        this.refurbishTreeData()
+      }
+    })
+  }
+
+
+
+  ngAfterViewInit(): void {
+    
+    this.getSitePlan()
+    // 监听canvas组件选中素材事件
+    this.canvas.on("select",obj=>{
+      //选中素材属性注入函数
+      this.setAssetsProperty(obj.assetData)
+    })
+    // 监听canvas组件取消选中素材事件
+    this.canvas.on("deselect",obj=>{
+      this.isShowProperty = false
+    })
+    // 监听canvas组件新增素材事件
+    this.canvas.on("createIcon",obj=>{
+      this.renovateTreeData(false)
+    })
+    // 监听canvas组件删除素材事件
+    this.canvas.on("deleteIcon",obj=>{
+      this.renovateTreeData(false)
+    })
+
+  }
+
+  copyAssetData:any //存储用于复制的素材
+  //复制素材
+  copyAsset(){
+    this.canvas.copy()
+  }
+  //粘贴素材
+  pasteAsset(){
+    let companyId = sessionStorage.getItem("companyId")
+    let buildingId = this.beforeOneCheckedBuilding.id
+    let floorId = this.selectingSitePlan.id
+    this.canvas.paste(companyId,buildingId,floorId)
+  }
+
+  basicInfo:boolean = true //基本信息名称显隐
+  wantToWork:boolean = true //想定作业名称显隐
+  //点击基本信息名称
+  basicInfoClick(){
+    this.basicInfo = !this.basicInfo
+    this.canvas.setNameVisible(this.basicInfo,0)
+  }
+
+  //点击想定作业名称
+  wantToWorkClick(){
+    this.wantToWork = !this.wantToWork
+    this.canvas.setNameVisible(this.wantToWork,1)
+  }
+
+  isEditPattern:boolean = true //是否为编辑模式
+  //进入编辑模式
+  editpat(){
+    let config = new MatSnackBarConfig();
+    config.verticalPosition = 'top';
+    config.duration = 3000
+    this.snackBar.open('进入编辑模式','确定',config);
+    this.isEditPattern = true
+  }
+
+  //进入查看模式
+  lookpat(){
+    let config = new MatSnackBarConfig();
+    config.verticalPosition = 'top';
+    config.duration = 3000
+    this.snackBar.open('进入查看模式','确定',config);
+    this.isEditPattern = false
+  }
+
+  //保存平面图
+  saveNum :any = []
+  saveSite(){
+    if (this.selectingSitePlan && this.selectingSitePlan.id) {
+      this.saveNum = []
+      let SitePlanData = JSON.parse(JSON.stringify(this.canvasData.originaleveryStoreyData));
+      SitePlanData.data = JSON.stringify(SitePlanData.data)
+      let CompanyData = JSON.parse(JSON.stringify(this.canvasData.originalcompanyBuildingData));
+      CompanyData.data = JSON.stringify(CompanyData.data)
+  
+      let object = this.canvasData.originalcompanyBuildingData.data
+      let adjoinArr = [] //毗邻数组
+  
+      if(this.pattern){//如果是基本信息编辑模式
+        for (const key in object) {
+          if (object[key].Name == "毗邻") {//如果是相同楼层,则筛选出毗邻
+            
+            object[key].PropertyInfos.forEach(element => {
+              if(element.PropertyName == "方向"){
+                adjoinArr.push(element.PropertyValue)
+              }
+            });
+          }
+        }
+        
+        if((new Set(adjoinArr)).size != adjoinArr.length){
+          let config = new MatSnackBarConfig();
+          config.verticalPosition = 'top';
+          config.duration = 3000
+          this.snackBar.open('保存失败,毗邻存在相同方向','确定',config);
+          return false
+        }else{
+          //如果是单位 总平面图
+          if(this.checkedBuildingIndex==-1){
+            //保存平面图数据
+            this.http.post("/api/SitePlanData",SitePlanData,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('平面图数据保存失败','确定',config);
+            })
+    
+            //保存建筑数据
+            this.http.post("/api/CompanyData",CompanyData,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('单位数据保存失败','确定',config);
+            })
+    
+            //批量保存单位毗邻
+            let CompanyAdjoins = this.canvasData.getCompanyAdjoinInfo()
+            this.http.post("/api/CompanyAdjoins/Batch",CompanyAdjoins,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('单位毗邻保存失败','确定',config);
+            })
+    
+            //批量保存单位重点部位
+            let CompanyImportantLocations = this.canvasData.getCompanyImportantLocations()
+            this.http.post("/api/CompanyImportantLocations/Batch",CompanyImportantLocations,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('单位重点部位保存失败','确定',config);
+            })
+    
+            //批量保存单位消防设施素材
+            let CompanyFacilityAssets = this.canvasData.getAllCompanyFacilityAssetInfo()
+            this.http.post("/api/CompanyFacilityAssets/Batch",CompanyFacilityAssets,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('单位消防设施素材保存失败','确定',config);
+            })
+    
+          }else{ //如果是建筑
+    
+            //建筑平面图数据
+            this.http.post("/api/BuildingAreaData",SitePlanData,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('平面图数据保存失败','确定',config);
+            })
+    
+            //建筑数据
+            this.http.post("/api/BuildingData",CompanyData,{
+              params:{
+                companyId:this.params.companyId
+              }
+            }).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('单位数据保存失败','确定',config);
+            })
+    
+            //批量保存建筑毗邻
+            let buildingAdjoins = this.canvasData.getBuildingAdjoinInfo()
+            this.http.post(`/api/BuildingAdjoins/Batch?companyId=${this.params.companyId}&buildingId=${this.canvasData.selectStorey.buildingId}`,buildingAdjoins).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('建筑毗邻保存失败','确定',config);
+            })
+    
+            //批量保存建筑重点部位
+            let buildingImportantLocations = this.canvasData.getBuildingImportantLocations()
+            this.http.post(`/api/BuildingImportantLocations/Batch?companyId=${this.params.companyId}&buildingId=${this.canvasData.selectStorey.buildingId}`,buildingImportantLocations).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('建筑重点部位保存失败','确定',config);
+            })
+    
+            //批量保存建筑消防设施素材
+            let buildingFacilityAssets = this.canvasData.getAllBuildingFacilityAssetInfo()
+            this.http.post(`/api/BuildingFacilityAssets/Batch?companyId=${this.params.companyId}&buildingId=${this.canvasData.selectStorey.buildingId}`,buildingFacilityAssets).subscribe(data => {
+              this.saveNum.push("1")
+              if(this.saveNum.length == 5){
+                this.canvasData.isChange = false
+                let config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config);
+              }
+            },err=>{
+              let config = new MatSnackBarConfig();
+              config.verticalPosition = 'top';
+              config.duration = 3000
+              this.canvasData.isChange = true
+              this.snackBar.open('建筑消防设施素材保存失败','确定',config);
+            })
+    
+          }
+        }
+      }else{//如果是想定作业编辑模式
+        const dialogRef = this.dialog.open(saveOneDialog, {
+          data: {allDisposalNode: this.canvasData.allDisposalNode,
+                selectedBuildingData:this.beforeOneCheckedBuilding,
+                selectedSiteData:this.selectingSitePlan,
+                siteOrbuilding:this.checkedBuildingIndex,
+                disasterId: this.allFirePlan[0].id || ''
+                }
+        });
+    
+        dialogRef.afterClosed().subscribe(result => {
+          console.log('The dialog was closed');
+        });
+      }
+
+    } else { //if
+      const config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('暂无楼层数据','确定',config);
+    }
+  }
+
+  //获得所有的建筑物
+  getAllBuildings(){
+    this.http.get("/api/Buildings",{
+      params:{
+        companyId : this.params.companyId
+      }
+    }).subscribe(data=>{
+      this.allBuildings = data
+    })
+  }
+
+  //拖拽tree
+  drop(e){
+    console.log(1111,e)
+  }
+  drop2(e){
+    console.log(222,e)
+  }
+
+  //创建建筑
+  createBuilding(){
+    let data = {
+      allBuildings:this.allBuildings,
+      companyId :this.params.companyId
+    }
+    let dialogRef = this.dialog.open(CreateBuilding,{data});
+    dialogRef.afterClosed().subscribe(data=>{
+      if (data == "创建成功") {
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('创建成功','确定',config);
+        this.getAllBuildings()
+      }else if (data == "创建失败") {
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('创建失败','确定',config);
+      }
+    });  
+  }
+
+  //选择建筑
+  checkedBuilding(item,index){
+    if (this.checkedBuildingIndex!=index) {
+
+      if (this.canvasData.isChange) { //true 数据被改动
+        let isTrue = confirm('是否保存当前编辑数据')
+        if (isTrue) { //先保存数据 在切换
+          let isSuccess = this.saveSite()//true的时候 先保存数据
+          if (isSuccess != false) {
+            this.beforeOneCheckedBuilding = item
+            this.checkedBuildingIndex = index
+            if (index==-1) { //总平面图数据
+              this.getSitePlan()
+            } else { //建筑楼层/区域数据
+              this.getBuildingSitePlan(item)
+            }
+          }
+        } else {
+          this.beforeOneCheckedBuilding = item
+          this.checkedBuildingIndex = index
+          if (index==-1) { //总平面图数据
+            this.getSitePlan()
+          } else { //建筑楼层/区域数据
+            this.getBuildingSitePlan(item)
+          }
+        }
+      } else { //flase 数据未被改动
+        this.beforeOneCheckedBuilding = item
+        this.checkedBuildingIndex = index
+        if (index==-1) { //总平面图数据
+          this.getSitePlan()
+        } else { //建筑楼层/区域数据
+          this.getBuildingSitePlan(item)
+        }
+      } //if
+
+    } //if
+  }
+
+  //编辑建筑
+  editBuilding(e,item){
+    e.stopPropagation();
+    let dialogRef = this.dialog.open(EditBuilding,{data: {item:item}});
+    dialogRef.afterClosed().subscribe(data=>{
+      if (data == "修改成功") {
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('修改成功','确定',config);
+        this.getAllBuildings()
+      }else if (data == "修改失败") {
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('修改失败','确定',config);
+      }
+    });  
+  }
+
+  //删除建筑
+  deleteBuilding(e,item){
+    e.stopPropagation();
+    if(confirm("是否删除该建筑") == true){
+      let isHave = this.canvasData.allDisposalNode.find(items=>{ return items.buildingId === item.id })
+      if (isHave == undefined) {
+        this.http.delete(`/api/Buildings/${item.id}`).subscribe(data=>{
+          const config = new MatSnackBarConfig();
+          config.verticalPosition = 'top';
+          config.duration = 3000
+          this.snackBar.open('删除成功','确定',config);
+          this.http.get("/api/Buildings",{
+            params:{
+              companyId :this.params.companyId
+            }
+          }).subscribe(data=>{
+            this.allBuildings = data
+            this.beforeOneCheckedBuilding = {name:"总平面图"}
+            this.checkedBuildingIndex = -1
+            this.getSitePlan()
+          })
+        },err=>{
+          const config = new MatSnackBarConfig();
+          config.verticalPosition = 'top';
+          config.duration = 3000
+          this.snackBar.open('删除失败','确定',config);
+          this.getAllBuildings()
+        })
+      } else { //建筑 含有数据节点时
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('含有数据节点的建筑不允许删除','确定',config);
+      }
+    }
+  }
+
+  storeyData //将建筑素材和当前楼层素材合二为一
+
+  //处理 tree 数据结构
+  handleTreeData (storeyData) {
+
+    this.storeyData = storeyData
+    let data = this.allFireElements //所有消防要素模板
+    let treeData = [] //tree型 处理完成后的数据
+    data.forEach(element => {
+      element.isTemplate = true //添加模板标识
+      element.isLook = true //添加是否可见标识
+      element.name!='其他'? element.children = [] : null
+
+      if(storeyData){
+        for(let key in storeyData.data){
+          storeyData.data[key].isLookPattern = true
+          if(element.id == storeyData.data[key].FireElementId){
+            storeyData.data[key].isTemplate = false
+            storeyData.data[key].isLook = true
+            
+            element.isNewElement = true //该节点children是否存在新添加的真实素材 标识
+
+            //定义查看模式下能看到的元素
+            element.isLookPattern = true
+            if(element.parentId){
+              data.forEach(i => {
+                if(i.id == element.parentId){
+                  i.isLookPattern = true
+                }
+              })
+            }
+            //
+
+            element.children.push(storeyData.data[key])
+          }
+        }
+      }
+      data.forEach(item => { if (item.parentId == element.id) {element.children.push(item)} });
+    });
+    data.forEach(element => {
+      if (!element.parentId) { treeData.push(element) }
+    });
+    this.dataSource.data = treeData
+    this.treeControl.expandAll()
+    
+  }
+
+  //点击树节点
+  clickTreeNode(node){
+    if(this.canvasData.originalcompanyBuildingData.data[node.id]){
+      this.setAssetsProperty(this.canvasData.originalcompanyBuildingData.data[node.id])
+    }else if(this.canvasData.originaleveryStoreyData.data[node.id]){
+      this.setAssetsProperty(this.canvasData.originaleveryStoreyData.data[node.id])
+    }
+
+    //canvas上的素材高亮
+    let iconHighLightArr:any = []
+    if(node.isTemplate){//如果是模板,则开始向下找
+      node.children.forEach(item => {
+        if(item.isTemplate){//如果子节点依旧是模板,则继续开始向下找
+          item.children.forEach(i => {
+            iconHighLightArr.push(i.Id)
+          })
+        }else{
+          iconHighLightArr.push(item.Id)
+        }
+      })
+    }else{
+      iconHighLightArr.push(node.id)
+    }
+    this.canvas.setHighlight(iconHighLightArr)
+  }
+
+  //点击数节点的显示隐藏icon
+  clickLookItem(node){
+
+    //修改真实素材islook属性
+    for(let key in this.storeyData.data){
+      if(key == node.id){
+        this.storeyData.data[key].isLook = !this.storeyData.data[key].isLook
+      }
+    }
+    
+    //所有消防要素模板变化islook值
+    if(node.isTemplate){
+      this.allFireElements.forEach(item=>{
+        if(item.id == node.id || item.name == "其他"){
+          item.isLook = !item.isLook
+        }
+      })
+    }
+    
+
+    //子节点跟随父节点的islook变化
+    if(node.children && node.children.length != 0){
+      node.children.forEach(item=>{
+        item.isLook = !node.isLook 
+        if(item.children && item.children.length != 0){
+          item.children.forEach(i=>{
+            i.isLook =  !node.isLook 
+          })
+        }
+      })
+     }
+
+
+    const nodes = this.treeControl.dataNodes;
+    const expandNodes = [];
+    nodes.forEach((item) => {
+      if(item.expandable && this.treeControl.isExpanded(item)){
+        expandNodes.push(item.id);
+      }
+    });
+    
+    this.dataSource.data = [...this.dataSource.data]
+    
+    let newNodes = this.treeControl.dataNodes;
+    newNodes = newNodes.filter(n => {
+      return expandNodes.indexOf(n.id) >= 0;
+    });
+    newNodes.forEach(item => {
+      this.treeControl.expand(item);
+    });
+
+    //canvas上的素材显隐
+    let iconVisibleArr:any = []
+    if(node.isTemplate){//如果是模板,则开始向下找
+      node.children.forEach(item => {
+        if(item.isTemplate){//如果子节点依旧是模板,则继续开始向下找
+          item.children.forEach(i => {
+            iconVisibleArr.push(i.Id)
+          })
+        }else{
+          iconVisibleArr.push(item.Id)
+        }
+      })
+    }else{
+      iconVisibleArr.push(node.id)
+    }
+    this.canvas.setIconVisible(iconVisibleArr,!node.isLook)
+  }
+
+  //计算 可视区域内宽度, 是否缩放背景图
+  backGroundScale () {
+    // let that = this
+    // let dad = this.element.nativeElement.querySelector('.functionalDomainContent').clientWidth
+    // let dadHeight = this.element.nativeElement.querySelector('.functionalDomainContent').clientHeight
+    // let left = this.element.nativeElement.querySelector('.functionalDomainLeft').clientWidth
+    // let right = this.element.nativeElement.querySelector('.functionalDomainRight').clientWidth
+    // let imgWidth = dad - left - right//可视区域内 宽度
+    // let img = new Image()
+    // img.src = this.selectingSitePlan.imageUrl;
+    // img.onload = function(){
+    //   if (img.height > dadHeight && img.width > imgWidth) {
+    //     let width = imgWidth/img.width
+    //     let height = dadHeight/img.height
+    //     that.canvas.setBackgroundScale((width>height? height : width)-0.005)
+    //     return
+    //   } else if (img.height > dadHeight) {
+    //     that.canvas.setBackgroundScale((dadHeight/img.height)-0.005)
+    //     return
+    //   } else if (img.width > imgWidth) {
+    //     that.canvas.setBackgroundScale((imgWidth/img.width)-0.005)
+    //     return
+    //   }
+    // };
+  }
+
+  //封装 刷新 tree 数据
+ async renovateTreeData (isRefresh:boolean = true) {
+    this.allFireElements[this.allFireElements.length-1].children = []
+    isRefresh? await this.canvas.refresh() : null
+    this.canvas.setNameVisible(this.basicInfo,0)
+    this.canvas.setNameVisible(this.wantToWork,1)
+    isRefresh? this.canvasData.isChange = false : null //服务中 数据是否改动 改为false
+    isRefresh? this.isShowProperty = true : null
+    isRefresh? this.isShowAttribute = true : null
+    !this.pattern? this.mateFireForce() : null //刷新 建筑楼层 火源/力量图标
+
+    let beforeOneId = this.selectingSitePlan.id || '' //当前 选中 平面图 楼层/区域 id
+    let companyBuildingData = JSON.parse(JSON.stringify( this.canvasData.originalcompanyBuildingData || {} )) // 当前 单位/建筑 数据 
+    let storeyData = JSON.parse(JSON.stringify( this.canvasData.originaleveryStoreyData || {} )) //当前 楼层 数据
+
+    for(let key in companyBuildingData.data){
+      if (companyBuildingData.data[key].FloorId === beforeOneId) { //处理 单位/建筑 数据是否归于当前楼层下
+        storeyData.data[key] = companyBuildingData.data[key]
+      }
+    }
+    for(let key in storeyData.data){ //筛选数据 没有匹配全部放入到   其他   数组
+      let noMatch = this.allFireElements.find( every=> every.id===storeyData.data[key].FireElementId )
+      if (!noMatch) {
+        this.allFireElements[this.allFireElements.length-1].children.push(storeyData.data[key])
+      }
+    }
+    this.handleTreeData(storeyData) //处理tree数据结构
+  }
+
+
+
+  //陈鹏飞↓↓↓
+  //陈鹏飞↓↓↓
+  //陈鹏飞↓↓↓
+  params = {companyId: sessionStorage.getItem('companyId')}
+  allFireElements:any = []; //当前 单位/建筑 下的消防要素
+
+  isShowAttribute:boolean = true; //属性栏 是否显示 默认数据
+  isShowProperty:boolean = false //属性栏 是否有东西
+
+  toggleExpandPanel:boolean = false; //左侧可展开面板展开或关闭
+  toggleExpandPanelRight:boolean = false; //右侧可展开面板展开或关闭
+  togglePlane:boolean = true; //可展开面板平面图 显隐
+  toggleMaterialBank:boolean = false; //可展开面板素材库 显隐
+  toggleHandlePlans:boolean = true; //可展开面板处置预案 显隐
+  //可展开面板展开或关闭
+  toggle () {
+    this.toggleExpandPanel = !this.toggleExpandPanel
+  }
+  //可展开面板展开或关闭
+  toggle2 () {
+    this.toggleExpandPanelRight = !this.toggleExpandPanelRight
+  }
+  //可展开面板 平面图 展开或关闭
+  togglePlanarGraph () {
+    this.togglePlane = !this.togglePlane
+  }
+  //可展开面板 素材库 展开或关闭
+  toggleMaterial () {
+    this.toggleMaterialBank = !this.toggleMaterialBank
+  }
+  //可展开面板 处置预案 展开或关闭
+  toggleHandlePlan () {
+    this.toggleHandlePlans = !this.toggleHandlePlans
+  }
+
+  sitePlanData:any = []; //总平面图 楼层/区域 数据
+  selectingSitePlan:any; //选中的 平面图 楼层/区域
+  selectSitePlanIndex:number; //选中的 平面图 楼层/区域 index
+
+  //获取总平面图
+  getSitePlan () {
+    let fireData = this.getFireElements(sessionStorage.getItem('buildingTypeId')) //获取单位下 消防要素
+    let planData = this.getSitePlanCompanyData() //获取 单位 数据
+    this.http.get('/api/SitePlans',{params:this.params}).subscribe(data=>{
+      this.sitePlanData = data
+      this.selectingSitePlan = this.sitePlanData[0] || {}
+      this.canvasData.selectStorey = this.sitePlanData[0] || {} //服务中 存一份数据
+      this.selectSitePlanIndex = 0
+
+      Promise.all([fireData,planData]).then((res)=>{
+        this.getSitePlanStorey(this.selectingSitePlan) //获取 平面图 楼层数据
+      })
+
+    })
+  }
+
+  //获取建筑 楼层/区域
+  getBuildingSitePlan (item) {
+    let params = { buildingId: item.id }
+    let fireData = this.getFireElements(item.buildingTypes[0].id || '') //获取建筑下 消防要素
+    let planData = this.getBuildingData(params) //获取 建筑 数据
+    this.http.get('/api/BuildingAreas',{params}).subscribe(data=>{
+      this.sitePlanData = data
+      this.selectingSitePlan = this.sitePlanData[0] || {}
+      this.canvasData.selectStorey = this.sitePlanData[0] || {} //服务中 存一份数据
+      this.selectSitePlanIndex = 0
+
+      Promise.all([fireData,planData]).then((res)=>{
+        this.getBuildingStorey(this.selectingSitePlan) //获取 建筑 楼层数据
+      })
+
+    })
+  }
+
+  //根据单位类型获得所有的消防要素
+   getFireElements (e) {
+    let params = {ids:e}
+    return new Promise ((resolve,reject)=>{
+      this.http.get('/api/Companies/FireElements',{params}).subscribe((data:any)=>{
+        this.allFireElements = data //所有消防要素
+        let other = {
+          children: [],
+          computed: true,
+          id: '',
+          name: '其他',
+          order: 999,
+          parentId: null,
+          tag: "INPUT",
+          isLookPattern : true
+        }
+        this.allFireElements.push(other)
+        resolve('success')
+      })
+    })
+  }
+
+  //获取 单位 数据
+  getSitePlanCompanyData () {
+    return new Promise ((resolve,reject)=>{
+      this.http.get('/api/CompanyData',{params:this.params}).subscribe((data:any)=>{
+        this.canvasData.originalcompanyBuildingData = data || {} // 单位原数据
+        this.canvasData.originalcompanyBuildingData.data? this.canvasData.originalcompanyBuildingData.data = JSON.parse(this.canvasData.originalcompanyBuildingData.data) : this.canvasData.originalcompanyBuildingData.data = {}
+        this.canvasData.originalcompanyBuildingData.version? null : this.canvasData.originalcompanyBuildingData.version = "2.0"
+        this.canvasData.originalcompanyBuildingData.companyId? null : this.canvasData.originalcompanyBuildingData.companyId = sessionStorage.getItem('companyId')
+        resolve('success')
+      })
+    })
+  }
+
+  //获取 平面图 楼层数据
+  getSitePlanStorey (e) {
+    let params = {sitePlanId: e.id}
+    this.http.get(`/api/SitePlanData`,{params}).subscribe((data:any)=>{
+      this.canvasData.originaleveryStoreyData = data || {} // 楼层原数据
+      this.canvasData.originaleveryStoreyData.data? this.canvasData.originaleveryStoreyData.data = JSON.parse(this.canvasData.originaleveryStoreyData.data) : this.canvasData.originaleveryStoreyData.data = {}
+      this.canvasData.originaleveryStoreyData.version? null : this.canvasData.originaleveryStoreyData.version = "2.0"
+      this.canvasData.originaleveryStoreyData.sitePlanId? null : this.canvasData.originaleveryStoreyData.sitePlanId = e.id || null
+      this.renovateTreeData()
+    })
+  }
+
+  //获取 建筑 数据
+  getBuildingData (e) {
+    return new Promise ((resolve,reject)=>{
+      this.http.get(`/api/BuildingData`,{params:e}).subscribe((data:any)=>{
+        this.canvasData.originalcompanyBuildingData = data || {} // 建筑原数据
+        this.canvasData.originalcompanyBuildingData.data? this.canvasData.originalcompanyBuildingData.data = JSON.parse(this.canvasData.originalcompanyBuildingData.data) : this.canvasData.originalcompanyBuildingData.data = {}
+        this.canvasData.originalcompanyBuildingData.version? null : this.canvasData.originalcompanyBuildingData.version = "2.0"
+        this.canvasData.originalcompanyBuildingData.buildingId? null : this.canvasData.originalcompanyBuildingData.buildingId = e.buildingId
+        resolve('success')
+      })
+    })
+  }
+
+  //获取 建筑 楼层数据
+  getBuildingStorey (e) {
+    let params = {buildingAreaId: e.id}
+    this.http.get(`/api/BuildingAreaData`,{params}).subscribe((data:any)=>{
+      this.canvasData.originaleveryStoreyData = data || {} // 楼层原数据
+      this.canvasData.originaleveryStoreyData.data? this.canvasData.originaleveryStoreyData.data = JSON.parse(this.canvasData.originaleveryStoreyData.data) : this.canvasData.originaleveryStoreyData.data = {}
+      this.canvasData.originaleveryStoreyData.version? null : this.canvasData.originaleveryStoreyData.version = "2.0"
+      this.canvasData.originaleveryStoreyData.buildingAreaId? null : this.canvasData.originaleveryStoreyData.buildingAreaId = e.id || null
+      this.renovateTreeData()
+    })
+  }
+
+  //点击选中 平面图 楼层/区域 时
+  selectSitePlan (item,index) {
+    
+    if (this.selectSitePlanIndex != index) {
+      this.canvasData.selectPanelPoint = new DisposalNodeData();
+      if (this.canvasData.isChange) { //true 数据被改动
+        let isTrue = confirm('是否保存当前编辑数据')
+        if (isTrue) { //先保存数据 在切换
+          let isSuccess = this.saveSite()//true的时候 先保存数据
+          if (isSuccess != false) {
+            this.selectingSitePlan = item
+            this.selectSitePlanIndex = index
+            this.canvasData.selectStorey = item //服务中 存一份数据
+            if (this.checkedBuildingIndex==-1) {  //总平面图时
+              this.getSitePlanStorey(item) //获取 平面图 楼层数据
+            } else {  //楼层/区域时
+              this.getBuildingStorey(item) //获取 建筑 楼层数据
+            }
+          }
+        } else { //不保存数据 直接切换
+          this.selectingSitePlan = item
+          this.selectSitePlanIndex = index
+          this.canvasData.selectStorey = item //服务中 存一份数据
+          if (this.checkedBuildingIndex==-1) {  //总平面图时
+            this.getSitePlanStorey(item) //获取 平面图 楼层数据
+            this.getSitePlanCompanyData()
+          } else {  //楼层/区域时
+            this.getBuildingStorey(item) //获取 建筑 楼层数据
+            let params = { buildingId: this.beforeOneCheckedBuilding.id }
+            this.getBuildingData(params)
+          }
+        }
+  
+      } else { //false 数据没被改动
+        this.selectingSitePlan = item
+        this.selectSitePlanIndex = index
+        this.canvasData.selectStorey = item //服务中 存一份数据
+        if (this.checkedBuildingIndex==-1) {  //总平面图时
+          this.getSitePlanStorey(item) //获取 平面图 楼层数据
+        } else {  //楼层/区域时
+          this.getBuildingStorey(item) //获取 建筑 楼层数据
+        }
+      } //if
+
+    }
+  }
+
+  //新增平面图 楼层/区域
+  foundPanel (e) {
+    e.stopPropagation()
+    let data = {
+      isBuilding: this.checkedBuildingIndex==-1? false:true,
+      Panel: this.beforeOneCheckedBuilding,
+      order: this.sitePlanData.length? this.sitePlanData[this.sitePlanData.length-1].order+1:0,
+    }
+    let dialogRef = this.dialog.open(leftFunctionalDomainComponent,{data});
+    dialogRef.afterClosed().subscribe(data=>{
+      if (data =='总平面图') {
+        this.renovateSitePlan()
+      } else if (data =='建筑') {
+        this.renovateBuilding()
+      }
+    })
+  }
+
+  //编辑平面图 楼层/区域
+  editPlaneData (e) {
+    let data = {
+      isBuilding: this.checkedBuildingIndex==-1? false:true,
+      Panel: this.beforeOneCheckedBuilding,
+      buildingData: e,
+    }
+    let dialogRef = this.dialog.open(editPlaneFigureComponent,{data});
+    dialogRef.afterClosed().subscribe(data=>{
+      if (data =='总平面图') {
+        this.renovateSitePlan()
+      } else if (data =='建筑') {
+        this.renovateBuilding()
+      }
+    })
+  }
+
+  //平面图 楼层/区域 上移
+  moveUp (item,index) {
+    if (index != 0) {
+      let replaceIndex = this.sitePlanData[index].order
+      this.sitePlanData[index].order = this.sitePlanData[index-1].order
+      this.sitePlanData[index-1].order = replaceIndex
+      if (this.checkedBuildingIndex==-1) { //总平面图
+        this.http.put(`/api/SitePlans/${this.sitePlanData[index-1].id}`,this.sitePlanData[index-1]).subscribe(data=>{
+          this.http.put(`/api/SitePlans/${this.sitePlanData[index].id}`,this.sitePlanData[index]).subscribe(data=>{
+            this.selectSitePlanIndex = this.selectSitePlanIndex-1
+            this.renovateSitePlan()
+          })
+        })
+      } else { //楼层/区域
+        this.http.put(`/api/BuildingAreas/${this.sitePlanData[index-1].id}`,this.sitePlanData[index-1],{params:this.params}).subscribe(data=>{
+          this.http.put(`/api/BuildingAreas/${this.sitePlanData[index].id}`,this.sitePlanData[index],{params:this.params}).subscribe(data=>{
+            this.selectSitePlanIndex = this.selectSitePlanIndex-1
+            this.renovateBuilding()
+          })
+        })
+      }
+
+    } //if index
+  }
+
+  //平面图 楼层/区域 下移
+  moveDown (item,index) {
+    if (index != this.sitePlanData.length-1) {
+      let replaceIndex = this.sitePlanData[index].order
+      this.sitePlanData[index].order = this.sitePlanData[index+1].order
+      this.sitePlanData[index+1].order = replaceIndex
+      if (this.checkedBuildingIndex==-1) { //总平面图
+        this.http.put(`/api/SitePlans/${this.sitePlanData[index+1].id}`,this.sitePlanData[index+1]).subscribe(data=>{
+          this.http.put(`/api/SitePlans/${this.sitePlanData[index].id}`,this.sitePlanData[index]).subscribe(data=>{
+            this.selectSitePlanIndex = this.selectSitePlanIndex+1
+            this.renovateSitePlan()
+          })
+        })
+      } else { //楼层/区域
+        this.http.put(`/api/BuildingAreas/${this.sitePlanData[index+1].id}`,this.sitePlanData[index+1],{params:this.params}).subscribe(data=>{
+          this.http.put(`/api/BuildingAreas/${this.sitePlanData[index].id}`,this.sitePlanData[index],{params:this.params}).subscribe(data=>{
+            this.selectSitePlanIndex = this.selectSitePlanIndex+1
+            this.renovateBuilding()
+          })
+        })
+      }
+
+    } //if index
+  }
+
+  //旋转底图
+  revolveImg (item) {
+    item.imageAngle==270? item.imageAngle = 0 : item.imageAngle = item.imageAngle+90
+    if (this.checkedBuildingIndex==-1) { //总平面图
+      this.http.put(`/api/SitePlans/${item.id}`,item).subscribe(data=>{
+        let isSuccess = this.renovateSitePlan()
+        isSuccess.then(res=>{
+          this.canvas.refreshBackgroundImage()
+        })
+      })
+    } else { //楼层/区域
+      this.http.put(`/api/BuildingAreas/${item.id}`,item,{params:this.params}).subscribe(data=>{
+        let isSuccess = this.renovateBuilding()
+        isSuccess.then(res=>{
+          this.canvas.refreshBackgroundImage()
+        })
+      })
+    }
+
+  }
+
+  //删除 平面图 楼层/区域
+  deletePlaneData (item) {
+    const isDelete = confirm('您确定要删除吗');
+    if (isDelete) {
+      if (this.checkedBuildingIndex==-1) { //总平面图
+        let isHave = this.canvasData.allDisposalNode.find(items=>{ return items.sitePlanId === item.id })
+        if (isHave == undefined) {
+          this.http.delete(`/api/SitePlans/${item.id}`).subscribe(data=>{
+            this.deleteShareData(item,-1)
+          })
+        } else {
+          const config = new MatSnackBarConfig();
+          config.verticalPosition = 'top';
+          config.duration = 3000
+          this.snackBar.open('含有数据节点的楼层不允许删除','确定',config);
+        }
+      } else { //楼层/区域
+        let isHave = this.canvasData.allDisposalNode.find(items=>{ return items.buildingAreaId === item.id })
+        if (isHave == undefined) {
+          this.http.delete(`/api/BuildingAreas/${item.id}`).subscribe(data=>{
+            this.deleteShareData(item,1)
+          })
+        } else {
+          const config = new MatSnackBarConfig();
+          config.verticalPosition = 'top';
+          config.duration = 3000
+          this.snackBar.open('含有数据节点的楼层不允许删除','确定',config);
+        }
+      }
+    }
+  }
+
+  //删除当前 单位/建筑的 共享数据中 已删除data
+  deleteShareData (e,isCompany) {
+    this.sitePlanData.forEach((element,index) => {
+      if (element.id===e.id) {
+        this.sitePlanData.splice(index,1)
+        return
+      }
+    });
+    let data = this.canvasData.originalcompanyBuildingData;
+    for(let key in data.data){
+      if (data.data[key].FloorId === e.id) { //处理 单位/建筑 数据是否归于当前楼层下
+        delete data.data[key]
+      }
+    }
+    let newData = JSON.parse(JSON.stringify(this.canvasData.originalcompanyBuildingData));
+    newData.data = JSON.stringify(newData.data) // 转换JSON 数据格式
+    this.selectingSitePlan = this.sitePlanData[0] || {}
+    this.canvasData.selectStorey = this.sitePlanData[0] || {} //服务中 存一份数据
+    this.selectSitePlanIndex = 0
+    this.canvasData.isChange = false
+    if (isCompany===-1) {
+      this.http.post("/api/CompanyData",newData).subscribe(data => {})
+      this.getSitePlanStorey(this.selectingSitePlan) //获取 平面图 楼层数据
+    } else {
+      this.http.post("/api/BuildingData",newData,{params:this.params}).subscribe(data => {})
+      this.getBuildingStorey(this.selectingSitePlan) //获取 建筑 楼层数据
+    }
+  }
+
+  //复制图层 平面图 楼层/区域
+  duplicateLayer (item) {
+    if (this.checkedBuildingIndex==-1) { //总平面图
+      item.id = ""
+      item.modifiedTime = new Date()
+      item.name = item.name + '(副本)'
+      item.order = this.sitePlanData[this.sitePlanData.length-1].order+1
+      this.http.post('/api/SitePlans',item).subscribe((data:any)=>{
+        let newData = {
+          version: this.canvasData.originaleveryStoreyData.version || "2.0",
+          id: "",
+          data: JSON.stringify( JSON.parse(JSON.stringify(this.canvasData.originaleveryStoreyData.data)) ) || null,
+          sitePlanId: data.id
+        }
+        this.http.post('/api/SitePlanData',newData,{params:this.params}).subscribe(data=>{
+          this.renovateSitePlan()
+        })
+
+      })
+    } else { //楼层/区域
+      item.id = ""
+      item.modifiedTime = new Date()
+      item.name = item.name + '(副本)'
+      item.order = this.sitePlanData[this.sitePlanData.length-1].order+1
+      this.http.post('/api/BuildingAreas',item,{params:this.params}).subscribe((data:any)=>{
+        let newData = {
+          version: this.canvasData.originaleveryStoreyData.version || "2.0",
+          id: "",
+          data: JSON.stringify( JSON.parse(JSON.stringify(this.canvasData.originaleveryStoreyData.data)) ) || null,
+          buildingAreaId: data.id
+        }
+        this.http.post('/api/BuildingAreaData',newData,{params:this.params}).subscribe(data=>{
+          this.renovateBuilding()
+        })
+
+      })
+    }
+  }
+
+  //平面图 楼层/区域  替换底图
+  replaceBaseMap (e,item) {
+    e.stopPropagation();
+    let file = e.target.files[0] || null //获取上传的文件
+    let fileSize = file.size || null //上传文件的总大小
+    let maxSize = 5 * 1024 * 1024 //5MB一个分片
+
+    if (file && fileSize<=maxSize) { //上传文件<=5MB时
+      let formData = new FormData()
+      formData.append("file",file)
+      this.http.post(`/api/Objects/WebPlan2D/${sessionStorage.getItem('companyId')}`,formData).subscribe((data:any)=>{
+        this.renovateBaseMap(data.objectName,item)
+      })
+    } else {
+      const config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('上传底图需小于5MB','确定',config);
+    }
+  }
+
+  //封装 替换底图 function
+  renovateBaseMap (e,item) {
+    item.imageUrl = '/api/Objects/WebPlan2D/' + e
+    if (this.checkedBuildingIndex ==-1) { //总平面图
+      this.http.put(`/api/SitePlans/${item.id}`,item).subscribe(data=>{
+        let isSuccess = this.renovateSitePlan()
+        isSuccess.then(res=>{
+          this.canvas.refreshBackgroundImage()
+        })
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('上传底图成功','确定',config);
+      })
+    } else { //楼层/区域
+      this.http.put(`/api/BuildingAreas/${item.id}`,item,{params:this.params}).subscribe(data=>{
+        let isSuccess = this.renovateBuilding()
+        isSuccess.then(res=>{
+          this.canvas.refreshBackgroundImage()
+        })
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('上传底图成功','确定',config);
+      })
+    }
+  }
+
+  //封装 刷新总平面图 数据
+  renovateSitePlan () {
+    return new Promise ((resolve,reject)=>{
+      this.http.get('/api/SitePlans',{params:this.params}).subscribe(data=>{
+        this.sitePlanData = data
+        this.selectingSitePlan = this.sitePlanData[this.selectSitePlanIndex]
+        this.canvasData.selectStorey = this.sitePlanData[this.selectSitePlanIndex] //服务中 存一份数据
+        this.canvasData.originaleveryStoreyData.sitePlanId? null : this.canvasData.originaleveryStoreyData.sitePlanId = this.selectingSitePlan.id || null
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('数据更新成功','确定',config);
+        resolve('success')
+      })
+    })
+  }
+
+  //封装 刷新 楼层/区域 数据
+  renovateBuilding () {
+    let params = {
+      buildingId: this.beforeOneCheckedBuilding.id
+    }
+    return new Promise ((resolve,reject)=>{
+      this.http.get('/api/BuildingAreas',{params}).subscribe(data=>{
+        this.sitePlanData = data
+        this.selectingSitePlan = this.sitePlanData[this.selectSitePlanIndex]
+        this.canvasData.selectStorey = this.sitePlanData[this.selectSitePlanIndex] //服务中 存一份数据
+        this.canvasData.originaleveryStoreyData.buildingAreaId? null : this.canvasData.originaleveryStoreyData.buildingAreaId = this.selectingSitePlan.id || null
+        const config = new MatSnackBarConfig();
+        config.verticalPosition = 'top';
+        config.duration = 3000
+        this.snackBar.open('数据更新成功','确定',config);
+        resolve('success')
+      })
+    })
+  }
+
+  allLibrary:any = []; //所有素材库 + 素材
+  selectLibrary:any; //选中的素材库
+  selectImage:any; //选中的素材库图片
+  selectImageIndex:number; //选中的素材库图片index
+
+  //获取素材库
+  getAllLibrary (type:string='input') {
+    this.http.get(`/api/AssetLibraries?tag=${type}`).subscribe((data:any)=>{
+      data.forEach(element => {
+        element.images = []
+      });
+      this.allLibrary = data
+      this.selectImageIndex = -1
+      // this.canvas.cancelPaint()
+    })
+  }
+
+  //素材库展开面板展开时
+  opened (e) {
+    if (!e.images.length) { //当前素材库没加载素材时
+      this.http.get(`/api/Assets?libraryId=${e.id}`).subscribe((data:any)=>{
+        e.images = data
+      })
+    }
+  }
+
+  //点击选中素材库图片时
+  selectImg (item,items,index) {
+    this.selectLibrary = item.name
+    this.selectImage = items
+    this.selectImageIndex = index
+    this.canvasData.selectTemplateData = items
+    this.canvas.beginPaint()
+  }
+
+
+
+  //处置预案
+  allFirePlan:any = []; //所有灾情
+  selectDisposalNode:string = ''; //当前点击tree节点 css选中样式
+
+  //获取所有灾情
+  getAllFirePlan () {
+    let params = {componentId: sessionStorage.getItem('planId')}
+    this.http.get('/api/Disasters',{params:params}).subscribe((data:any)=>{
+      if (!data.length) { //该 单位没有灾情时
+        let msg = {
+          name: '灾情',
+          modifiedTime: new Date(),
+          planComponentId: sessionStorage.getItem('planId')
+        }
+        this.http.post('/api/Disasters',msg).subscribe(data=>{ 
+          this.allFirePlan.push(data)
+          let params = {disasterId: this.allFirePlan[0].id || ''}
+          this.http.get('/api/DisposalNodes',{params:params}).subscribe(data=>{ //所有处置节点
+            this.canvasData.allDisposalNode = data
+          })
+        })
+      } else { //单位 有灾情时
+        this.allFirePlan = data
+        let params = {disasterId: this.allFirePlan[0].id || ''}
+        this.http.get('/api/DisposalNodes',{params:params}).subscribe(data=>{ //所有处置节点
+          this.canvasData.allDisposalNode = data
+        })
+      }
+    })
+  }
+
+  //获取所有处置节点
+  getDisposalNode () {
+    this.selectDisposalNode = ''
+    let params = {disasterId: this.allFirePlan[0].id || ''}
+    this.http.get('/api/DisasterData/Markers',{params:params}).subscribe(data=>{ //灾情标签信息
+      this.canvasData.allNodeMarkers = data
+      this.mateFireForce()
+    })
+    this.http.get('/api/DisposalNodes',{params:params}).subscribe(data=>{ //处置节点
+      this.canvasData.allDisposalNode = data
+      this.handleHybridTree()
+    })
+  }
+
+  treeData:any = []; //渲染tree处理完成数据
+  defaultExpandedKeys:any = []; //首次渲染 tree展开状态
+  //处理 节点 Tree数据
+  handleHybridTree () {
+    this.defaultExpandedKeys = []
+    let treeData = []
+    let data =  JSON.parse(JSON.stringify( this.canvasData.allDisposalNode || [] )) 
+    data.forEach(element => {
+      this.defaultExpandedKeys.push(element.id)
+      element.title = element.name //name
+      element.key = element.id //id
+      element.children = [] //children
+      if (element.sitePlanId || element.buildingAreaId) { //是数据节点
+        element.isLeaf = true
+        element.isDataNode = true
+      } else { //不是数据节点
+        element.isLeaf = false
+        element.isDataNode = false
+      }
+      data.forEach(item=>{
+        item.parentId === element.id? element.children.push(item) : null
+      })
+    });
+    data.forEach(element=>{
+      !element.parentId? treeData.push(element) : null
+    })
+    this.treeData = [...treeData]
+    this.defaultExpandedKeys = [...this.defaultExpandedKeys]
+  }
+
+  //刷新 treeData 保存已展开节点
+  refurbishTreeData () {
+    this.defaultExpandedKeys = []
+    let params = {disasterId: this.allFirePlan[0].id || ''}
+    this.http.get('/api/DisposalNodes',{params:params}).subscribe(nodeData=>{ //处置节点
+      this.canvasData.allDisposalNode = nodeData
+      let oldTreeData = this.nzTreeComponent.getExpandedNodeList()
+      oldTreeData.forEach(item=>{
+        this.defaultExpandedKeys.push(item.key)
+      })
+      let treeData = []
+      let data =  JSON.parse(JSON.stringify( this.canvasData.allDisposalNode || [] )) 
+      data.forEach(element => {
+        element.title = element.name //name
+        element.key = element.id //id
+        element.children = [] //children
+        if (element.sitePlanId || element.buildingAreaId) { //是数据节点
+          element.isLeaf = true
+          element.isDataNode = true
+        } else { //不是数据节点
+          element.isLeaf = false
+          element.isDataNode = false
+        }
+        data.forEach(item=>{
+          item.parentId === element.id? element.children.push(item) : null
+        })
+      });
+      data.forEach(element=>{
+        !element.parentId? treeData.push(element) : null
+      })
+      this.treeData = [...treeData]
+      this.defaultExpandedKeys = [...this.defaultExpandedKeys]
+    })
+  }
+
+  sitePlanIcon = {fire:0,force:0} // 总平面图 火源/力量 图标 是否展示
+
+  //刷新 建筑楼层 匹配 火源/力量   图标
+  mateFireForce () {
+    let data = this.canvasData.allNodeMarkers.markers || {}
+    for(let key in data){ //遍历 火/力量 图标
+      this.sitePlanData.forEach(element => { //楼层 
+        if (element.id==key) { // 相匹配时
+          data[key].fireCount != 0? element.fire=1 : element.fire=0
+          data[key].forceCount != 0? element.force=1 : element.force=0
+        }
+      });
+      this.allBuildings.forEach(element => { //建筑
+        if (element.id==key) { // 相匹配时
+          data[key].fireCount != 0? element.fire=1 : element.fire=0
+          data[key].forceCount != 0? element.force=1 : element.force=0
+        }
+      });
+      if (this.params.companyId==key) { //总平面图时
+        data[key].fireCount != 0? this.sitePlanIcon.fire=1 : this.sitePlanIcon.fire=0
+        data[key].forceCount != 0? this.sitePlanIcon.force=1 : this.sitePlanIcon.force=0
+      }
+    }
+
+    let buildingMSG = this.canvasData.allNodeMarkers.highlightMarkers || {}
+    for (let key in buildingMSG) {
+      if (key === this.canvasData.selectPanelPoint.DisposalNodeId) {
+        for (let keys in buildingMSG[key]) {
+          this.sitePlanData.forEach(element => { //楼层 
+            if (element.id === keys) {
+              buildingMSG[key][keys].fireCount != 0? element.fire=2 : null
+              buildingMSG[key][keys].forceCount != 0? element.force=2 : null
+            }
+          });
+          this.allBuildings.forEach(element => { //建筑
+            if (element.id === keys) { // 相匹配时
+              buildingMSG[key][keys].fireCount != 0? element.fire=2 : null
+              buildingMSG[key][keys].forceCount != 0? element.force=2 : null
+            }
+          });
+          if (this.params.companyId === keys) { //总平面图时
+            buildingMSG[key][keys].fireCount != 0? this.sitePlanIcon.fire=2 : null
+            buildingMSG[key][keys].forceCount != 0? this.sitePlanIcon.force=2 : null
+          }
+        }
+      }
+    }
+  }
+
+  // 切换 基本信息时  刷新  删除 建筑楼层 自定义属性
+  mateDeleteCustomize () {
+    this.defaultExpandedKeys = []
+    this.sitePlanIcon = {fire:0,force:0}
+    this.sitePlanData.forEach(element => { //楼层
+      delete element.fire
+      delete element.force
+    });
+    this.allBuildings.forEach(element => { //建筑
+      delete element.fire
+      delete element.force
+    });
+  }
+
+  //计算差异
+  countValue (e) {
+    e.stopPropagation()
+    let params = {disasterId: this.allFirePlan[0].id || ''}
+    this.http.get('/api/DisasterData/Diffs',{params:params}).subscribe(data=>{
+      this.canvasData.allNodeMarkers = data
+      this.mateFireForce()
+      const config = new MatSnackBarConfig();
+      config.verticalPosition = 'top';
+      config.duration = 3000
+      this.snackBar.open('计算差异完成','确定',config);
+    })
+  }
+
+  //新建 处置预案 节点
+  addPanelPoint (e,item,treeData) {
+    e.stopPropagation()
+    let order
+    if (item) {
+      item.children.length? order = item.children[item.children.length-1].order+1 : order = 0
+    } else {
+      treeData.length? order = treeData[treeData.length-1].order+1 : order = 0
+    }
+    let data = {
+      name: '',
+      level: item? 1 : 0,
+      order: order,
+      description: '',
+      disasterId: this.allFirePlan[0].id || '',
+      parentId: item? item.id : null,
+      planComponentId: sessionStorage.getItem('planId') || '',
+    }
+    let dialogRef = this.dialog.open(addDisposalNodeComponent,{data});
+    dialogRef.afterClosed().subscribe(data=>{
+      if (data) { this.refurbishTreeData() }
+    })
+  }
+
+  //编辑 处置预案 节点
+  editPanelPoint (e,item) {
+    e.stopPropagation()
+    let data = item.origin
+    let dialogRef = this.dialog.open(editDisposalNodeComponent,{data});
+    dialogRef.afterClosed().subscribe(data=>{
+      if (data) {
+        item.title = data
+        this.canvasData.allDisposalNode.forEach(element => { element.id === item.key? element.name=data : null });
+      }
+    })
+  }
+
+  //复制 处置预案 节点
+  copyPanelPoint (e,item,treeData) { 
+    e.stopPropagation()
+    if (confirm(`确定要复制 ${item.title} ?`)) {
+      let params = {id: item.origin.id}
+      let order
+      if (item.level==0) {
+        treeData.length? order = treeData[treeData.length-1].order+1 : order = 0
+      } else {
+        let parent = item.getParentNode() //获取父节点
+        parent.origin.children.length? order = parent.origin.children[parent.origin.children.length-1].order+1 : order = 0
+      }
+      let data = {
+        name: item.title + '(副本)',
+        level: item.level,
+        order: order,
+        description: '',
+        disasterId: item.origin.disasterId || '',
+        parentId: item.origin.parentId || null,
+        planComponentId: item.origin.planComponentId || '',
+      }
+      this.http.post('/api/DisposalNodes/Clone',data,{params:params}).subscribe(data=>{
+        this.refurbishTreeData()
+      })
+    } //isTrue
+  }
+
+  //删除 处置预案 节点
+  deletePanelPoint (e,item) { 
+    e.stopPropagation()
+    if (confirm(`确定要删除 ${item.name} ?`)) {
+      this.http.delete(`/api/DisposalNodes/${item.id}`).subscribe(data=>{
+        this.refurbishTreeData()
+      })
+    }
+  }
+
+  //点击 处置Tree节点
+  selectanelPoint (e) {
+    if (!e.buildingAreaId && !e.sitePlanId) { //当前节点 不是 数据节点 时
+      let msg = this.canvasData.findDisposalNode(e.id)
+      this.canvasData.selectPanelPointBaseData = msg
+      this.selectDisposalNode == msg.id? this.selectDisposalNode = '' : this.selectDisposalNode = msg.id //选中 节点
+
+    } else if (e.buildingAreaId || e.sitePlanId) { //当前节点 是 数据节点 时
+      let msg = this.canvasData.findDisposalNode(e.parentId)
+      this.canvasData.selectPanelPointBaseData = msg
+      this.selectDisposalNode = e.parentId //选中 节点
+      
+      if (this.canvasData.selectPanelPoint.DisposalNodeId != e.id) { //选择节点  不是当前节点时
+        let params = {nodeId: e.id}
+        let parameter = { //查询 节点 对应 建筑/楼层 index,id
+          buildingIndex: e.sitePlanId? -1 : this.allBuildings.findIndex(item=>{ return item.id===e.buildingId }), //总平面图/建筑 index
+          storeyId: e.sitePlanId? e.sitePlanId : e.buildingAreaId, //楼层id
+        }
+        this.http.get('/api/DisposalNodeData',{params:params}).subscribe(data=>{
+          this.canvasData.selectPanelPoint = this.canvasData.deserialize(JSON.stringify(data || new DisposalNodeData())) //选择 当前 节点
+          this.canvasData.selectPanelPoint.Data = this.canvasData.deserialize(this.canvasData.selectPanelPoint.Data)
+          this.seekPanelPoint(parameter)
+        })
+      } //if
+
+    } //else if
+  }
+
+  //查找 数据节点 对应 建筑/楼层
+  seekPanelPoint (paramsData) {
+    this.checkedBuildingIndex = paramsData.buildingIndex
+    if (paramsData.buildingIndex ==-1) { //总平面图时
+      this.beforeOneCheckedBuilding={name:"总平面图"}
+      let fireData = this.getFireElements(sessionStorage.getItem('buildingTypeId')) //获取单位下 消防要素
+      let planData = this.getSitePlanCompanyData() //获取 单位 数据
+      this.http.get('/api/SitePlans',{params:this.params}).subscribe(data=>{
+        this.sitePlanData = data
+        let index = this.sitePlanData.findIndex(item=>{ return item.id===paramsData.storeyId })
+        this.selectingSitePlan = this.sitePlanData[index] || {}
+        this.canvasData.selectStorey = this.sitePlanData[index] || {} //服务中 存一份数据
+        this.selectSitePlanIndex = index
+  
+        Promise.all([fireData,planData]).then((res)=>{
+          this.getSitePlanStorey(this.selectingSitePlan) //获取 平面图 楼层数据
+        })
+      }) //get
+    } else { //建筑时
+      this.beforeOneCheckedBuilding=this.allBuildings[paramsData.buildingIndex]
+      let params = { buildingId: this.beforeOneCheckedBuilding.id }
+      let fireData = this.getFireElements(this.beforeOneCheckedBuilding.buildingTypes[0].id || '') //获取建筑下 消防要素
+      let planData = this.getBuildingData(params) //获取 建筑 数据
+      this.http.get('/api/BuildingAreas',{params}).subscribe(data=>{
+        this.sitePlanData = data
+        let index = this.sitePlanData.findIndex(item=>{ return item.id===paramsData.storeyId })
+        this.selectingSitePlan = this.sitePlanData[index] || {}
+        this.canvasData.selectStorey = this.sitePlanData[index] || {} //服务中 存一份数据
+        this.selectSitePlanIndex = index
+  
+        Promise.all([fireData,planData]).then((res)=>{
+          this.getBuildingStorey(this.selectingSitePlan) //获取 建筑 楼层数据
+        })
+      }) //get
+    }
+  }
+
+
+
+}
+
+
+
+//创建建筑
+@Component({
+  selector: 'app-createBuilding',
+  templateUrl: './createBuilding.html',
+  styleUrls: ['./collection-tools.component.scss']
+})
+export class CreateBuilding {
+
+  constructor(private http:HttpClient,public dialog: MatDialog,public dialogRef: MatDialogRef<CreateBuilding>,@Inject(MAT_DIALOG_DATA) public data) { }
+
+  allBuildingType:any//所有的建筑类型
+  selected:any; //选中的建筑
+  ngOnInit(): void {
+    this.getAllBuildingType()
+  }
+
+  //获得所有单位类型
+  getAllBuildingType(){
+    this.http.get("/api/BuildingTypes/Simple").subscribe(data=>{
+      this.allBuildingType = data
+    })
+  }
+
+  //创建建筑功能分区
+  onSubmit (e) {
+    let companyId = sessionStorage.getItem("companyId")
+    let lastBuildingOrder = this.data.allBuildings.length != 0 ? this.data.allBuildings[this.data.allBuildings.length - 1].order + 1 : 0
+    let data = 
+    {
+      id: "",
+      name: e.propertyName,
+      order: lastBuildingOrder ,
+      enabled: true,
+      companyId: companyId,
+      buildingTypes: [
+        {
+          id: e.buildingId,
+          name: ""
+        }
+      ]
+    }
+    this.http.post("/api/Buildings",data,{
+      params:{
+        companyId : this.data.companyId
+      }
+    }).subscribe(data=>{
+      this.dialogRef.close("创建成功");
+    },err=>{
+      this.dialogRef.close("创建失败");
+    })
+  }
+}
+
+//编辑建筑
+@Component({
+  selector: 'app-editBuilding',
+  templateUrl: './editBuilding.html',
+  styleUrls: ['./collection-tools.component.scss']
+})
+export class EditBuilding {
+
+  constructor(private http:HttpClient,public dialog: MatDialog,public dialogRef: MatDialogRef<EditBuilding>,@Inject(MAT_DIALOG_DATA) public data) { }
+  
+  defaultName:String = this.data.item.name//默认建筑名称
+  defaultBuildingType:String = this.data.item.buildingTypes[0].id//默认建筑类型
+  allBuildingType:any//所有的建筑类型
+
+  ngOnInit(): void {
+    this.getAllBuildingType()
+  }
+
+  getAllBuildingType(){
+    this.http.get("/api/BuildingTypes/Simple").subscribe(data=>{
+      this.allBuildingType = data
+    })
+  }
+
+  //编辑建筑信息
+  onSubmit (e) {
+    let companyId = sessionStorage.getItem("companyId")
+    let data = 
+    {
+      id: this.data.item.id,
+      name: e.propertyName,
+      order: this.data.item.order,
+      enabled: true,
+      companyId: companyId,
+      buildingTypes: [
+        {
+          id: e.buildingId,
+          name: ""
+        }
+      ]
+    }
+    this.http.put(`/api/Buildings/${this.data.item.id}`,data,{
+      params:{
+        id:this.data.item.id,
+        companyId:companyId
+      }
+    }).subscribe(data=>{
+      this.dialogRef.close("修改成功");
+    },err=>{
+      this.dialogRef.close("修改失败");
+    })
+  }
+}
+
+//查看图片大图
+@Component({
+  selector: 'viewdetails',
+  templateUrl: './viewdetails.html',
+  styleUrls: ['./collection-tools.component.scss']
+})
+export class ViewDetailss {
+  // myControl = new FormControl();
+  //注入MatDialogRef,可以用来关闭对话框
+  //要访问对话框组件中的数据,必须使用MAT_DIALOG_DATA注入令牌
+  constructor(private http: HttpClient,public dialogRef: MatDialogRef<ViewDetailss>,@Inject(MAT_DIALOG_DATA) public data,private element: ElementRef) {}
+  imagesArr = this.data.imagesArr
+  onNoClick(): void {
+    this.dialogRef.close();
+  }
+  ngOnInit(): void {
+    setTimeout(() => {
+      var mySwiper = new Swiper('.swiper-container',{
+        loop: false,
+        initialSlide :this.data.index,//默认索引
+        // 如果需要前进后退按钮
+        navigation: {
+          nextEl: '.swiper-button-next',
+          prevEl: '.swiper-button-prev',
+      } 
+        //其他设置
+      });  
+    }, 0);
+  }
+  closeDialog(){
+    this.dialogRef.close();
+  }
+  count = 10
+  zoomimg(e) {
+    if(this.count != 1 || e.wheelDelta >= 120){
+      if(e.wheelDelta >= 120){
+        this.count++
+      }else{
+        this.count--
+      }
+    }
+    e.srcElement.style.zoom = this.count + '0%'
+    e.srcElement.style.maxWidth = null
+    e.srcElement.style.maxHeight = null
+  }
+
+}
diff --git a/src/app/ui/collection-tools/createBuilding.html b/src/app/ui/collection-tools/createBuilding.html
new file mode 100644
index 0000000..16fdd7b
--- /dev/null
+++ b/src/app/ui/collection-tools/createBuilding.html
@@ -0,0 +1,28 @@
+<div mat-dialog-title>新增建筑</div>
+<div>
+    <form (ngSubmit)="onSubmit(form.value)" #form="ngForm" class="example-container">
+
+        <div mat-dialog-content>
+            <mat-form-field>
+                  <input type="text" matInput ngModel
+                  required name="propertyName" placeholder="建筑名称" autocomplete="off">
+            </mat-form-field>
+        </div>
+        <div mat-dialog-content>
+            <mat-form-field>
+                <mat-select [(value)]="selected" required ngModel name="buildingId" placeholder="建筑类型">
+                    <mat-option *ngFor="let item of allBuildingType" [value]="item.id">
+                    {{item.name}}
+                    </mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>  
+        <div mat-dialog-actions>
+            <button mat-raised-button color="primary" type="submit" 
+            [disabled]="!form.form.valid">
+                确定
+            </button>
+            <button mat-raised-button mat-dialog-close>取消</button>
+        </div>
+    </form>
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/editBuilding.html b/src/app/ui/collection-tools/editBuilding.html
new file mode 100644
index 0000000..433316d
--- /dev/null
+++ b/src/app/ui/collection-tools/editBuilding.html
@@ -0,0 +1,23 @@
+<div mat-dialog-title>编辑建筑</div>
+<div>
+    <form (ngSubmit)="onSubmit(form.value)" #form="ngForm" class="example-container">
+        <div mat-dialog-content>
+            <mat-form-field>
+                <input type="text" matInput [(ngModel)]="defaultName" required name="propertyName" placeholder="建筑名称" autocomplete="off">
+            </mat-form-field>
+        </div>
+        <div mat-dialog-content>
+            <mat-form-field>
+                <mat-select required [(ngModel)]="defaultBuildingType" name="buildingId" placeholder="建筑类型">
+                    <mat-option *ngFor="let item of allBuildingType" [value]="item.id">
+                        {{item.name}}
+                    </mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>  
+        <div mat-dialog-actions>
+            <button mat-raised-button color="primary" type="submit" [disabled]="!form.form.valid">确定</button>
+            <button mat-raised-button mat-dialog-close>取消</button>
+        </div>
+    </form>
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/editDisposalNode.html b/src/app/ui/collection-tools/editDisposalNode.html
new file mode 100644
index 0000000..40b2270
--- /dev/null
+++ b/src/app/ui/collection-tools/editDisposalNode.html
@@ -0,0 +1,23 @@
+<div class="functionalDomainContent">
+  <div mat-dialog-title>
+    <label>修改灾情节点名称</label>
+  </div>
+
+  <form (ngSubmit)="onSubmit(form.value)" #form="ngForm" class="example-container">
+
+    <div class="keyMargin">
+      <mat-form-field>
+        <input matInput name="name" required [(ngModel)]="nodeName" placeholder="名称">
+      </mat-form-field>
+    </div>
+
+    <div class="submitBottom">
+      <button mat-raised-button color="primary" type="submit" [disabled]="!form.form.valid">
+          确定
+      </button>
+      <button mat-raised-button mat-dialog-close>取消</button>
+    </div>
+
+  </form>
+
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/editPlaneFigure.html b/src/app/ui/collection-tools/editPlaneFigure.html
new file mode 100644
index 0000000..a0f8016
--- /dev/null
+++ b/src/app/ui/collection-tools/editPlaneFigure.html
@@ -0,0 +1,39 @@
+<div class="functionalDomainContent">
+  
+  <div mat-dialog-title>
+    <label *ngIf="!data.isBuilding">编辑平面图</label>
+    <label *ngIf="data.isBuilding">编辑楼层/区域</label>
+  </div>
+
+  <form (ngSubmit)="onSubmit(form.value)" #form="ngForm" class="example-container">
+
+    <div class="keyMargin">
+      <mat-form-field>
+        <input matInput name="name" required [(ngModel)]="name" placeholder="名称">
+      </mat-form-field>
+    </div>
+
+    <div class="keyMargin" *ngIf="data.isBuilding">
+      <mat-checkbox name="isRefugeStorey"  [(ngModel)]="checked">是否为避难层</mat-checkbox>
+    </div>
+
+    <div class="keyMargin">
+      <mat-form-field>
+        <input matInput name="area" type="number" required [(ngModel)]="area" placeholder="面积 (平方米)">
+      </mat-form-field>
+    </div>
+
+    <div class="keyMargin">
+      <textarea name="details" [(ngModel)]="details" placeholder="详情"></textarea>
+    </div>
+
+    <div class="submitBottom">
+      <button mat-raised-button color="primary" type="submit" [disabled]="!form.form.valid">
+          确定
+      </button>
+      <button mat-raised-button mat-dialog-close>取消</button>
+    </div>
+
+  </form>
+
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/leftFunctionalDomain.ts b/src/app/ui/collection-tools/leftFunctionalDomain.ts
new file mode 100644
index 0000000..519653d
--- /dev/null
+++ b/src/app/ui/collection-tools/leftFunctionalDomain.ts
@@ -0,0 +1,180 @@
+import { Component, OnInit, Inject } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
+
+@Component({
+  selector: 'app-leftFunctionalDomain',
+  templateUrl: './addPlaneFigure.html',
+  styleUrls: ['./panel.scss']
+})
+export class leftFunctionalDomainComponent implements OnInit {
+
+  constructor(
+    private http:HttpClient,
+    public dialog: MatDialog,
+    public snackBar: MatSnackBar,
+    public dialogRef: MatDialogRef<any>,
+    @Inject(MAT_DIALOG_DATA) public data) { }
+
+  ngOnInit(): void {
+
+  }
+
+  params = {companyId: sessionStorage.getItem('companyId')}
+  checked:boolean = false;//是否为避难层
+  
+  //提交表单创建平面图
+  onSubmit (e) {
+    if (!this.data.isBuilding) { //总平面图 创建平面图
+      let data = {
+        companyId: sessionStorage.getItem('companyId'),
+        name: e.name,
+        order: this.data.order,
+        area:e.area,
+        details:e.details,
+        enabled: true,
+        modifiedTime: new Date(),
+      }
+      this.http.post('/api/SitePlans',data).subscribe(data=>{
+        this.dialogRef.close('总平面图');
+      })
+    } else { //建筑 创建楼层/区域
+      let data = {
+        isRefugeStorey: e.isRefugeStorey,
+        buildingId: this.data.Panel.id,
+        name: e.name,
+        order: this.data.order,
+        area:e.area,
+        details:e.details,
+        enabled: true,
+        modifiedTime: new Date(),
+      }
+      this.http.post('/api/BuildingAreas',data,{params:this.params}).subscribe(data=>{
+        this.dialogRef.close('建筑');
+      })
+    }
+  }
+
+
+
+}
+
+//编辑平面图 楼层/区域
+@Component({
+  selector: 'app-editPlaneFigure',
+  templateUrl: './editPlaneFigure.html',
+  styleUrls: ['./panel.scss']
+})
+export class editPlaneFigureComponent implements OnInit {
+
+  constructor(private http:HttpClient,public dialog: MatDialog,public snackBar: MatSnackBar,public dialogRef: MatDialogRef<any>,@Inject(MAT_DIALOG_DATA) public data) { }
+
+  ngOnInit(): void {
+    this.name = this.data.buildingData.name || ''
+    this.checked = this.data.buildingData.isRefugeStorey || false
+    this.area = this.data.buildingData.area || 0
+    this.details = this.data.buildingData.details || ''
+  }
+
+  params = {companyId: sessionStorage.getItem('companyId')}
+  name:any; //name
+  checked:boolean = false;//是否为避难层
+  area:number; //面积
+  details:string; //详情
+
+  //提交表单修改平面图
+  onSubmit (e) {
+    if (!this.data.isBuilding) { //总平面图 修改平面图
+      let data = {
+        companyId: sessionStorage.getItem('companyId'),
+        id: this.data.buildingData.id,
+        name: e.name,
+        cadUrl: this.data.buildingData.cadUrl,
+        imageUrl: this.data.buildingData.imageUrl,
+        imageAngle: this.data.buildingData.imageAngle,
+        order: this.data.buildingData.order,
+        area:e.area,
+        details:e.details,
+        enabled: this.data.buildingData.enabled,
+        modifiedTime: new Date(),
+      }
+      this.http.put(`/api/SitePlans/${this.data.buildingData.id}`,data).subscribe(data=>{
+        this.dialogRef.close('总平面图');
+      })
+    } else { //建筑 修改楼层/区域
+      let data = {
+        isRefugeStorey: e.isRefugeStorey,
+        buildingId: this.data.Panel.id,
+        id: this.data.buildingData.id,
+        name: e.name,
+        cadUrl: this.data.buildingData.cadUrl,
+        imageUrl: this.data.buildingData.imageUrl,
+        imageAngle: this.data.buildingData.imageAngle,
+        order: this.data.buildingData.order,
+        area:e.area,
+        details:e.details,
+        enabled: this.data.buildingData.enabled,
+        modifiedTime: new Date(),
+      }
+      this.http.put(`/api/BuildingAreas/${this.data.buildingData.id}`,data,{params:this.params}).subscribe(data=>{
+        this.dialogRef.close('建筑');
+      })
+    }
+  }
+
+
+
+}
+
+
+
+//创建 处置预案 节点
+@Component({
+  selector: 'app-addDisposalNode',
+  templateUrl: './addDisposalNode.html',
+  styleUrls: ['./panel.scss']
+})
+export class addDisposalNodeComponent implements OnInit {
+
+  constructor(private http:HttpClient,public dialog: MatDialog,public snackBar: MatSnackBar,public dialogRef: MatDialogRef<any>,@Inject(MAT_DIALOG_DATA) public data) { }
+
+  ngOnInit(): void {
+  }
+
+  //提交表单
+  onSubmit (e) {
+    this.data.name = e.name
+    this.http.post('/api/DisposalNodes',this.data).subscribe(data=>{
+      this.dialogRef.close('success');
+    })
+  }
+
+}
+
+
+
+//编辑 处置预案 节点
+@Component({
+  selector: 'app-editDisposalNode',
+  templateUrl: './editDisposalNode.html',
+  styleUrls: ['./panel.scss']
+})
+export class editDisposalNodeComponent implements OnInit {
+
+  constructor(private http:HttpClient,public dialog: MatDialog,public snackBar: MatSnackBar,public dialogRef: MatDialogRef<any>,@Inject(MAT_DIALOG_DATA) public data) { }
+
+  ngOnInit(): void {
+    this.nodeName = JSON.parse(JSON.stringify( this.data.name || '' )) 
+  }
+  nodeName:string;
+  
+  //提交表单
+  onSubmit (e) {
+    this.data.name = e.name
+    this.http.put(`/api/DisposalNodes/${this.data.id}`,this.data).subscribe(data=>{
+      this.dialogRef.close(e.name);
+    })
+  }
+
+}
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/panel.scss b/src/app/ui/collection-tools/panel.scss
new file mode 100644
index 0000000..f0ae073
--- /dev/null
+++ b/src/app/ui/collection-tools/panel.scss
@@ -0,0 +1,291 @@
+.matIcons {
+  color: #8E909F;
+}
+
+
+
+//平面图 素材库 公共样式 头部
+.planarGraphHeader{
+  height: 35px;
+  min-height: 35px;
+  cursor: pointer;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0 24px;
+  border-radius: 5px;
+  font-family: Roboto, "Helvetica Neue", sans-serif;
+  font-size: 15px;
+  font-weight: 400;
+  color: #000;
+  background: linear-gradient(to top,#cdced1,#FFF);
+}
+//平面图头部字体图标样式
+.hover {
+  width: 18px;
+  height: 18px;
+  margin-left: 90px;
+  border: 1px solid #999;
+  border-radius: 3px;
+  .mat-icon {font-size: 18px; color: #999;}
+}
+.hover:hover { 
+  background-color: #4DA5FA;
+  .mat-icon {color: #fff;}
+}
+
+//平面图
+.sitePlanContent {
+  position: relative;
+  width: 100%;
+  height: 35px;
+  line-height: 35px;
+  box-sizing: border-box;
+  padding: 0 10px 0 25px;
+  .mat-icon {
+    font-size: 20px;
+  }
+}
+
+//火源/力量 图标
+.fireForce {
+  display: block;
+  float: right;
+  margin: 8px 5px 0 0;
+  width: 40px;
+  height: 20px;
+  line-height: 20px;
+  text-align: center;
+  position: relative;
+  overflow: hidden;
+  img{
+    width: 20px; 
+    height: 20px;
+  }
+}
+//替换底图  inputfile
+.a-upload {
+  display: block;
+  float: right;
+  margin: 8px 18px 0 0;
+  width: 20px;
+  height: 20px;
+  line-height: 20px;
+  text-align: center;
+  position: relative;
+  overflow: hidden;
+  input {
+    position: absolute;
+    width: 20px;
+    height: 20px;
+    left: 0;
+    top: 0;
+    opacity: 0;
+  }
+}
+.a-upload:hover {
+  .mat-icon {
+    color: #fff;
+  }
+}
+//上传底图 inputfile
+#a-uploadImg {
+  display: block;
+  width: 300px;
+  height: 170px;
+  position: fixed;
+  top: 40%;
+  left: 48%;
+  overflow: hidden;
+  border-radius: 5px;
+  border: 1px solid #999;
+  z-index: 999;
+  input {
+    position: absolute;
+    width: 300px;
+    height: 170px;
+    left: 0;
+    top: 0;
+    opacity: 0;
+  }
+  img {
+    width: 100%;
+    height: auto;
+  }
+}
+#a-uploadImg:hover {
+  border: 5px solid skyblue;
+}
+
+//hover时显示右边操作栏
+.sitePlanContent:hover {
+  #rightOperate {
+    display: block;
+  }
+}
+//右边操作栏
+#rightOperate{
+  width: 50px;
+  height: 100px;
+  position: absolute;
+  top: -32px;
+  right: -48px;
+  z-index: 99999;
+  border-radius: 0 100px 100px 0;
+  background-color: #F0F4F7;
+  // #F0F4F7 cdced1
+  display: none;
+  .functionButton {
+    height: 25%;
+    line-height: 25px;
+  }
+  .bigFunctionIcon {
+    font-size: 24px;
+  }
+  .functionIcon {
+    color: #999;
+  }
+  .functionIcon:hover {
+    color: #4DA5FA;
+  }
+}
+
+//处置预案 素材库 公用div
+.publiclBankPlan {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  padding-bottom: 10px;
+  // border-top: 1px dashed #999;
+}
+
+
+
+//   基本信息/想定作业 切换
+.scenarioAssignment {
+  overflow-y: auto;
+}
+.selectEditMode {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.materialBankDIV{
+  flex: 1;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+//   基本信息/想定作业 切换
+//处置预案
+#terrNodePublic {
+  height: 35px;
+  line-height: 35px;
+  display: flex;
+  .textNode {flex: 1;}
+}
+//字体图标
+.planIconDiv {
+  display: inline-block;
+  .mat-icon{
+    font-size: 20px;
+    width: 20px;
+    height: 20px;
+    color: #666;
+    margin-right: 3px;
+  }
+}
+
+
+
+.mat-expansion-panel-header {
+  height: 40px !important;
+}
+//素材库溢出隐藏
+#materialBank {
+  margin: 1px 0;
+}
+//素材库图片flex
+#panelLibrary .text{
+  box-sizing: border-box;
+  margin-left: 10px;
+}
+.panelLibraryFlex {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between; /* 水平居中 */
+  .imgBox {
+    width: 70px;
+    height: 100px;
+    display: inline-block;
+    text-align: center;
+    border-radius: 3px;
+    margin: 5px 0;
+    img {
+      width: 70px;
+      height: auto;
+      max-height: 70px;
+      cursor:pointer;
+    }
+    p {
+      font-size: 12px;
+      cursor:pointer;
+    }
+  }
+}
+
+//文本溢出
+.overflowText {
+  overflow: hidden;
+  text-overflow:ellipsis;
+  white-space: nowrap;
+}
+// 楼层/区域 是避难层时
+.isRefugeStorey {
+  color: #fff;
+  background-color: rgb(238, 186, 186);
+}
+//选中平面图时
+.selectSitePlan {
+  color: #fff;
+  background-color: #6BC2FF;
+}
+//选中素材库图片时
+.selectImg {
+  color: #fff;
+  background-color: #4DA5FA;
+}
+//选中 处置节点时
+.selectanelPoint {
+  background-color: #F4C235;
+}
+
+
+
+//左侧功能区弹出框样式
+.keyMargin {
+  width: 100%;
+  margin: 5px 0;
+  .mat-form-field {
+    width: 100%;
+  }
+}
+.submitBottom {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between; /* 水平居中 */
+}
+.functionalDomainContent {
+  width: 300px;
+  height: 100%;
+  textarea {
+    border-radius: 5px;
+    border: 1px solid #999;
+    width: 100%;
+    height: 120px;
+    resize:none;
+  }
+}
diff --git a/src/app/ui/collection-tools/save.ts b/src/app/ui/collection-tools/save.ts
new file mode 100644
index 0000000..5dc3b6b
--- /dev/null
+++ b/src/app/ui/collection-tools/save.ts
@@ -0,0 +1,301 @@
+import { Component, OnInit, Inject } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
+import {CanvasShareDataService,DisposalNodeData} from '../../canvas-share-data.service' //引入服务
+// 保存想定作业第一个弹窗
+@Component({
+    selector: 'dialog-overview-example-dialog',
+    templateUrl: 'saveOne.html',
+    styleUrls: ['./collection-tools.component.scss']
+})
+export class saveOneDialog {
+
+    constructor(
+        private http:HttpClient,
+        public dialog: MatDialog,
+        public snackBar: MatSnackBar,
+        public dialogRef: MatDialogRef<saveOneDialog>,
+        @Inject(MAT_DIALOG_DATA) public data) {}
+
+    onNoClick(): void {
+        this.dialogRef.close()
+    }
+    allDisposalNode = this.data.allDisposalNode
+
+    saveType(type){
+      this.dialogRef.close()
+      const dialogRef = this.dialog.open(saveTwoDialog, {
+        data: {type: type,
+          allDisposalNode: this.data.allDisposalNode,
+          selectedBuildingData:this.data.selectedBuildingData,
+          selectedSiteData:this.data.selectedSiteData,
+          siteOrbuilding:this.data.siteOrbuilding,
+          disasterId:this.data.disasterId}
+      });
+      dialogRef.afterClosed().subscribe(result => {
+      });
+    }
+
+}
+
+  // 保存想定作业第二个弹窗
+@Component({
+    selector: 'dialog-overview-example-dialog',
+    templateUrl: 'saveTwo.html',
+    styleUrls: ['./collection-tools.component.scss']
+})
+    export class saveTwoDialog {
+  
+    constructor(
+      private http:HttpClient,
+      public dialogRef: MatDialogRef<saveTwoDialog>,
+      public canvasData: CanvasShareDataService,
+      public snackBar: MatSnackBar,
+      @Inject(MAT_DIALOG_DATA) public data) {}
+      
+    type = this.data.type
+    allDisposalNode = this.data.allDisposalNode
+    allPlanDisposalNode = []
+    allRootDisposalNode =  [{name:"根节点",id:null}]
+    allDisposalNodeChild = []
+    ngOnInit(): void {
+      //所有非数据节点
+      this.allDisposalNode.forEach(item => {
+        if(!item.sitePlanId && !item.buildingAreaId){
+          this.allPlanDisposalNode.push(item)
+        }
+      })
+
+      //所有一级节点
+      this.allDisposalNode.forEach(item => {
+        if(!item.parentId){
+          this.allRootDisposalNode.push(item)
+        }
+      })
+      this.allDisposalNodeChild = JSON.parse(JSON.stringify(this.allDisposalNode))
+      this.allDisposalNodeChild.forEach(item => {
+        item.children = []
+        this.allDisposalNodeChild.forEach(i => {
+          if(i.parentId == item.id){
+            item.children.push(i)
+          }
+        })
+      })
+      // console.log(this.nodeItem.id)
+    }
+    onNoClick(): void {
+      this.dialogRef.close();
+    }
+    nodeItem
+    itemChildNum = 0 //点击处置节点子数据节点的数量
+    clickNode(item){
+      console.log(item)
+      this.nodeItem = item
+      this.allDisposalNodeChild.forEach(item => {
+        if(item.id ==  this.nodeItem.id){
+          this.itemChildNum = item.children.length
+        }
+      })
+    }
+
+    selectedBuildingData = this.data.selectedBuildingData
+    selectedSiteData = this.data.selectedSiteData
+    onSubmit(value,type){
+      // console.log(type)
+      let name = this.selectedBuildingData.name + '-' + this.selectedSiteData.name
+      //如果保存到已有节点
+      var postdata = {
+        id: "",
+        name: name,
+        level: 0,
+        order: this.itemChildNum,
+        description: "",
+        notes: "",
+        weather: null,
+        airTemperature: null,
+        windDirection: null,
+        windScale: null,
+        imageNames: null,
+        imageUrls: null,
+        parentId:  this.nodeItem ? this.nodeItem.id : null,
+        disasterId: this.data.disasterId,
+        planComponentId: sessionStorage.getItem('planId') || '',
+        companyId: this.data.siteOrbuilding == -1 ? sessionStorage.getItem('companyId') : null,
+        sitePlanId:  this.data.siteOrbuilding==-1 ? this.selectedSiteData.id : null,
+        buildingId: this.selectedBuildingData.id || null,
+        buildingAreaId: this.data.siteOrbuilding!=-1 ? this.selectedSiteData.id : null
+      }
+      if(type == 'old'){
+        let istrue = this.canvasData.findDisposalNode(this.nodeItem.id,name)
+        let putdata = this.nodeItem
+            putdata.weather = this.canvasData.selectPanelPointBaseData.weather
+            putdata.airTemperature = Number(this.canvasData.selectPanelPointBaseData.airTemperature)
+            putdata.windScale = Number(this.canvasData.selectPanelPointBaseData.windScale) 
+            putdata.windDirection = Number(this.canvasData.selectPanelPointBaseData.windDirection) 
+            putdata.description = this.canvasData.selectPanelPointBaseData.description
+            putdata.notes = this.canvasData.selectPanelPointBaseData.notes
+        
+        
+        if(istrue){//如果该处置节点下已有同名数据节点 则只修改 2个接口
+          new Promise((resolve,reject)=>{
+            this.http.put(`/api/DisposalNodes/${value.nodeId}`,putdata).subscribe(data => {
+              resolve("更新处置节点成功,将天气 节点详情等信息保存到点击的节点")
+            })
+          }).then((values)=>{
+            this.canvasData.sendMessage('send a message');//发布一条消息
+              // 保存平面图数据到当前节点
+              let postdata =JSON.parse(JSON.stringify(this.canvasData.selectPanelPoint)) 
+              postdata.Data = JSON.stringify(postdata.Data)
+              this.http.post(`/api/DisposalNodeData`,postdata).subscribe(data => {
+                const config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config)
+              },err=>{
+                const config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存失败','确定',config)
+              })
+              this.dialogRef.close();
+              this.canvasData.sendMessage('send a message');//发布一条消息
+          })
+          
+        }else{//需要3个接口
+          new Promise((resolve,reject)=>{
+            this.http.put(`/api/DisposalNodes/${value.nodeId}`,putdata).subscribe(data => {
+              resolve("更新处置节点成功,将天气 节点详情等信息保存到点击的节点")
+            })
+          }).then((values)=>{
+            console.log(values)
+            postdata.level = putdata.level + 1 
+            new Promise((resolve,reject) => {
+              this.http.post(`/api/DisposalNodes`,postdata).subscribe(data => {
+                resolve(data)
+              })
+            }).then((data:any)=>{
+              console.log(7788,data)
+              let objData = {
+                id: "",
+                data: JSON.stringify(this.canvasData.selectPanelPoint.Data) || null,
+                version: this.canvasData.selectPanelPoint.Version || "2.0",
+                disposalNodeId: data.id,
+                planComponentId: sessionStorage.getItem("planId"),
+              }
+              this.http.post(`/api/DisposalNodeData`,objData).subscribe(data => {
+                const config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config)
+              },err=>{
+                const config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存失败','确定',config)
+              })
+              this.dialogRef.close();
+              this.canvasData.sendMessage('send a message');//发布一条消息
+            })
+          })
+        }
+
+      }else{//如果保存到新建节点
+        let dispositionNodeData //处置节点data
+        let order
+        let oneLevelNum = []
+        //将order赋值为所有一级节点最后一个+1
+        this.allDisposalNode.forEach(item => {
+          if(!item.parentId){
+            oneLevelNum.push(item)
+          }
+        })
+        if(oneLevelNum.length == 0){
+          order = 0
+        }else{
+          order = oneLevelNum[oneLevelNum.length - 1].order + 1
+        }
+        
+        if(this.nodeItem){//如果点击了下拉选择框
+          if(this.nodeItem.id != null){
+            this.allDisposalNodeChild.forEach(item => {
+              if(item.id == this.nodeItem.id){
+                order = item.children.length
+              }
+            })
+          }
+        }
+        dispositionNodeData = {
+          id: "",
+          name: value.name,
+          level: this.nodeItem && this.nodeItem.id != null ? this.nodeItem.level + 1 : 0,
+          order: order,
+          description: "",
+          notes: "",
+          weather: null,
+          airTemperature: 0,
+          windDirection: 0,
+          windScale: 0,
+          imageNames: null,
+          imageUrls: null,
+          parentId: this.nodeItem ? this.nodeItem.id : null,
+          disasterId: this.data.disasterId,
+          planComponentId: sessionStorage.getItem('planId') || '',
+          companyId: null,
+          sitePlanId: null,
+          buildingId: null,
+          buildingAreaId: null
+        }
+
+        dispositionNodeData.weather = this.canvasData.selectPanelPointBaseData.weather
+        dispositionNodeData.airTemperature = Number(this.canvasData.selectPanelPointBaseData.airTemperature)
+        dispositionNodeData.windScale = Number(this.canvasData.selectPanelPointBaseData.windScale) 
+        dispositionNodeData.windDirection = Number(this.canvasData.selectPanelPointBaseData.windDirection) 
+        dispositionNodeData.description = this.canvasData.selectPanelPointBaseData.description
+        dispositionNodeData.notes = this.canvasData.selectPanelPointBaseData.notes
+        //1.先创建一个处置节点  然后 .then  2.创建数据节点到刚创建的处置节点  3.然后拿着创建好的数据节点的id  将平面图data保存
+        new Promise((resolve,reject) => {
+          this.http.post("/api/DisposalNodes",dispositionNodeData).subscribe((data:any) => {
+            resolve(data.id)
+          })
+        }).then((id) => {
+          let dataNodeData 
+          console.log("qnm",id)
+          new Promise((resolve,reject) => {
+            postdata.parentId = id
+            postdata.level = dispositionNodeData.level + 1
+            this.http.post("/api/DisposalNodes",postdata).subscribe((data:any) => {
+              resolve(data)
+            })
+          }).then((data:any) => {
+            // 保存平面图数据到当前节点
+              // console.log(6666,data)
+              // let postdata =JSON.parse(JSON.stringify(this.canvasData.selectPanelPoint)) 
+              // postdata.Data = JSON.stringify(postdata.Data)
+              let objData = {
+                id: "",
+                data: JSON.stringify(this.canvasData.selectPanelPoint.Data) || null,
+                version: this.canvasData.selectPanelPoint.Version || "2.0",
+                disposalNodeId: data.id,
+                planComponentId: sessionStorage.getItem("planId"),
+              }
+              
+              this.http.post(`/api/DisposalNodeData`,objData).subscribe(data => {
+                const config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存成功','确定',config)
+                
+              },err=>{
+                const config = new MatSnackBarConfig();
+                config.verticalPosition = 'top';
+                config.duration = 3000
+                this.snackBar.open('保存失败','确定',config)
+              })
+              this.dialogRef.close();
+              this.canvasData.sendMessage("send a message")
+          })
+        })
+      }
+    }
+}
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/saveOne.html b/src/app/ui/collection-tools/saveOne.html
new file mode 100644
index 0000000..a6b0b32
--- /dev/null
+++ b/src/app/ui/collection-tools/saveOne.html
@@ -0,0 +1,5 @@
+<div mat-dialog-title>处置节点保存</div>
+<div style="display: flex;">
+    <button mat-stroked-button style="margin-right: 5px;" (click)="saveType('new')">新建节点并保存</button>
+    <button mat-stroked-button (click)="saveType('old')">保存到已有节点</button>
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/saveTwo.html b/src/app/ui/collection-tools/saveTwo.html
new file mode 100644
index 0000000..fec97ff
--- /dev/null
+++ b/src/app/ui/collection-tools/saveTwo.html
@@ -0,0 +1,53 @@
+<div *ngIf="type == 'new'">
+    <div mat-dialog-title>新增节点</div>
+    <div>
+        <form (ngSubmit)="onSubmit(form.value,'new')" #form="ngForm" class="example-container">
+
+            <div mat-dialog-content>
+                <mat-form-field>
+                    <input type="text" matInput ngModel
+                    required name="name" placeholder="节点名称" autocomplete="off">
+                </mat-form-field>
+            </div>
+            <div mat-dialog-content>
+                <mat-form-field>
+                    <mat-select [(value)]="allRootDisposalNode[0].name" required placeholder="父节点名称">
+                        <mat-option *ngFor="let item of allRootDisposalNode" [value]="item.name" (click)="clickNode(item)">
+                            {{item.name}}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
+            </div>  
+            <div mat-dialog-actions>
+                <button mat-raised-button color="primary" type="submit" 
+                [disabled]="!form.form.valid">
+                    确定
+                </button>
+                <button mat-raised-button mat-dialog-close>取消</button>
+            </div>
+        </form>
+    </div>
+</div>
+<div *ngIf="type == 'old'">
+    <div mat-dialog-title>保存到已有节点</div>
+    <div>
+        <form (ngSubmit)="onSubmit(form.value,'old')" #form="ngForm" class="example-container">
+            <div mat-dialog-content>
+                <mat-form-field>
+                    <mat-select required ngModel placeholder="父节点名称" name="nodeId">
+                        <mat-option *ngFor="let item of allPlanDisposalNode" [value]="item.id"  (click)="clickNode(item)">
+                            {{item.name}}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
+            </div>  
+            <div mat-dialog-actions>
+                <button mat-raised-button color="primary" type="submit" 
+                [disabled]="!form.form.valid">
+                    确定
+                </button>
+                <button mat-raised-button mat-dialog-close>取消</button>
+            </div>
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/ui/collection-tools/viewdetails.html b/src/app/ui/collection-tools/viewdetails.html
new file mode 100644
index 0000000..76638c2
--- /dev/null
+++ b/src/app/ui/collection-tools/viewdetails.html
@@ -0,0 +1,24 @@
+<div style="position: relative;width: 1400px;height: 800px;line-height: 800px;" class="swiper-container">
+    <div style="position: absolute;right: -2px;top: -392px;cursor: pointer;z-index: 999;width: 24px;height: 24px;" (click)="closeDialog()">
+        <span><mat-icon>clear</mat-icon></span>
+    </div>
+    
+    <div class="swiper-wrapper">
+        <div class="swiper-slide" style="text-align: center;" *ngFor="let img of imagesArr">
+            <img id="bigimg" (mousewheel)="zoomimg($event)" style="
+            max-width: 96%;
+            max-height: 100%;
+            min-width: 1px;
+            min-height: 1px;
+            position: absolute;
+            top: 0;
+            left: 0;
+            bottom: 0;
+            right: 0;
+            margin: auto;" [src]="img.PropertyValue" alt="">
+        </div>
+    </div>
+      <!-- 如果需要导航按钮 -->
+    <div class="swiper-button-next"></div>
+    <div class="swiper-button-prev"></div>
+</div>
\ No newline at end of file
diff --git a/src/app/ui/ui.module.ts b/src/app/ui/ui.module.ts
index e911908..e0a90db 100644
--- a/src/app/ui/ui.module.ts
+++ b/src/app/ui/ui.module.ts
@@ -65,8 +65,13 @@ import { StatisticAnalysisComponent } from './statistic-analysis/statistic-analy
 import { JoinExamComponent } from './join-exam/join-exam.component';
 import { TestRecordsComponent } from './test-records/test-records.component'
 import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
+import {CollectionToolsComponent,CreateBuilding,EditBuilding,ViewDetailss,} from './collection-tools/collection-tools.component'
+import {leftFunctionalDomainComponent,editPlaneFigureComponent,addDisposalNodeComponent,editDisposalNodeComponent} from './collection-tools/leftFunctionalDomain'
+import {saveOneDialog,saveTwoDialog} from './collection-tools/save'
+import {WorkingAreaComponent} from '../working-area/working-area.component'
+import { NzTreeModule } from 'ng-zorro-antd/tree';
 @NgModule({
-  declarations: [FolderDialog,ViewDetails,ChangepasswordComponent,SizePipe,NamePipe,NamePipe2,NamePipe3,ConfirmpswDirective, AllFileComponent, ChangeuserdataComponent, UploadFilesComponent,AddEnterpriserUser,EnterpriseuserComponent,editenterpriseuser,seeenterpriseuser,TeacherManagementComponent,editTeacher,AddTeacher,seeTeacher, LearningRecordDetailsComponent, EhartsStatisticsComponent, CreateExamComponent, LookOverTestComponent, StatisticAnalysisComponent, JoinExamComponent, TestRecordsComponent,testState,CreateDialog],
+  declarations: [FolderDialog,ViewDetails,ChangepasswordComponent,SizePipe,NamePipe,NamePipe2,NamePipe3,ConfirmpswDirective, AllFileComponent, ChangeuserdataComponent, UploadFilesComponent,AddEnterpriserUser,EnterpriseuserComponent,editenterpriseuser,seeenterpriseuser,TeacherManagementComponent,editTeacher,AddTeacher,seeTeacher, LearningRecordDetailsComponent, EhartsStatisticsComponent, CreateExamComponent, LookOverTestComponent, StatisticAnalysisComponent, JoinExamComponent, TestRecordsComponent,testState,CreateDialog,CollectionToolsComponent,CreateBuilding,EditBuilding,ViewDetailss,leftFunctionalDomainComponent,editPlaneFigureComponent,addDisposalNodeComponent,editDisposalNodeComponent,saveOneDialog,saveTwoDialog,WorkingAreaComponent],
 
   imports: [
     NzDatePickerModule,
@@ -115,7 +120,8 @@ import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
     ScrollingModule,
     ReactiveFormsModule,
     FormsModule,
-    FileUploadModule
+    FileUploadModule,
+    NzTreeModule
   ],
   exports: [
     AllFileComponent
diff --git a/src/app/working-area/charm.js b/src/app/working-area/charm.js
new file mode 100644
index 0000000..a87804d
--- /dev/null
+++ b/src/app/working-area/charm.js
@@ -0,0 +1,836 @@
+export class Charm {
+  constructor(renderingEngine = PIXI) {
+
+    if (renderingEngine === undefined) throw new Error("Please assign a rendering engine in the constructor before using charm.js");
+
+    //Find out which rendering engine is being used (the default is Pixi)
+    this.renderer = "";
+
+    //If the `renderingEngine` is Pixi, set up Pixi object aliases
+    if (renderingEngine.ParticleContainer && renderingEngine.Sprite) {
+      this.renderer = "pixi";
+    }
+
+
+    //An array to store the global tweens
+    this.globalTweens = [];
+
+    //An object that stores all the easing formulas
+    this.easingFormulas = {
+
+      //Linear
+      linear(x) {
+        return x;
+      },
+
+      //Smoothstep
+      smoothstep(x) {
+        return x * x * (3 - 2 * x);
+      },
+      smoothstepSquared(x) {
+        return Math.pow((x * x * (3 - 2 * x)), 2);
+      },
+      smoothstepCubed(x) {
+        return Math.pow((x * x * (3 - 2 * x)), 3);
+      },
+
+      //Acceleration
+      acceleration(x) {
+        return x * x;
+      },
+      accelerationCubed(x) {
+        return Math.pow(x * x, 3);
+      },
+
+      //Deceleration
+      deceleration(x) {
+        return 1 - Math.pow(1 - x, 2);
+      },
+      decelerationCubed(x) {
+        return 1 - Math.pow(1 - x, 3);
+      },
+
+      //Sine
+      sine(x) {
+        return Math.sin(x * Math.PI / 2);
+      },
+      sineSquared(x) {
+        return Math.pow(Math.sin(x * Math.PI / 2), 2);
+      },
+      sineCubed(x) {
+        return Math.pow(Math.sin(x * Math.PI / 2), 2);
+      },
+      inverseSine(x) {
+        return 1 - Math.sin((1 - x) * Math.PI / 2);
+      },
+      inverseSineSquared(x) {
+        return 1 - Math.pow(Math.sin((1 - x) * Math.PI / 2), 2);
+      },
+      inverseSineCubed(x) {
+        return 1 - Math.pow(Math.sin((1 - x) * Math.PI / 2), 3);
+      },
+
+      //Spline
+      spline(t, p0, p1, p2, p3) {
+        return 0.5 * (
+          (2 * p1) +
+          (-p0 + p2) * t +
+          (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
+          (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t
+        );
+      },
+
+      //Bezier curve
+      cubicBezier(t, a, b, c, d) {
+        let t2 = t * t;
+        let t3 = t2 * t;
+        return a + (-a * 3 + t * (3 * a - a * t)) * t + (3 * b + t * (-6 * b + b * 3 * t)) * t + (c * 3 - c * 3 * t) * t2 + d * t3;
+      }
+    };
+
+    //Add `scaleX` and `scaleY` properties to Pixi sprites
+    this._addScaleProperties = (sprite) => {
+      if (this.renderer === "pixi") {
+        if (!("scaleX" in sprite) && ("scale" in sprite) && ("x" in sprite.scale)) {
+          Object.defineProperty(
+            sprite,
+            "scaleX", {
+            get() {
+              return sprite.scale.x
+            },
+            set(value) {
+              sprite.scale.x = value
+            }
+          }
+          );
+        }
+        if (!("scaleY" in sprite) && ("scale" in sprite) && ("y" in sprite.scale)) {
+          Object.defineProperty(
+            sprite,
+            "scaleY", {
+            get() {
+              return sprite.scale.y
+            },
+            set(value) {
+              sprite.scale.y = value
+            }
+          }
+          );
+        }
+      }
+    };
+  }
+
+  //The low level `tweenProperty` function is used as the foundation
+  //for the the higher level tween methods.
+  tweenProperty(
+    sprite, //Sprite object
+    property, //String property
+    startValue, //Tween start value
+    endValue, //Tween end value
+    totalFrames, //Duration in frames
+    type = "smoothstep", //The easing type
+    yoyo = false, //Yoyo?
+    delayBeforeRepeat = 0 //Delay in frames before repeating
+  ) {
+
+    //Create the tween object
+    let o = {};
+
+    //If the tween is a bounce type (a spline), set the
+    //start and end magnitude values
+    let typeArray = type.split(" ");
+    if (typeArray[0] === "bounce") {
+      o.startMagnitude = parseInt(typeArray[1]);
+      o.endMagnitude = parseInt(typeArray[2]);
+    }
+
+    //Use `o.start` to make a new tween using the current
+    //end point values
+    o.start = (startValue, endValue) => {
+
+      //Clone the start and end values so that any possible references to sprite
+      //properties are converted to ordinary numbers 
+      o.startValue = JSON.parse(JSON.stringify(startValue));
+      o.endValue = JSON.parse(JSON.stringify(endValue));
+      o.playing = true;
+      o.totalFrames = totalFrames;
+      o.frameCounter = 0;
+
+      //Add the tween to the global `tweens` array. The `tweens` array is
+      //updated on each frame
+      this.globalTweens.push(o);
+    };
+
+    //Call `o.start` to start the tween
+    o.start(startValue, endValue);
+
+    //The `update` method will be called on each frame by the game loop.
+    //This is what makes the tween move
+    o.update = () => {
+
+      let time, curvedTime;
+
+      if (o.playing) {
+
+        //If the elapsed frames are less than the total frames,
+        //use the tweening formulas to move the sprite
+        if (o.frameCounter < o.totalFrames) {
+
+          //Find the normalized value
+          let normalizedTime = o.frameCounter / o.totalFrames;
+
+          //Select the correct easing function from the 
+          //`ease` object’s library of easing functions
+
+
+          //If it's not a spline, use one of the ordinary easing functions
+          if (typeArray[0] !== "bounce") {
+            curvedTime = this.easingFormulas[type](normalizedTime);
+          }
+
+          //If it's a spline, use the `spline` function and apply the
+          //2 additional `type` array values as the spline's start and
+          //end points
+          else {
+            curvedTime = this.easingFormulas.spline(normalizedTime, o.startMagnitude, 0, 1, o.endMagnitude);
+          }
+
+          //Interpolate the sprite's property based on the curve
+          sprite[property] = (o.endValue * curvedTime) + (o.startValue * (1 - curvedTime));
+
+          o.frameCounter += 1;
+        }
+
+        //When the tween has finished playing, run the end tasks
+        else {
+          sprite[property] = o.endValue;
+          o.end();
+        }
+      }
+    };
+
+    //The `end` method will be called when the tween is finished
+    o.end = () => {
+
+      //Set `playing` to `false`
+      o.playing = false;
+
+      //Call the tween's `onComplete` method, if it's been assigned
+      if (o.onComplete) o.onComplete();
+
+      //Remove the tween from the `tweens` array
+      this.globalTweens.splice(this.globalTweens.indexOf(o), 1);
+
+      //If the tween's `yoyo` property is `true`, create a new tween
+      //using the same values, but use the current tween's `startValue`
+      //as the next tween's `endValue` 
+      if (yoyo) {
+        this.wait(delayBeforeRepeat).then(() => {
+          o.start(o.endValue, o.startValue);
+        });
+      }
+    };
+
+    //Pause and play methods
+    o.play = () => o.playing = true;
+    o.pause = () => o.playing = false;
+
+    //Return the tween object
+    return o;
+  }
+
+  //`makeTween` is a general low-level method for making complex tweens
+  //out of multiple `tweenProperty` functions. Its one argument,
+  //`tweensToAdd` is an array containing multiple `tweenProperty` calls
+
+  makeTween(tweensToAdd) {
+
+    //Create an object to manage the tweens
+    let o = {};
+
+    //Create a `tweens` array to store the new tweens
+    o.tweens = [];
+
+    //Make a new tween for each array
+    tweensToAdd.forEach(tweenPropertyArguments => {
+
+      //Use the tween property arguments to make a new tween
+      let newTween = this.tweenProperty(...tweenPropertyArguments);
+
+      //Push the new tween into this object's internal `tweens` array
+      o.tweens.push(newTween);
+    });
+
+    //Add a counter to keep track of the
+    //number of tweens that have completed their actions
+    let completionCounter = 0;
+
+    //`o.completed` will be called each time one of the tweens
+    //finishes
+    o.completed = () => {
+
+      //Add 1 to the `completionCounter`
+      completionCounter += 1;
+
+      //If all tweens have finished, call the user-defined `onComplete`
+      //method, if it's been assigned. Reset the `completionCounter`
+      if (completionCounter === o.tweens.length) {
+        if (o.onComplete) o.onComplete();
+        completionCounter = 0;
+      }
+    };
+
+    //Add `onComplete` methods to all tweens
+    o.tweens.forEach(tween => {
+      tween.onComplete = () => o.completed();
+    });
+
+    //Add pause and play methods to control all the tweens
+    o.pause = () => {
+      o.tweens.forEach(tween => {
+        tween.playing = false;
+      });
+    };
+    o.play = () => {
+      o.tweens.forEach(tween => {
+        tween.playing = true;
+      });
+    };
+
+    //Return the tween object
+    return o;
+  }
+
+  /* High level tween methods */
+
+  //1. Simple tweens
+
+  //`fadeOut`
+  fadeOut(sprite, frames = 60) {
+    return this.tweenProperty(
+      sprite, "alpha", sprite.alpha, 0, frames, "sine"
+    );
+  }
+
+  //`fadeIn`
+  fadeIn(sprite, frames = 60) {
+    return this.tweenProperty(
+      sprite, "alpha", sprite.alpha, 1, frames, "sine"
+    );
+  }
+
+  //`pulse`
+  //Fades the sprite in and out at a steady rate.
+  //Set the `minAlpha` to something greater than 0 if you
+  //don't want the sprite to fade away completely
+  pulse(sprite, frames = 60, minAlpha = 0) {
+    return this.tweenProperty(
+      sprite, "alpha", sprite.alpha, minAlpha, frames, "smoothstep", true
+    );
+  }
+
+  //2. Complex tweens
+
+  slide(
+    sprite, endX, endY,
+    frames = 60, type = "smoothstep", yoyo = false, delayBeforeRepeat = 0
+  ) {
+    return this.makeTween([
+
+      //Create the x axis tween
+      [sprite, "x", sprite.x, endX, frames, type, yoyo, delayBeforeRepeat],
+
+      //Create the y axis tween
+      [sprite, "y", sprite.y, endY, frames, type, yoyo, delayBeforeRepeat]
+
+    ]);
+  }
+
+  breathe(
+    sprite, endScaleX = 0.8, endScaleY = 0.8,
+    frames = 60, yoyo = true, delayBeforeRepeat = 0
+  ) {
+
+    //Add `scaleX` and `scaleY` properties to Pixi sprites
+    this._addScaleProperties(sprite);
+
+    return this.makeTween([
+
+      //Create the scaleX tween
+      [
+        sprite, "scaleX", sprite.scaleX, endScaleX,
+        frames, "smoothstepSquared", yoyo, delayBeforeRepeat
+      ],
+
+      //Create the scaleY tween
+      [
+        sprite, "scaleY", sprite.scaleY, endScaleY,
+        frames, "smoothstepSquared", yoyo, delayBeforeRepeat
+      ]
+    ]);
+  }
+
+  scale(sprite, endScaleX = 0.5, endScaleY = 0.5, frames = 60) {
+
+    //Add `scaleX` and `scaleY` properties to Pixi sprites
+    this._addScaleProperties(sprite);
+
+    return this.makeTween([
+
+      //Create the scaleX tween
+      [
+        sprite, "scaleX", sprite.scaleX, endScaleX,
+        frames, "smoothstep", false
+      ],
+
+      //Create the scaleY tween
+      [
+        sprite, "scaleY", sprite.scaleY, endScaleY,
+        frames, "smoothstep", false
+      ]
+    ]);
+  }
+
+  strobe(
+    sprite, scaleFactor = 1.3, startMagnitude = 10, endMagnitude = 20,
+    frames = 10, yoyo = true, delayBeforeRepeat = 0
+  ) {
+
+    let bounce = "bounce " + startMagnitude + " " + endMagnitude;
+
+    //Add `scaleX` and `scaleY` properties to Pixi sprites
+    this._addScaleProperties(sprite);
+
+    return this.makeTween([
+
+      //Create the scaleX tween
+      [
+        sprite, "scaleX", sprite.scaleX, scaleFactor, frames,
+        bounce, yoyo, delayBeforeRepeat
+      ],
+
+      //Create the scaleY tween
+      [
+        sprite, "scaleY", sprite.scaleY, scaleFactor, frames,
+        bounce, yoyo, delayBeforeRepeat
+      ]
+    ]);
+  }
+
+  wobble(
+    sprite,
+    scaleFactorX = 1.2,
+    scaleFactorY = 1.2,
+    frames = 10,
+    xStartMagnitude = 10,
+    xEndMagnitude = 10,
+    yStartMagnitude = -10,
+    yEndMagnitude = -10,
+    friction = 0.98,
+    yoyo = true,
+    delayBeforeRepeat = 0
+  ) {
+
+    let bounceX = "bounce " + xStartMagnitude + " " + xEndMagnitude;
+    let bounceY = "bounce " + yStartMagnitude + " " + yEndMagnitude;
+
+    //Add `scaleX` and `scaleY` properties to Pixi sprites
+    this._addScaleProperties(sprite);
+
+    let o = this.makeTween([
+
+      //Create the scaleX tween
+      [
+        sprite, "scaleX", sprite.scaleX, scaleFactorX, frames,
+        bounceX, yoyo, delayBeforeRepeat
+      ],
+
+      //Create the scaleY tween
+      [
+        sprite, "scaleY", sprite.scaleY, scaleFactorY, frames,
+        bounceY, yoyo, delayBeforeRepeat
+      ]
+    ]);
+
+    //Add some friction to the `endValue` at the end of each tween 
+    o.tweens.forEach(tween => {
+      tween.onComplete = () => {
+
+        //Add friction if the `endValue` is greater than 1
+        if (tween.endValue > 1) {
+          tween.endValue *= friction;
+
+          //Set the `endValue` to 1 when the effect is finished and 
+          //remove the tween from the global `tweens` array
+          if (tween.endValue <= 1) {
+            tween.endValue = 1;
+            this.removeTween(tween);
+          }
+        }
+      };
+    });
+
+    return o;
+  }
+
+  //3. Motion path tweens
+
+  followCurve(
+    sprite,
+    pointsArray,
+    totalFrames,
+    type = "smoothstep",
+    yoyo = false,
+    delayBeforeRepeat = 0
+  ) {
+
+    //Create the tween object
+    let o = {};
+
+    //If the tween is a bounce type (a spline), set the
+    //start and end magnitude values
+    let typeArray = type.split(" ");
+    if (typeArray[0] === "bounce") {
+      o.startMagnitude = parseInt(typeArray[1]);
+      o.endMagnitude = parseInt(typeArray[2]);
+    }
+
+    //Use `tween.start` to make a new tween using the current
+    //end point values
+    o.start = (pointsArray) => {
+      o.playing = true;
+      o.totalFrames = totalFrames;
+      o.frameCounter = 0;
+
+      //Clone the points array
+      o.pointsArray = JSON.parse(JSON.stringify(pointsArray));
+
+      //Add the tween to the `globalTweens` array. The `globalTweens` array is
+      //updated on each frame
+      this.globalTweens.push(o);
+    };
+
+    //Call `tween.start` to start the first tween
+    o.start(pointsArray);
+
+    //The `update` method will be called on each frame by the game loop.
+    //This is what makes the tween move
+    o.update = () => {
+
+      let normalizedTime, curvedTime,
+        p = o.pointsArray;
+
+      if (o.playing) {
+
+        //If the elapsed frames are less than the total frames,
+        //use the tweening formulas to move the sprite
+        if (o.frameCounter < o.totalFrames) {
+
+          //Find the normalized value
+          normalizedTime = o.frameCounter / o.totalFrames;
+
+          //Select the correct easing function
+
+          //If it's not a spline, use one of the ordinary tween
+          //functions
+          if (typeArray[0] !== "bounce") {
+            curvedTime = this.easingFormulas[type](normalizedTime);
+          }
+
+          //If it's a spline, use the `spline` function and apply the
+          //2 additional `type` array values as the spline's start and
+          //end points
+          else {
+            //curve = tweenFunction.spline(n, type[1], 0, 1, type[2]);
+            curvedTime = this.easingFormulas.spline(normalizedTime, o.startMagnitude, 0, 1, o.endMagnitude);
+          }
+
+          //Apply the Bezier curve to the sprite's position 
+          sprite.x = this.easingFormulas.cubicBezier(curvedTime, p[0][0], p[1][0], p[2][0], p[3][0]);
+          sprite.y = this.easingFormulas.cubicBezier(curvedTime, p[0][1], p[1][1], p[2][1], p[3][1]);
+
+          //Add one to the `elapsedFrames`
+          o.frameCounter += 1;
+        }
+
+        //When the tween has finished playing, run the end tasks
+        else {
+          //sprite[property] = o.endValue;
+          o.end();
+        }
+      }
+    };
+
+    //The `end` method will be called when the tween is finished
+    o.end = () => {
+
+      //Set `playing` to `false`
+      o.playing = false;
+
+      //Call the tween's `onComplete` method, if it's been
+      //assigned
+      if (o.onComplete) o.onComplete();
+
+      //Remove the tween from the global `tweens` array
+      this.globalTweens.splice(this.globalTweens.indexOf(o), 1);
+
+      //If the tween's `yoyo` property is `true`, reverse the array and
+      //use it to create a new tween
+      if (yoyo) {
+        this.wait(delayBeforeRepeat).then(() => {
+          o.pointsArray = o.pointsArray.reverse();
+          o.start(o.pointsArray);
+        });
+      }
+    };
+
+    //Pause and play methods
+    o.pause = () => {
+      o.playing = false;
+    };
+    o.play = () => {
+      o.playing = true;
+    };
+
+    //Return the tween object
+    return o;
+  }
+
+  walkPath(
+    sprite, //The sprite
+    originalPathArray, //A 2D array of waypoints
+    totalFrames = 300, //The duration, in frames
+    type = "smoothstep", //The easing type
+    loop = false, //Should the animation loop?
+    yoyo = false, //Shoud the direction reverse?
+    delayBetweenSections = 0 //Delay, in milliseconds, between sections
+  ) {
+
+    //Clone the path array so that any possible references to sprite
+    //properties are converted into ordinary numbers 
+    let pathArray = JSON.parse(JSON.stringify(originalPathArray));
+
+    //Figure out the duration, in frames, of each path section by 
+    //dividing the `totalFrames` by the length of the `pathArray`
+    let frames = totalFrames / pathArray.length;
+
+    //Set the current point to 0, which will be the first waypoint
+    let currentPoint = 0;
+
+    //The `makePath` function creates a single tween between two points and
+    //then schedules the next path to be made after it
+    let makePath = (currentPoint) => {
+
+      //Use the `makeTween` function to tween the sprite's
+      //x and y position
+      let tween = this.makeTween([
+
+        //Create the x axis tween between the first x value in the
+        //current point and the x value in the following point
+        [
+          sprite,
+          "x",
+          pathArray[currentPoint][0],
+          pathArray[currentPoint + 1][0],
+          frames,
+          type
+        ],
+
+        //Create the y axis tween in the same way
+        [
+          sprite,
+          "y",
+          pathArray[currentPoint][1],
+          pathArray[currentPoint + 1][1],
+          frames,
+          type
+        ]
+      ]);
+
+      //When the tween is complete, advance the `currentPoint` by one.
+      //Add an optional delay between path segments, and then make the
+      //next connecting path
+      tween.onComplete = () => {
+
+        //Advance to the next point
+        currentPoint += 1;
+
+        //If the sprite hasn't reached the end of the
+        //path, tween the sprite to the next point
+        if (currentPoint < pathArray.length - 1) {
+          this.wait(delayBetweenSections).then(() => {
+            tween = makePath(currentPoint);
+          });
+        }
+
+        //If we've reached the end of the path, optionally
+        //loop and yoyo it
+        else {
+
+          //Reverse the path if `loop` is `true`
+          if (loop) {
+
+            //Reverse the array if `yoyo` is `true`
+            if (yoyo) pathArray.reverse();
+
+            //Optionally wait before restarting
+            this.wait(delayBetweenSections).then(() => {
+
+              //Reset the `currentPoint` to 0 so that we can
+              //restart at the first point
+              currentPoint = 0;
+
+              //Set the sprite to the first point
+              sprite.x = pathArray[0][0];
+              sprite.y = pathArray[0][1];
+
+              //Make the first new path
+              tween = makePath(currentPoint);
+
+              //... and so it continues!
+            });
+          }
+        }
+      };
+
+      //Return the path tween to the main function
+      return tween;
+    };
+
+    //Make the first path using the internal `makePath` function (below)
+    let tween = makePath(currentPoint);
+
+    //Pass the tween back to the main program
+    return tween;
+  }
+
+  walkCurve(
+    sprite, //The sprite
+    pathArray, //2D array of Bezier curves
+    totalFrames = 300, //The duration, in frames
+    type = "smoothstep", //The easing type
+    loop = false, //Should the animation loop?
+    yoyo = false, //Should the direction reverse?
+    delayBeforeContinue = 0 //Delay, in milliseconds, between sections
+  ) {
+
+    //Divide the `totalFrames` into sections for each part of the path
+    let frames = totalFrames / pathArray.length;
+
+    //Set the current curve to 0, which will be the first one
+    let currentCurve = 0;
+
+    //The `makePath` function
+    let makePath = (currentCurve) => {
+
+      //Use the custom `followCurve` function to make
+      //a sprite follow a curve
+      let tween = this.followCurve(
+        sprite,
+        pathArray[currentCurve],
+        frames,
+        type
+      );
+
+      //When the tween is complete, advance the `currentCurve` by one.
+      //Add an optional delay between path segments, and then make the
+      //next path
+      tween.onComplete = () => {
+        currentCurve += 1;
+        if (currentCurve < pathArray.length) {
+          this.wait(delayBeforeContinue).then(() => {
+            tween = makePath(currentCurve);
+          });
+        }
+
+        //If we've reached the end of the path, optionally
+        //loop and reverse it
+        else {
+          if (loop) {
+            if (yoyo) {
+
+              //Reverse order of the curves in the `pathArray` 
+              pathArray.reverse();
+
+              //Reverse the order of the points in each curve
+              pathArray.forEach(curveArray => curveArray.reverse());
+            }
+
+            //After an optional delay, reset the sprite to the
+            //beginning of the path and make the next new path
+            this.wait(delayBeforeContinue).then(() => {
+              currentCurve = 0;
+              sprite.x = pathArray[0][0];
+              sprite.y = pathArray[0][1];
+              tween = makePath(currentCurve);
+            });
+          }
+        }
+      };
+
+      //Return the path tween to the main function
+      return tween;
+    };
+
+    //Make the first path
+    let tween = makePath(currentCurve);
+
+    //Pass the tween back to the main program
+    return tween;
+  }
+
+  //4. Utilities
+
+  /*
+  The `wait` method lets you set up a timed sequence of events
+
+    wait(1000)
+      .then(() => console.log("One"))
+      .then(() => wait(1000))
+      .then(() => console.log("Two"))
+      .then(() => wait(1000))
+      .then(() => console.log("Three"))
+
+  */
+
+  wait(duration = 0) {
+    return new Promise((resolve, reject) => {
+      setTimeout(resolve, duration);
+    });
+  }
+
+  //A utility to remove tweens from the game
+  removeTween(tweenObject) {
+
+    //Remove the tween if `tweenObject` doesn't have any nested
+    //tween objects
+    if (!tweenObject.tweens) {
+      tweenObject.pause();
+
+      //array.splice(-1,1) will always remove last elemnt of array, so this
+      //extra check prevents that (Thank you, MCumic10! https://github.com/kittykatattack/charm/issues/5)
+      if (this.globalTweens.indexOf(tweenObject) != -1) {
+        this.globalTweens.splice(this.globalTweens.indexOf(tweenObject), 1);
+      }
+
+      //Otherwise, remove the nested tween objects
+    } else {
+      tweenObject.pause();
+      tweenObject.tweens.forEach(element => {
+        this.globalTweens.splice(this.globalTweens.indexOf(element), 1);
+      });
+    }
+  }
+
+  update() {
+
+    //Update all the tween objects in the `globalTweens` array
+    if (this.globalTweens.length > 0) {
+      for (let i = this.globalTweens.length - 1; i >= 0; i--) {
+        let tween = this.globalTweens[i];
+        if (tween) tween.update();
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/app/working-area/model/PropertyInfo.ts b/src/app/working-area/model/PropertyInfo.ts
new file mode 100644
index 0000000..4ae2c6a
--- /dev/null
+++ b/src/app/working-area/model/PropertyInfo.ts
@@ -0,0 +1,62 @@
+/**
+ * 属性
+ */
+export class PropertyInfo {
+  constructor(instanceData: any) {
+    this.Tag = instanceData.tag;
+    this.Order = instanceData.order;
+    this.Enabled = instanceData.enabled;
+    this.Visible = instanceData.visible;
+    this.Required = instanceData.required;
+    this.RuleName = instanceData.ruleName;
+    this.RuleValue = instanceData.ruleValue;
+    this.PhysicalUnit = instanceData.physicalUnit;
+    this.PropertyName = instanceData.propertyName;
+    this.PropertyType = instanceData.propertyType;
+    this.PropertyValue = instanceData.propertyValue;
+  }
+  /**
+   * 标记位,用于扩展
+   */
+  public Tag: string;
+  /**
+   * 属性排序
+   */
+  public Order: number;
+  /**
+   * 是否启用
+   */
+  public Enabled: boolean;
+  /**
+   * 是否可见
+   */
+  public Visible: boolean;
+  /**
+   * 必填
+   */
+  public Required: boolean;
+  /**
+   * 验证规则名称
+   */
+  public RuleName: string;
+  /**
+   * 验证规则值
+   */
+  public RuleValue: string;
+  /**
+   * 物理单位
+   */
+  public PhysicalUnit: string;
+  /**
+   * 属性名称
+   */
+  public PropertyName: string;
+  /**
+   * 属性类型
+   */
+  public PropertyType: number;
+  /**
+   * 属性值
+   */
+  public PropertyValue: string;
+}
diff --git a/src/app/working-area/model/arrows.ts b/src/app/working-area/model/arrows.ts
new file mode 100644
index 0000000..a553771
--- /dev/null
+++ b/src/app/working-area/model/arrows.ts
@@ -0,0 +1,41 @@
+import { WorkingAreaComponent } from '../working-area.component';
+import * as PIXI from 'pixi.js';
+
+/**
+ * 箭头
+ * 创建一个只有2个点组成的箭头
+ */
+export class Arrows extends PIXI.Container {
+    public line: PIXI.Graphics = new PIXI.Graphics();
+    public ready = false;
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.workingArea.backgroundImage.addChild(this);
+        this.name = this.assetData.Id;
+        this.addChild(this.line);
+        this.refresh();
+        this.interactive = true;
+        this.on('mousedown', event => {
+            if (!this.ready) { return; }
+            event.stopPropagation();
+            this.workingArea.selection.selectOne(this);
+        });
+    }
+    /**
+     * 刷新
+     */
+    public refresh() {
+        this.line.clear();
+        this.line.lineStyle(5, 0xff0000, 1);
+        this.line.moveTo(this.assetData.pointA.x, this.assetData.pointA.y);
+        this.line.lineTo(this.assetData.pointB.x, this.assetData.pointB.y);
+
+        const angle = Math.atan2((this.assetData.pointB.y - this.assetData.pointA.y), (this.assetData.pointB.x - this.assetData.pointA.x))
+            * (180 / Math.PI) + 90;
+
+        this.line.beginFill(0xff0000);
+        console.log(Math.PI / 180 / 1.6);
+        this.line.drawStar(this.assetData.pointB.x, this.assetData.pointB.y, 3, 10, 0, (Math.PI / 180 * angle));
+        this.line.endFill();
+    }
+}
diff --git a/src/app/working-area/model/axImageShape.ts b/src/app/working-area/model/axImageShape.ts
new file mode 100644
index 0000000..99fca6c
--- /dev/null
+++ b/src/app/working-area/model/axImageShape.ts
@@ -0,0 +1,24 @@
+import * as PIXI from 'pixi.js';
+
+
+/**
+ * 安信形状
+ */
+export class AxImageShape extends PIXI.Container {
+    image: PIXI.Sprite;
+
+    constructor() {
+        super();
+
+    }
+
+
+    paintVertexShape(rect: PIXI.Rectangle) {
+
+    }
+    paintBackground(rect: PIXI.Rectangle) { }
+
+    paintForeground(rect: PIXI.Rectangle) { }
+
+    paintEdgeShape(pts: Array<PIXI.Point>) { }
+}
diff --git a/src/app/working-area/model/axShape.ts b/src/app/working-area/model/axShape.ts
new file mode 100644
index 0000000..f285c24
--- /dev/null
+++ b/src/app/working-area/model/axShape.ts
@@ -0,0 +1,56 @@
+import * as PIXI from 'pixi.js';
+// import { Point, Rectangle, Graphics } from 'pixi.js';
+
+/**
+ * 安信形状
+ */
+export class AxShape extends PIXI.Container {
+
+    points: Array<PIXI.Point> = [];
+    title: string;
+    titleVisible: boolean;
+    g: PIXI.Graphics = new PIXI.Graphics();
+
+    constructor() {
+        super();
+        this.addChild(this.g);
+        // this.drawDashedLine(this.g, new Point(0, 0), new Point(0, 200), 0xff0000);
+    }
+    // /**
+    //  * 绘制虚线
+    //  * @param g
+    //  * @param p0
+    //  * @param pe
+    //  * @param color
+    //  * @param width
+    //  * @param dashLen
+    //  */
+    // drawDashedLine(g: Graphics, p0: Point, pe: Point, color: number, width: number = 1, dashLen: number = 5) {
+    //         g.lineStyle(width, color);
+    //         const len = Math.sqrt(Math.pow(pe.x - p0.x, 2) + Math.pow(pe.y - p0.y, 2));
+    //         // tslint:disable-next-line: no-bitwise
+    //         const num = ~~(len / dashLen);
+    //         for (let i = 0; i < num; i++) {
+    //             const x = p0.x + (pe.x - p0.x) / num * i;
+    //             const y = p0.y + (pe.y - p0.y) / num * i;
+    //             // tslint:disable-next-line: no-bitwise
+    //             i & 1 ? g.lineTo(x, y) : g.moveTo(x, y);
+    //         }
+    // }
+
+    paintVertexShape(rect: PIXI.Rectangle) {
+        // this.paintBackground(c, x, y, w, h);
+
+        // if (!this.outline || this.style == null || mxUtils.getValue(
+        // 	this.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0)
+        // {
+        // 	c.setShadow(false);
+        // 	this.paintForeground(c, x, y, w, h);
+        // }
+    }
+    paintBackground(rect: PIXI.Rectangle) { }
+
+    paintForeground(rect: PIXI.Rectangle) { }
+
+    paintEdgeShape(pts: Array<PIXI.Point>) { }
+}
diff --git a/src/app/working-area/model/gameMode.ts b/src/app/working-area/model/gameMode.ts
new file mode 100644
index 0000000..feabdbb
--- /dev/null
+++ b/src/app/working-area/model/gameMode.ts
@@ -0,0 +1,7 @@
+/**
+ * 游戏状态
+ */
+export enum GameMode {
+  BasicInformation,
+  Assignment
+}
\ No newline at end of file
diff --git a/src/app/working-area/model/multipointIcon.ts b/src/app/working-area/model/multipointIcon.ts
new file mode 100644
index 0000000..d5467a7
--- /dev/null
+++ b/src/app/working-area/model/multipointIcon.ts
@@ -0,0 +1,248 @@
+import { WorkingAreaComponent } from '../working-area.component';
+import { GameMode } from './gameMode';
+import * as PIXI from 'pixi.js';
+
+/**
+ * 多点连线
+ */
+export class MultipointIcon extends PIXI.Container {
+    public pointsData: PIXI.Point[];
+    public pointsGraphics: PIXI.Graphics[] = [];
+    public iconsTilingSprite: PIXI.TilingSprite[] = [];
+    style = new PIXI.TextStyle({
+        fontFamily: 'Arial',
+        fontSize: 18,
+        fontStyle: 'normal',
+        fontWeight: 'bold',
+        fill: ['#000000'],
+        stroke: '#ffffff',
+        strokeThickness: 3,
+        dropShadow: true,
+        dropShadowColor: '#000000',
+        dropShadowBlur: 3,
+        dropShadowAngle: Math.PI / 6,
+        dropShadowDistance: 1,
+        wordWrap: false,
+        wordWrapWidth: 100,
+    });
+
+    public text = new PIXI.Text(this.assetData.Name
+        + '\r\n'
+        + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style);
+    /**
+     *
+     * @param texture 图片素材
+     * @param points  点集合
+     */
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.name = this.assetData.Id;
+        this.pointsData = this.assetData.MultiPoint;
+        this.x = this.assetData.Point.x;
+        this.y = this.assetData.Point.y;
+        this.workingArea.backgroundImage.addChild(this);
+        // 画线图标
+        for (let i = 0, count = this.pointsData.length - 1; i < count; i++) {
+            const pointA = this.pointsData[i];
+            const pointB = this.pointsData[i + 1];
+
+            const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI);
+            const a = pointB.x - pointA.x;
+            const b = pointB.y - pointA.y;
+            const distance = Math.sqrt(a * a + b * b);
+
+            const icon = new PIXI.TilingSprite(PIXI.Texture.from(this.assetData.ImageUrl), distance, 64);
+            icon.anchor.set(0, 0.5);
+            icon.x = pointA.x;
+            icon.y = pointA.y;
+            icon.angle = angle;
+            icon.height = this.assetData.Thickness === 0 ? 32 : this.assetData.Thickness;
+            this.iconsTilingSprite.push(icon);
+            this.addChild(icon);
+            if (i === 0) {
+                this.text.anchor.set(0.5);
+                this.text.position = icon.position;
+                this.text.y -= this.assetData.Height;
+                this.addChild(this.text);
+            }
+        }
+        // 画点
+        this.pointsData.forEach((item, index, array) => {
+            const iconPoint = new PIXI.Graphics();
+            iconPoint.lineStyle(1, 0xFFBD01, 1);
+            iconPoint.beginFill(0xFFFFFF, 1);
+            iconPoint.drawCircle(0, 0, 15);
+            iconPoint.x = item.x;
+            iconPoint.y = item.y;
+            iconPoint.endFill();
+            iconPoint.visible = false;
+            this.pointsGraphics.push(iconPoint);
+            this.addChild(iconPoint);
+        });
+        // 添加圆点事件
+        this.pointsGraphics.forEach((item, index, array) => {
+            item.interactive = true;
+            item.on('mousedown', event => {
+                event.stopPropagation();
+                if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) {
+                    event.currentTarget.data = event.data;
+                    event.currentTarget.alpha = 0.5;
+                    event.currentTarget.dragging = true;
+                }
+            })
+                .on('mouseup', event => {
+                    if (event.currentTarget.dragging) {
+                        event.currentTarget.alpha = 1;
+                        event.currentTarget.dragging = false;
+                        event.currentTarget.data = null;
+                    }
+                })
+                .on('mouseupoutside', event => {
+                    if (event.currentTarget.dragging) {
+                        event.currentTarget.alpha = 1;
+                        event.currentTarget.dragging = false;
+                        event.currentTarget.data = null;
+                    }
+                })
+                .on('mousemove', event => {
+                    if (event.currentTarget.dragging) {
+                        const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent);
+                        event.currentTarget.x = newPosition.x;
+                        event.currentTarget.y = newPosition.y;
+
+                        this.assetData.MultiPoint[index].x = newPosition.x;
+                        this.assetData.MultiPoint[index].y = newPosition.y;
+                        this.workingArea.canvasData.isChange = true;
+
+                        if (index === 0) {// 第一个点
+                            this.iconsTilingSprite[index].x = newPosition.x;
+                            this.iconsTilingSprite[index].y = newPosition.y;
+
+                            const pointA = array[index];
+                            const pointB = array[index + 1];
+
+                            const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI);
+                            const a = pointB.x - pointA.x;
+                            const b = pointB.y - pointA.y;
+                            const distance = Math.sqrt(a * a + b * b);
+                            this.iconsTilingSprite[index].angle = angle;
+                            this.iconsTilingSprite[index].width = distance;
+
+                            this.text.position = this.iconsTilingSprite[index].position;
+                            this.text.y -= this.assetData.Height;
+                        } else if (index < array.length - 1) {// 不是第一个点,也不是最后一个点
+                            this.iconsTilingSprite[index].x = newPosition.x;
+                            this.iconsTilingSprite[index].y = newPosition.y;
+
+                            const pointA = array[index]; // 当前点
+                            const pointB = array[index + 1]; // 后一个点
+                            const pointC = array[index - 1]; // 前一个点
+
+                            const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI);
+                            const a = pointB.x - pointA.x;
+                            const b = pointB.y - pointA.y;
+                            const distance = Math.sqrt(a * a + b * b);
+                            this.iconsTilingSprite[index].angle = angle;
+                            this.iconsTilingSprite[index].width = distance;
+
+                            const angleC = Math.atan2((pointA.y - pointC.y), (pointA.x - pointC.x)) * (180 / Math.PI);
+                            const aC = pointA.x - pointC.x;
+                            const bC = pointA.y - pointC.y;
+                            const distanceC = Math.sqrt(aC * aC + bC * bC);
+                            this.iconsTilingSprite[index - 1].angle = angleC;
+                            this.iconsTilingSprite[index - 1].width = distanceC;
+                        } else if (index === array.length - 1) { // 最后一个点
+                            const pointA = array[index]; // 当前点
+                            const pointC = array[index - 1]; // 前一个点
+
+                            const angleC = Math.atan2((pointA.y - pointC.y), (pointA.x - pointC.x)) * (180 / Math.PI);
+                            const aC = pointA.x - pointC.x;
+                            const bC = pointA.y - pointC.y;
+                            const distanceC = Math.sqrt(aC * aC + bC * bC);
+                            this.iconsTilingSprite[index - 1].angle = angleC;
+                            this.iconsTilingSprite[index - 1].width = distanceC;
+                        }
+                    }
+                })
+                .on('rightclick', event => {
+                })
+                .on('mouseover', event => {
+
+                });
+        });
+        // // 缩放
+        // this.workingArea.on('backgroundScale', data => {
+        //   const scale = 1 / data;
+        //   this.text.scale.set(scale);
+        // });
+        // 添加选中事件
+        this.iconsTilingSprite.forEach((item, index, array) => {
+            item.interactive = true;
+            item.on('mousedown', event => {
+                event.stopPropagation();
+                this.workingArea.selection.selectOne(this);
+                if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) {
+                    event.currentTarget.parent.data = event.data;
+                    event.currentTarget.parent.alpha = 0.5;
+                    event.currentTarget.parent.dragging = true;
+
+                    event.currentTarget.parent.dragPoint = event.data.getLocalPosition(event.currentTarget.parent.parent);
+                    event.currentTarget.parent.dragPoint.x -= event.currentTarget.parent.x;
+                    event.currentTarget.parent.dragPoint.y -= event.currentTarget.parent.y;
+                }
+            })
+                .on('mouseup', event => {
+                    if (event.currentTarget.parent.dragging) {
+                        event.currentTarget.parent.alpha = 1;
+                        event.currentTarget.parent.dragging = false;
+                        event.currentTarget.parent.data = null;
+                    }
+                })
+                .on('mouseupoutside', event => {
+                    if (event.currentTarget.parent.dragging) {
+                        event.currentTarget.parent.alpha = 1;
+                        event.currentTarget.parent.dragging = false;
+                        event.currentTarget.parent.data = null;
+                    }
+                })
+                .on('mousemove', event => {
+                    if (event.currentTarget.parent.dragging) {
+                        const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent);
+                        event.currentTarget.parent.x = newPosition.x - event.currentTarget.parent.dragPoint.x;
+                        event.currentTarget.parent.y = newPosition.y - event.currentTarget.parent.dragPoint.y;
+
+                        this.assetData.Point = new PIXI.Point(this.x, this.y);
+                        this.workingArea.canvasData.isChange = true;
+                    }
+                })
+                .on('rightclick', event => {
+
+                });
+        });
+    }
+    /**
+     * 设置点显示状态
+     * @param value 显示状态
+     */
+    public setPointVisiable(value: boolean) {
+        this.pointsGraphics.forEach((item) => {
+            item.visible = value;
+        });
+    }
+    // 设置名称
+    public setNameVisible(value: boolean, mode: GameMode) {
+        if (this.assetData.GameMode === mode) {
+            this.text.visible = value;
+        }
+    }
+    // 刷新数据
+    public refresh() {
+        console.log(this.assetData);
+        this.iconsTilingSprite.forEach(element => {
+            element.height = this.assetData.Thickness === 0 ? 32 : this.assetData.Thickness;
+        });
+        this.text.text = this.assetData.Name
+            + '\r\n'
+            + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue;
+    }
+}
\ No newline at end of file
diff --git a/src/app/working-area/model/paintModel.ts b/src/app/working-area/model/paintModel.ts
new file mode 100644
index 0000000..dc0b031
--- /dev/null
+++ b/src/app/working-area/model/paintModel.ts
@@ -0,0 +1,33 @@
+/**
+ * 绘制模式
+ */
+export enum PaintMode {
+    /**
+     * 单点图标
+     */
+    singlePointIcon,
+    /**
+     * 线段图标
+     */
+    lineIcon,
+    /**
+     * 自定义多边形
+     */
+    polygonIcon,
+    /**
+     * 水带多边形
+     */
+    Pipeline,
+    /**
+     * 暂无
+     */
+    Arrows,
+    /**
+     * 暂无
+     */
+    Car,
+    /**
+     * 结束绘制
+     */
+    endPaint,
+}
diff --git a/src/app/working-area/model/pipeline.ts b/src/app/working-area/model/pipeline.ts
new file mode 100644
index 0000000..904732a
--- /dev/null
+++ b/src/app/working-area/model/pipeline.ts
@@ -0,0 +1,332 @@
+import { WorkingAreaComponent } from '../working-area.component';
+import * as PIXI from 'pixi.js';
+
+/**
+ * 管线
+ */
+export class Pipeline extends PIXI.Container {
+    public line: PIXI.Graphics = new PIXI.Graphics();
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.name = this.assetData.Id;
+        this.x = this.assetData.Point.x;
+        this.y = this.assetData.Point.y;
+        this.workingArea.backgroundImage.addChild(this);
+        this.addChild(this.line);
+        // 画线图标
+        this.refresh();
+        this.interactive = true;
+        this.on('mousedown', event => {
+            event.stopPropagation();
+            this.workingArea.selection.selectOne(this);
+        });
+    }
+    /**
+     * 刷新
+     */
+    public refresh() {
+
+        const strokeWidth = 1;
+        const startWidth = 30 + strokeWidth;
+        const endWidth = 30 + strokeWidth;
+        const edgeWidth = 10;
+        const openEnded = false;
+        const markerStart = false;
+        const markerEnd = true;
+        const spacing = (openEnded) ? 0 : 0 + strokeWidth / 2;
+        const startSize = 30 + strokeWidth;
+        const endSize = 30 + strokeWidth;
+        const isRounded = true;
+        const pts = this.assetData.MultiPoint;
+        const c = this.line;
+        if (pts.length < 2) { return; }
+            // Base vector (between first points)
+        const pe = pts[pts.length - 1];
+
+            // Finds first non-overlapping point
+        let i0 = 1;
+
+        while (i0 < pts.length - 1 && pts[i0].x === pts[0].x && pts[i0].y === pts[0].y) {
+            i0++;
+            }
+
+        const dx = pts[i0].x - pts[0].x;
+        const dy = pts[i0].y - pts[0].y;
+        const dist = Math.sqrt(dx * dx + dy * dy);
+
+        if (dist === 0) {
+            return;
+            }
+
+        // Computes the norm and the inverse norm
+        let nx = dx / dist;
+        let nx1 = nx;
+        let nx2 = nx;
+        let ny = dy / dist;
+        let ny2 = ny;
+        let ny1 = ny;
+        let orthx = edgeWidth * ny;
+        let orthy = -edgeWidth * nx;
+
+        // Stores the inbound function calls in reverse order in fns
+        const fns = [];
+
+        // if (isRounded) {
+        //     // c.setLineJoin('round');
+        //     c.lineTextureStyle({ join: PIXI.LINE_JOIN.ROUND });
+        // } else if (pts.length > 2) {
+        // // Only mitre if there are waypoints
+        //     // c.setMiterLimit(1.42);
+        //     c.lineTextureStyle({ miterLimit: 1.42 });
+        // }
+        // c.lineStyle(1, 0x000000, 1);
+        c.clear();
+        c.lineTextureStyle({ width: 1, color: 0x00000, join: PIXI.LINE_JOIN.ROUND });
+        // c.begin();
+        c.beginFill(0xffffff);
+        const startNx = nx;
+        const startNy = ny;
+
+        if (markerStart && !openEnded) {
+            this.paintMarker(c, pts[0].x, pts[0].y, nx, ny, startSize, startWidth, edgeWidth, spacing, true);
+        } else {
+            const outStartX = pts[0].x + orthx / 2 + spacing * nx;
+            const outStartY = pts[0].y + orthy / 2 + spacing * ny;
+            const inEndX = pts[0].x - orthx / 2 + spacing * nx;
+            const inEndY = pts[0].y - orthy / 2 + spacing * ny;
+
+            if (openEnded) {
+                c.moveTo(outStartX, outStartY);
+                fns.push( () => {
+                c.lineTo(inEndX, inEndY);
+                });
+            } else {
+                c.moveTo(inEndX, inEndY);
+                c.lineTo(outStartX, outStartY);
+            }
+        }
+        let dx1 = 0;
+        let dy1 = 0;
+        let dist1 = 0;
+
+        for (let i = 0; i < pts.length - 2; i++) {
+        // Work out in which direction the line is bending
+        const pos = this.relativeCcw(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, pts[i + 2].x, pts[i + 2].y);
+
+        dx1 = pts[i + 2].x - pts[i + 1].x;
+        dy1 = pts[i + 2].y - pts[i + 1].y;
+
+        dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
+
+        if (dist1 !== 0) {
+            nx1 = dx1 / dist1;
+            ny1 = dy1 / dist1;
+
+            const tmp1 = nx * nx1 + ny * ny1;
+            const tmp = Math.max(Math.sqrt((tmp1 + 1) / 2), 0.04);
+
+            // Work out the normal orthogonal to the line through the control point and the edge sides intersection
+            nx2 = (nx + nx1);
+            ny2 = (ny + ny1);
+
+            const dist2 = Math.sqrt(nx2 * nx2 + ny2 * ny2);
+
+            if (dist2 !== 0) {
+                nx2 = nx2 / dist2;
+                ny2 = ny2 / dist2;
+
+                // Higher strokewidths require a larger minimum bend, 0.35 covers all but the most extreme cases
+                const strokeWidthFactor = Math.max(tmp, Math.min(1 / 200 + 0.04, 0.35));
+                const angleFactor = (pos !== 0 && isRounded) ? Math.max(0.1, strokeWidthFactor) : Math.max(tmp, 0.06);
+
+                const outX = pts[i + 1].x + ny2 * edgeWidth / 2 / angleFactor;
+                const outY = pts[i + 1].y - nx2 * edgeWidth / 2 / angleFactor;
+                const inX = pts[i + 1].x - ny2 * edgeWidth / 2 / angleFactor;
+                const inY = pts[i + 1].y + nx2 * edgeWidth / 2 / angleFactor;
+
+                if (pos === 0 || !isRounded) {
+                    // If the two segments are aligned, or if we're not drawing curved sections between segments
+                    // just draw straight to the intersection point
+                    c.lineTo(outX, outY);
+
+                    ((x, y) => {
+                        fns.push(() => {
+                            c.lineTo(x, y);
+                        });
+                    })(inX, inY);
+                } else if (pos === -1) {
+                    const c1x = inX + ny * edgeWidth;
+                    const c1y = inY - nx * edgeWidth;
+                    const c2x = inX + ny1 * edgeWidth;
+                    const c2y = inY - nx1 * edgeWidth;
+                    c.lineTo(c1x, c1y);
+                    if (isRounded) {
+                        c.quadraticCurveTo(outX, outY, c2x, c2y); // 圆角
+                    } else {
+                        c.lineTo(outX, outY);
+                    }
+                    ((x, y) => {
+                        fns.push(() => {
+                            c.lineTo(x, y);
+                        });
+                    })(inX, inY);
+                } else {
+                    c.lineTo(outX, outY);
+
+                    ((x, y) => {
+                        const c1x = outX - ny * edgeWidth;
+                        const c1y = outY + nx * edgeWidth;
+                        const c2x = outX - ny1 * edgeWidth;
+                        const c2y = outY + nx1 * edgeWidth;
+
+                        fns.push(() => {
+                            if (isRounded) {
+                                c.quadraticCurveTo(x, y, c1x, c1y);
+                            } else {
+                                c.lineTo(x, y);
+                            }
+                        });
+                        fns.push(() => {
+                            c.lineTo(c2x, c2y);
+                        });
+                            })(inX, inY);
+                }
+
+                nx = nx1;
+                ny = ny1;
+                }
+            }
+        }
+        orthx = edgeWidth * ny1;
+        orthy = - edgeWidth * nx1;
+
+        if (markerEnd && !openEnded) {
+            this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, false);
+        } else {
+            c.lineTo(pe.x - spacing * nx1 + orthx / 2, pe.y - spacing * ny1 + orthy / 2);
+
+            const inStartX = pe.x - spacing * nx1 - orthx / 2;
+            const inStartY = pe.y - spacing * ny1 - orthy / 2;
+
+            if (!openEnded) {
+                c.lineTo(inStartX, inStartY);
+            } else {
+                c.moveTo(inStartX, inStartY);
+
+                fns.splice(0, 0, () => {
+                    c.moveTo(inStartX, inStartY);
+                });
+            }
+        }
+
+        for (let i = fns.length - 1; i >= 0; i--) {
+            fns[i]();
+        }
+        c.closePath();
+        c.endFill();
+        // if (openEnded)
+        // {
+        //     c.end();
+        //     c.stroke();
+        // }
+        // else
+        // {
+        //     c.close();
+        //     c.fillAndStroke();
+        // }
+
+        // Workaround for shadow on top of base arrow
+        // c.setShadow(false);
+
+        // Need to redraw the markers without the low miter limit
+        // c.setMiterLimit(4);
+
+        // if (isRounded)
+        // {
+        //     c.setLineJoin('flat');
+        // }
+
+        // if (pts.length > 2) {
+        //     // Only to repaint markers if no waypoints
+        //     // Need to redraw the markers without the low miter limit
+        //     // c.setMiterLimit(4);
+        //     c.lineTextureStyle({ width: 1, color: 0x00000, miterLimit: 4 });
+        //     if (markerStart && !openEnded) {
+        //         // c.begin();
+        //         this.paintMarker(c, pts[0].x, pts[0].y, startNx, startNy, startSize, startWidth, edgeWidth, spacing, true);
+        //         // c.stroke();
+        //         // c.end();
+        //         // c.closePath();
+        //     }
+
+        //     if (markerEnd && !openEnded) {
+        //         // c.begin();
+        //         this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, true);
+        //         // c.stroke();
+        //         // c.end();
+        //         // c.closePath();
+        //     }
+        // }
+    }
+    /**
+     * Function: paintMarker
+     *
+     * Paints the marker.
+     */
+    paintMarker(c: PIXI.Graphics, ptX: number, ptY: number, nx: number, ny: number,
+                size: number, arrowWidth: number, edgeWidth: number, spacing: number, initialMove: boolean) {
+        const widthArrowRatio = edgeWidth / arrowWidth;
+        const orthx = edgeWidth * ny / 2;
+        const orthy = -edgeWidth * nx / 2;
+
+        const spaceX = (spacing + size) * nx;
+        const spaceY = (spacing + size) * ny;
+
+        if (initialMove) {
+            c.moveTo(ptX - orthx + spaceX, ptY - orthy + spaceY);
+        } else {
+            c.lineTo(ptX - orthx + spaceX, ptY - orthy + spaceY);
+        }
+        c.lineTo(ptX - orthx / widthArrowRatio + spaceX, ptY - orthy / widthArrowRatio + spaceY);
+        c.lineTo(ptX + spacing * nx, ptY + spacing * ny);
+        c.lineTo(ptX + orthx / widthArrowRatio + spaceX, ptY + orthy / widthArrowRatio + spaceY);
+        c.lineTo(ptX + orthx + spaceX, ptY + orthy + spaceY);
+    }
+    /**
+     * Function: relativeCcw
+     *
+     * Returns 1 if the given point on the right side of the segment, 0 if its
+     * on the segment, and -1 if the point is on the left side of the segment.
+     *
+     * Parameters:
+     *
+     * x1 - X-coordinate of the startpoint of the segment.
+     * y1 - Y-coordinate of the startpoint of the segment.
+     * x2 - X-coordinate of the endpoint of the segment.
+     * y2 - Y-coordinate of the endpoint of the segment.
+     * px - X-coordinate of the point.
+     * py - Y-coordinate of the point.
+     */
+    relativeCcw(x1: number, y1: number, x2: number, y2: number, px: number, py: number) {
+        x2 -= x1;
+        y2 -= y1;
+        px -= x1;
+        py -= y1;
+        let ccw = px * y2 - py * x2;
+
+        if (ccw === 0.0) {
+            ccw = px * x2 + py * y2;
+
+            if (ccw > 0.0) {
+                px -= x2;
+                py -= y2;
+                ccw = px * x2 + py * y2;
+
+                if (ccw < 0.0) {
+                    ccw = 0.0;
+                }
+            }
+        }
+        return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0);
+    }
+}
diff --git a/src/app/working-area/model/polygonIcon.ts b/src/app/working-area/model/polygonIcon.ts
new file mode 100644
index 0000000..3c604df
--- /dev/null
+++ b/src/app/working-area/model/polygonIcon.ts
@@ -0,0 +1,244 @@
+import { WorkingAreaComponent } from '../working-area.component';
+import { GameMode } from './gameMode';
+import * as PIXI from 'pixi.js';
+
+/**
+ * 多边形
+ */
+export class PolygonIcon extends PIXI.Container {
+    public pointsData: PIXI.Point[];
+    public pointsGraphics: PIXI.Graphics[] = [];
+    public polygonGraphics: PIXI.Graphics = new PIXI.Graphics();
+    public polygonLineGraphics: PIXI.Graphics = new PIXI.Graphics();
+    style = new PIXI.TextStyle({
+        fontFamily: 'Arial',
+        fontSize: 18,
+        fontStyle: 'normal',
+        fontWeight: 'bold',
+        fill: ['#000000'],
+        stroke: '#ffffff',
+        strokeThickness: 3,
+        dropShadow: true,
+        dropShadowColor: '#000000',
+        dropShadowBlur: 3,
+        dropShadowAngle: Math.PI / 6,
+        dropShadowDistance: 1,
+        wordWrap: false,
+        wordWrapWidth: 100,
+    });
+
+    public text = new PIXI.Text(this.assetData.Name
+        + '\r\n'
+        + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style);
+    /**
+     *
+     * @param points  点集合
+     */
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.name = this.assetData.Id;
+        this.x = this.assetData.Point.x;
+        this.y = this.assetData.Point.y;
+        this.pointsData = this.assetData.MultiPoint;
+        this.workingArea.backgroundImage.addChild(this);
+        this.sortableChildren = true;
+        // 画点
+        this.pointsData.forEach((item, index, array) => {
+            const iconPoint = new PIXI.Graphics();
+            iconPoint.lineStyle(1, 0xFFBD01, 1);
+            iconPoint.beginFill(0xFFFFFF, 1);
+            iconPoint.drawCircle(0, 0, 15);
+            iconPoint.x = item.x;
+            iconPoint.y = item.y;
+            iconPoint.endFill();
+            iconPoint.visible = false;
+            this.pointsGraphics.push(iconPoint);
+            this.addChild(iconPoint);
+        });
+        // 填充多边形
+
+        const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x');
+        const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255;
+        this.polygonGraphics.beginFill(color, angle);
+        this.polygonGraphics.drawPolygon(this.getPoints());
+        this.polygonGraphics.endFill();
+        this.addChild(this.polygonGraphics);
+        // 画多边形
+        this.polygonLineGraphics.lineStyle(5, 0xFFBD01, 1);
+        this.polygonLineGraphics.drawPolygon(this.getPoints());
+        this.polygonLineGraphics.closePath();
+        this.addChild(this.polygonLineGraphics);
+
+        this.text.anchor.set(0.5);
+        this.text.position = this.calculatePolygonGravityCenter(this.pointsData);
+        // console.log(this.calculatePolygonGravityCenter(this.pointsData));
+        this.polygonGraphics.addChild(this.text);
+        // 添加圆点事件
+        this.pointsGraphics.forEach((item, index, array) => {
+            item.interactive = true;
+            item.zIndex = 1;
+            item.on('mousedown', event => {
+                event.stopPropagation();
+                if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) {
+                    event.currentTarget.data = event.data;
+                    event.currentTarget.alpha = 0.5;
+                    event.currentTarget.dragging = true;
+                }
+            })
+                .on('mouseup', event => {
+                    if (event.currentTarget.dragging) {
+                        event.currentTarget.alpha = 1;
+                        event.currentTarget.dragging = false;
+                        event.currentTarget.data = null;
+                    }
+                })
+                .on('mouseupoutside', event => {
+                    if (event.currentTarget.dragging) {
+                        event.currentTarget.alpha = 1;
+                        event.currentTarget.dragging = false;
+                        event.currentTarget.data = null;
+                    }
+                })
+                .on('mousemove', event => {
+                    if (event.currentTarget.dragging) {
+                        const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent);
+                        event.currentTarget.x = newPosition.x;
+                        event.currentTarget.y = newPosition.y;
+
+                        this.assetData.MultiPoint[index].x = newPosition.x;
+                        this.assetData.MultiPoint[index].y = newPosition.y;
+                        this.workingArea.canvasData.isChange = true;
+                        // 填充多边形
+                        this.polygonGraphics.clear();
+                        this.polygonGraphics.beginFill(color, angle);
+                        this.polygonGraphics.drawPolygon(this.getPoints());
+                        this.polygonGraphics.endFill();
+                        // 画多边形
+                        this.polygonLineGraphics.clear();
+                        this.polygonLineGraphics.lineStyle(5, 0xFFBD01, 1);
+                        this.polygonLineGraphics.drawPolygon(this.getPoints());
+                        this.polygonLineGraphics.closePath();
+
+                        this.text.position = this.calculatePolygonGravityCenter(this.pointsData);
+                    }
+                })
+                .on('rightclick', event => {
+                });
+        });
+        // 添加选中事件
+        this.polygonGraphics.interactive = true;
+        this.polygonGraphics
+            .on('mousedown', event => {
+                event.stopPropagation();
+                this.workingArea.selection.selectOne(this);
+                if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) {
+                    event.currentTarget.parent.data = event.data;
+                    event.currentTarget.parent.alpha = 0.5;
+                    event.currentTarget.parent.dragging = true;
+
+                    event.currentTarget.parent.dragPoint = event.data.getLocalPosition(event.currentTarget.parent.parent);
+                    event.currentTarget.parent.dragPoint.x -= event.currentTarget.parent.x;
+                    event.currentTarget.parent.dragPoint.y -= event.currentTarget.parent.y;
+                }
+            })
+            .on('mouseup', event => {
+                if (event.currentTarget.parent.dragging) {
+                    event.currentTarget.parent.alpha = 1;
+                    event.currentTarget.parent.dragging = false;
+                    event.currentTarget.parent.data = null;
+                }
+            })
+            .on('mouseupoutside', event => {
+                if (event.currentTarget.parent.dragging) {
+                    event.currentTarget.parent.alpha = 1;
+                    event.currentTarget.parent.dragging = false;
+                    event.currentTarget.parent.data = null;
+                }
+            })
+            .on('mousemove', event => {
+                if (event.currentTarget.parent.dragging) {
+                    const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent);
+                    event.currentTarget.parent.x = newPosition.x - event.currentTarget.parent.dragPoint.x;
+                    event.currentTarget.parent.y = newPosition.y - event.currentTarget.parent.dragPoint.y;
+
+                    this.assetData.Point = new PIXI.Point(this.x, this.y);
+                    this.workingArea.canvasData.isChange = true;
+                }
+            })
+            .on('rightclick', event => {
+                // this.workingArea.selection.deselectAll();
+            });
+        // // 缩放
+        // this.workingArea.on('backgroundScale', data => {
+        //   const scale = 1 / data;
+        //   this.text.scale.set(scale);
+        // });
+    }
+    /**
+     * 设置点显示状态
+     * @param value 显示状态
+     */
+    public setPointVisiable(value: boolean) {
+        this.pointsGraphics.forEach((item) => {
+            item.visible = value;
+        });
+    }
+
+    public calculatePolygonGravityCenter(points: PIXI.Point[]) {
+        let area = 0.0; // 多边形面积
+        let gravityLat = 0.0; // 重心点 latitude
+        let gravityLng = 0.0; // 重心点 longitude
+        points.forEach((item, index) => {
+            // 1
+            const lat = item.x;
+            const lng = item.y;
+            const nextLat = points[(index + 1) % points.length].x;
+            const nextLng = points[(index + 1) % points.length].y;
+            // 2
+            const tempArea = (nextLat * lng - nextLng * lat) / 2.0;
+            // 3
+            area += tempArea;
+            // 4
+            gravityLat += tempArea * (lat + nextLat) / 3;
+            gravityLng += tempArea * (lng + nextLng) / 3;
+        });
+        // 5
+        gravityLat = gravityLat / area;
+        gravityLng = gravityLng / area;
+
+        return new PIXI.Point(gravityLat, gravityLng);
+    }
+    /**
+     * 获取点集合
+     */
+    public getPoints(): PIXI.Point[] {
+        const points: PIXI.Point[] = [];
+        this.pointsGraphics.forEach(item => {
+            points.push(item.position);
+        });
+        return points;
+    }
+    /**
+     * 设置名称显示
+     * @param value true/false 显示/隐藏
+     * @param mode BasicInformation = 0 基本信息
+     *             Assignment想定作业 = 1 想定作业
+     */
+    public setNameVisible(value: boolean, mode: GameMode) {
+        if (this.assetData.GameMode === mode) {
+            this.text.visible = value;
+        }
+    }
+    public refresh() {
+        this.text.text = this.assetData.Name
+            + '\r\n'
+            + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue;
+        // 填充多边形
+        const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x');
+        const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255;
+        this.polygonGraphics.clear();
+        this.polygonGraphics.beginFill(color, angle);
+        this.polygonGraphics.drawPolygon(this.getPoints());
+        this.polygonGraphics.endFill();
+    }
+}
\ No newline at end of file
diff --git a/src/app/working-area/model/putCarArea.ts b/src/app/working-area/model/putCarArea.ts
new file mode 100644
index 0000000..7544d04
--- /dev/null
+++ b/src/app/working-area/model/putCarArea.ts
@@ -0,0 +1,59 @@
+import { OldFilmFilter } from 'pixi-filters';
+import { WorkingAreaComponent } from '../working-area.component';
+import { PaintMode } from './paintModel';
+import { SinglePointIcon } from './singlePointIcon';
+import * as PIXI from 'pixi.js';
+
+/**
+ * 汽车放置区域
+ */
+export class PutCarArea extends PIXI.Container {
+    public polygonGraphics: PIXI.Graphics = new PIXI.Graphics();
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.name = this.assetData.Id;
+        this.x = this.assetData.Point.x;
+        this.y = this.assetData.Point.y;
+        this.workingArea.backgroundImage.addChild(this);
+        this.sortableChildren = true;
+
+        // 填充多边形
+
+        const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x');
+        const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255;
+        this.polygonGraphics.beginFill(color, angle);
+        this.polygonGraphics.drawPolygon(this.assetData.MultiPoint);
+        this.polygonGraphics.endFill();
+        this.addChild(this.polygonGraphics);
+        // 添加选中事件
+        this.polygonGraphics.interactive = true;
+        this.polygonGraphics
+            .on('pointerdown', (event) => {
+                if (this.workingArea.getPaintMode() === PaintMode.Car) {
+                    this.workingArea.selectCar.Point =
+                        new PIXI.Point(this.workingArea.previewSinglePointIcon.x, this.workingArea.previewSinglePointIcon.y);
+                    this.workingArea.selectCar.Angle = this.assetData.Direction;
+                    const car = new SinglePointIcon(this.workingArea.selectCar, this.workingArea);
+                    this.workingArea.setPaintMode(PaintMode.endPaint);
+                }
+            })
+            .on('pointerup', (event) => {
+
+            })
+            .on('pointerupoutside', (event) => {
+
+            })
+            .on('pointerover', (event) => {
+                this.workingArea.previewSinglePointIcon.filters = null;
+                this.workingArea.previewSinglePointIcon.zIndex = this.zIndex + 1;
+                // 设置车辆方向
+                this.workingArea.previewSinglePointIcon.angle = this.assetData.Direction;
+                console.log(this.assetData.Name);
+            })
+            .on('pointerout', (event) => {
+                this.workingArea.previewSinglePointIcon.filters = [
+                    new OldFilmFilter()
+                ];
+            });
+    }
+}
\ No newline at end of file
diff --git a/src/app/working-area/model/singlePointIcon.ts b/src/app/working-area/model/singlePointIcon.ts
new file mode 100644
index 0000000..8574b9e
--- /dev/null
+++ b/src/app/working-area/model/singlePointIcon.ts
@@ -0,0 +1,373 @@
+import { WorkingAreaComponent } from '../working-area.component';
+import * as ObjectID from 'bson-objectid';
+import { GameMode } from './gameMode';
+import { Pipeline } from './pipeline';
+import { PaintMode } from './paintModel';
+import * as PIXI from 'pixi.js';
+import { PropertyInfo } from './PropertyInfo';
+import { AxShape } from './axShape';
+
+/**
+ * 单点图标
+ */
+export class SinglePointIcon extends AxShape {
+    style = new PIXI.TextStyle({
+        fontFamily: 'Arial',
+        fontSize: 18,
+        fontStyle: 'normal',
+        fontWeight: 'bold',
+        fill: ['#000000'],
+        stroke: '#ffffff',
+        strokeThickness: 3,
+        dropShadow: true,
+        dropShadowColor: '#000000',
+        dropShadowBlur: 3,
+        dropShadowAngle: Math.PI / 6,
+        dropShadowDistance: 1,
+        wordWrap: false,
+        wordWrapWidth: 100,
+    });
+
+    text = new PIXI.Text(this.assetData.Name
+        + '\r\n'
+        + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style);
+
+    /**
+     * 选中圆点
+     */
+    selectedPointTexture = PIXI.Texture.from('assets/images/handle-secondary.png');
+    image = PIXI.Sprite.from(this.assetData.ImageUrl);
+    selectionBox = new PIXI.Graphics();
+    up: PIXI.Sprite;
+    down: PIXI.Sprite;
+    left: PIXI.Sprite;
+    right: PIXI.Sprite;
+    upLeft: PIXI.Sprite;
+    upRight: PIXI.Sprite;
+    downLeft: PIXI.Sprite;
+    downRight: PIXI.Sprite;
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.workingArea.backgroundImage.addChild(this);
+        this.x = this.assetData.Point.x;
+        this.y = this.assetData.Point.y;
+        this.name = this.assetData.Id;
+
+        this.image.angle = this.assetData.Angle;
+
+        this.image.x = 0;
+        this.image.y = 0;
+        this.image.width = this.assetData.Width;
+        this.image.height = this.assetData.Height;
+        console.log(this.getBounds());
+        this.image.alpha = 1;
+        this.image.anchor.set(0.5);
+        this.image.interactive = true;
+        this.image
+            .on('mousedown', event => {
+                event.stopPropagation();
+                this.workingArea.selection.selectOne(this);
+                this.paintingPipeline(this.x, this.y);
+                // 如果链接对象不为空,禁止移动
+                if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) {
+                    event.currentTarget.parent.data = event.data;
+                    event.currentTarget.parent.alpha = 0.5;
+                    event.currentTarget.parent.dragging = true;
+                }
+            })
+            .on('mouseup', event => {
+                if (event.currentTarget.parent.dragging) {
+                    event.currentTarget.parent.alpha = 1;
+                    event.currentTarget.parent.dragging = false;
+                    event.currentTarget.parent.data = null;
+                }
+            })
+            .on('mouseupoutside', event => {
+                if (event.currentTarget.parent.dragging) {
+                    event.currentTarget.parent.alpha = 1;
+                    event.currentTarget.parent.dragging = false;
+                    event.currentTarget.parent.data = null;
+                }
+            })
+            .on('mousemove', event => {
+                if (event.currentTarget.parent.dragging) {
+                    // 如果拖动过程中发现父对象不是背景图
+                    if (this.parent !== this.workingArea.backgroundImage) {
+                        this.setParent(this.workingArea.backgroundImage);
+                        if (this.assetData.FixedSize) {
+                            const scale = 1 / this.workingArea.backgroundImage.scale.x;
+                            this.scale.set(scale);
+                        }
+                    }
+                    const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent);
+                    event.currentTarget.parent.x = newPosition.x;
+                    event.currentTarget.parent.y = newPosition.y;
+                    this.assetData.Point = new PIXI.Point(this.x, this.y);
+                    this.workingArea.canvasData.isChange = true;
+                }
+            })
+            .on('rightclick', event => {
+
+            })
+            .on('mouseover', event => {
+                // if (this.assetData.CanConnect) {
+                //     this.setSelectionBox(true, this.image);
+                // }
+            })
+            .on('mouseout', event => {
+                // if (this.assetData.CanConnect) {
+                //     this.setSelectionBox(false);
+                // }
+            });
+        this.text.x = this.image.x;
+        this.text.y = this.image.y - this.image.height / 2;
+        this.text.anchor.set(0.5, 1);
+
+        if (this.assetData.GameMode === 2) {
+            this.text.visible = false;
+        }
+        this.addChild(this.text);
+        this.addChild(this.image);
+        this.addChild(this.selectionBox);
+
+        if (this.assetData.CanConnect) {
+            // up
+            this.up = new PIXI.Sprite(this.selectedPointTexture);
+            this.up.anchor.set(0.5);
+            this.up.x = this.image.x;
+            this.up.y = this.image.y - (this.image.height / 2);
+            this.addChild(this.up);
+            this.up.interactive = true;
+            this.up
+                .on('mousedown', event => {
+                    event.stopPropagation();
+                    const pt = this.toGlobal(new PIXI.Point(this.up.x, this.up.y));
+                    const pt2 = this.workingArea.backgroundImage.toLocal(pt);
+                    this.paintingPipeline(pt2.x, pt2.y);
+                })
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.up);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+            // down
+            this.down = new PIXI.Sprite(this.selectedPointTexture);
+            this.down.anchor.set(0.5);
+            this.down.x = this.image.x;
+            this.down.y = this.image.y + (this.image.height / 2);
+            this.addChild(this.down);
+            this.down.interactive = true;
+            this.down
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.down);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+            // left
+            this.left = new PIXI.Sprite(this.selectedPointTexture);
+            this.left.anchor.set(0.5);
+            this.left.x = this.image.x - (this.image.width / 2);
+            this.left.y = this.image.y;
+            this.addChild(this.left);
+            this.left.interactive = true;
+            this.left
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.left);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+            // right
+            this.right = new PIXI.Sprite(this.selectedPointTexture);
+            this.right.anchor.set(0.5);
+            this.right.x = this.image.x + (this.image.width / 2);
+            this.right.y = this.image.y;
+            this.addChild(this.right);
+            this.right.interactive = true;
+            this.right
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.right);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+            // up-left
+            this.upLeft = new PIXI.Sprite(this.selectedPointTexture);
+            this.upLeft.anchor.set(0.5);
+            this.upLeft.x = this.image.x - (this.image.width / 2);
+            this.upLeft.y = this.image.y - (this.image.height / 2);
+            this.addChild(this.upLeft);
+            this.upLeft.interactive = true;
+            this.upLeft
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.upLeft);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+            // up-right
+            this.upRight = new PIXI.Sprite(this.selectedPointTexture);
+            this.upRight.anchor.set(0.5);
+            this.upRight.x = this.image.x + (this.image.width / 2);
+            this.upRight.y = this.image.y - (this.image.height / 2);
+            this.addChild(this.upRight);
+            this.upRight.interactive = true;
+            this.upRight
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.upRight);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+
+            // down-left
+            this.downLeft = new PIXI.Sprite(this.selectedPointTexture);
+            this.downLeft.anchor.set(0.5);
+            this.downLeft.x = this.image.x - (this.image.width / 2);
+            this.downLeft.y = this.image.y + (this.image.height / 2);
+            this.addChild(this.downLeft);
+            this.downLeft.interactive = true;
+            this.downLeft
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.downLeft);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+            // down-right
+            this.downRight = new PIXI.Sprite(this.selectedPointTexture);
+            this.downRight.anchor.set(0.5);
+            this.downRight.x = this.image.x + (this.image.width / 2);
+            this.downRight.y = this.image.y + (this.image.height / 2);
+            this.addChild(this.downRight);
+            this.downRight.interactive = true;
+            this.downRight
+                .on('mouseover', event => {
+                    this.setSelectionBox(true, this.downRight);
+                })
+                .on('mouseout', event => {
+                    this.setSelectionBox(false);
+                });
+
+            this.showConnectionPoint(false);
+        }
+    }
+    // 设置选择框
+    public setSelectionBox(b: boolean, sprite?: PIXI.Sprite) {
+        if (b) {
+            this.selectionBox.lineStyle(2, 0x00EB00, 1);
+            this.selectionBox.position = sprite.position;
+            this.selectionBox.drawRect(- sprite.width / 2, - sprite.height / 2, sprite.width, sprite.height);
+            // const p0 = new PIXI.Point(- sprite.width / 2, - sprite.height / 2);
+            // const pe = new PIXI.Point(sprite.width / 2, sprite.height / 2);
+            // const pw = new PIXI.Point(p0.x + sprite.width, p0.y);
+            // const ph = new PIXI.Point(p0.x, p0.y + sprite.height);
+            // this.drawDashedLine(this.selectionBox, p0, pw, 0x1234ff);
+            // this.drawDashedLine(this.selectionBox, p0, ph, 0x1234ff);
+            // this.drawDashedLine(this.selectionBox, pe, pw, 0x1234ff);
+            // this.drawDashedLine(this.selectionBox, pe, ph, 0x1234ff);
+        } else {
+            this.selectionBox.clear();
+        }
+    }
+    // 设置名称
+    public setNameVisible(value: boolean, mode: GameMode) {
+        if (this.assetData.GameMode === mode) {
+            this.text.visible = value;
+        }
+    }
+    // 显示连接点
+    public showConnectionPoint(b: boolean) {
+        this.up.visible = b;
+        this.down.visible = b;
+        this.left.visible = b;
+        this.right.visible = b;
+        this.upLeft.visible = b;
+        this.downLeft.visible = b;
+        this.upRight.visible = b;
+        this.downRight.visible = b;
+    }
+    paintingPipeline(x: number, y: number) {
+        if (this.assetData.CanConnect) {
+                    if (this.workingArea.getPaintMode() === PaintMode.Pipeline) {
+                        if (this.workingArea.paintingPipeline === null) {
+                            this.workingArea.previewLineSegment.visible = true;
+                            this.workingArea.currentClickPoint.position =
+                                new PIXI.Point(this.workingArea.circleShadow.x, this.workingArea.circleShadow.y);
+                            this.workingArea.paintPoints.push(new PIXI.Point(x, y));
+                            // const tempData = {
+                            //     Id: ObjectID.default.generate(),
+                            //     MultiPoint: JSON.parse(JSON.stringify(this.workingArea.paintPoints)),
+                            //     Point: new PIXI.Point(0, 0),
+                            //     Name: '管线',
+                            //     LinkedObjects: new Array(),
+                            // };
+                            const json = JSON.parse(JSON.stringify(this.workingArea.canvasData.selectTemplateData.propertyInfos));
+                            const list = [];
+                            json.forEach(element => {
+                                const property = new PropertyInfo(element);
+                                list.push(property);
+                            });
+                            const tempData =  {
+                                TemplateId: this.workingArea.canvasData.selectTemplateData.id,
+                                CanConnect: this.workingArea.canvasData.selectTemplateData.canConnect,
+                                Pipelines: new Array(),
+                                FloorId: this.workingArea.canvasData.selectStorey.id,
+                                Angle: this.workingArea.canvasData.selectTemplateData.angle,
+                                Color: this.workingArea.canvasData.selectTemplateData.color,
+                                Enabled: this.workingArea.canvasData.selectTemplateData.enabled,
+                                FillMode: this.workingArea.canvasData.selectTemplateData.fillMode,
+                                FireElementId: this.workingArea.canvasData.selectTemplateData.fireElementId,
+                                FixedSize: this.workingArea.canvasData.selectTemplateData.fixedSize,
+                                Height : 32,
+                                Width : 32,
+                                Id: ObjectID.default.generate(),
+                                ImageUrl: this.workingArea.canvasData.selectTemplateData.imageUrl,
+                                InteractiveMode: this.workingArea.canvasData.selectTemplateData.interactiveMode,
+                                MultiPoint : JSON.parse(JSON.stringify(this.workingArea.paintPoints)),
+                                Point: new PIXI.Point(0, 0),
+                                Name : this.workingArea.canvasData.selectTemplateData.name,
+                                PropertyInfos: list,
+                                Border : this.workingArea.canvasData.selectTemplateData.border,
+                                DrawMode : this.workingArea.canvasData.selectTemplateData.drawMode,
+                                Thickness : this.workingArea.canvasData.selectTemplateData.thickness,
+                                IsFromBuilding : this.workingArea.canvasData.selectTemplateData.isFromBuilding,
+                                GameMode: this.workingArea.canvasData.gameMode,
+                                LinkedObjects: new Array(),
+                            };
+                            this.workingArea.paintingPipeline = new Pipeline(tempData, this.workingArea);
+                            // this.workingArea.paintingPipeline.assetData.LinkedObjects.push(this);
+                            // this.assetData.Pipelines.push(this.workingArea.paintingPipeline.Id);
+                            this.workingArea.emit('createIcon', this.workingArea.paintingPipeline);
+                        } else {
+                            this.workingArea.previewLineSegment.visible = false;
+                            this.workingArea.currentClickPoint.position =
+                                new PIXI.Point(this.workingArea.circleShadow.x, this.workingArea.circleShadow.y);
+                            this.workingArea.paintPoints.push(new PIXI.Point(x, y));
+                            this.workingArea.paintingPipeline.assetData.MultiPoint =
+                                JSON.parse(JSON.stringify(this.workingArea.paintPoints));
+                            // this.workingArea.paintingPipeline.assetData.LinkedObjects.push(this);
+                            // this.assetData.Pipelines.push(this.workingArea.paintingPipeline);
+                            this.workingArea.paintingPipeline.refresh();
+                            this.workingArea.initPipelineData();
+                        }
+                    }
+                }
+    }
+    // 刷新
+    public refresh() {
+        if (this.assetData.CanConnect) {
+
+        }
+        this.image.width = this.assetData.Width;
+        this.image.height = this.assetData.Height;
+        this.image.angle = this.assetData.Angle;
+        this.text.text = this.assetData.Name
+            + '\r\n'
+            + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue;
+        this.text.x = this.image.x;
+        this.text.y = this.image.y - this.image.height / 2;
+    }
+}
diff --git a/src/app/working-area/model/wallSpace.ts b/src/app/working-area/model/wallSpace.ts
new file mode 100644
index 0000000..8cb8f1a
--- /dev/null
+++ b/src/app/working-area/model/wallSpace.ts
@@ -0,0 +1,347 @@
+import { WorkingAreaComponent } from '../working-area.component';
+import * as PIXI from 'pixi.js';
+
+/**
+ * 墙面
+ */
+export class WallSpace extends PIXI.Container {
+
+    line: PIXI.Graphics;
+    text: PIXI.Text;
+    style = new PIXI.TextStyle({
+        fontFamily: 'Arial',
+        fontSize: 18,
+        fontStyle: 'normal',
+        fontWeight: 'bold',
+        fill: ['#000000'],
+        stroke: '#ffffff',
+        strokeThickness: 3,
+        dropShadow: true,
+        dropShadowColor: '#000000',
+        dropShadowBlur: 3,
+        dropShadowAngle: Math.PI / 6,
+        dropShadowDistance: 1,
+        wordWrap: false,
+        wordWrapWidth: 100,
+    });
+    pts: PIXI.Point[];
+
+    constructor(public assetData: any, private workingArea: WorkingAreaComponent) {
+        super();
+        this.text = new PIXI.Text(this.assetData.Name
+        + '\r\n'
+            + this.assetData.PropertyInfos?.find((item: { PropertyName: string; }) =>
+            item.PropertyName === '名称/编号')?.PropertyValue, this.style);
+        this.line = new PIXI.Graphics();
+        this.addChild(this.text);
+        this.addChild(this.line);
+        this.workingArea.backgroundImage.addChild(this);
+        this.refresh(this.line, this.assetData.MultiPoint);
+
+    }
+
+    /**
+     * 刷新形状
+     */
+    public refresh(c: PIXI.Graphics, pts: PIXI.Point[]): void {
+        const strokeWidth = 1;
+        const startWidth = 30 + strokeWidth;
+        const endWidth = 30 + strokeWidth;
+        const edgeWidth = 10;
+        const openEnded = false;
+        const markerStart = false;
+        const markerEnd = true;
+        const spacing = (openEnded) ? 0 : 0 + strokeWidth / 2;
+        const startSize = 30 + strokeWidth;
+        const endSize = 30 + strokeWidth;
+        const isRounded = true;
+
+            // Base vector (between first points)
+        const pe = pts[pts.length - 1];
+
+            // Finds first non-overlapping point
+        let i0 = 1;
+
+        while (i0 < pts.length - 1 && pts[i0].x === pts[0].x && pts[i0].y === pts[0].y) {
+            i0++;
+            }
+
+        const dx = pts[i0].x - pts[0].x;
+        const dy = pts[i0].y - pts[0].y;
+        const dist = Math.sqrt(dx * dx + dy * dy);
+
+        if (dist === 0) {
+            return;
+            }
+
+        // Computes the norm and the inverse norm
+        let nx = dx / dist;
+        let nx1 = nx;
+        let nx2 = nx;
+        let ny = dy / dist;
+        let ny2 = ny;
+        let ny1 = ny;
+        let orthx = edgeWidth * ny;
+        let orthy = -edgeWidth * nx;
+
+        // Stores the inbound function calls in reverse order in fns
+        const fns = [];
+
+        // if (isRounded) {
+        //     // c.setLineJoin('round');
+        //     c.lineTextureStyle({ join: PIXI.LINE_JOIN.ROUND });
+        // } else if (pts.length > 2) {
+        // // Only mitre if there are waypoints
+        //     // c.setMiterLimit(1.42);
+        //     c.lineTextureStyle({ miterLimit: 1.42 });
+        // }
+        // c.lineStyle(1, 0x000000, 1);
+        c.lineTextureStyle({ width: 1, color: 0x00000, join: PIXI.LINE_JOIN.ROUND });
+        // c.begin();
+        c.beginFill(0xffffff);
+        const startNx = nx;
+        const startNy = ny;
+
+        if (markerStart && !openEnded) {
+            this.paintMarker(c, pts[0].x, pts[0].y, nx, ny, startSize, startWidth, edgeWidth, spacing, true);
+        } else {
+            const outStartX = pts[0].x + orthx / 2 + spacing * nx;
+            const outStartY = pts[0].y + orthy / 2 + spacing * ny;
+            const inEndX = pts[0].x - orthx / 2 + spacing * nx;
+            const inEndY = pts[0].y - orthy / 2 + spacing * ny;
+
+            if (openEnded) {
+                c.moveTo(outStartX, outStartY);
+                fns.push( () => {
+                c.lineTo(inEndX, inEndY);
+                });
+            } else {
+                c.moveTo(inEndX, inEndY);
+                c.lineTo(outStartX, outStartY);
+            }
+        }
+        let dx1 = 0;
+        let dy1 = 0;
+        let dist1 = 0;
+
+        for (let i = 0; i < pts.length - 2; i++) {
+        // Work out in which direction the line is bending
+        const pos = this.relativeCcw(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, pts[i + 2].x, pts[i + 2].y);
+
+        dx1 = pts[i + 2].x - pts[i + 1].x;
+        dy1 = pts[i + 2].y - pts[i + 1].y;
+
+        dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
+
+        if (dist1 !== 0) {
+            nx1 = dx1 / dist1;
+            ny1 = dy1 / dist1;
+
+            const tmp1 = nx * nx1 + ny * ny1;
+            const tmp = Math.max(Math.sqrt((tmp1 + 1) / 2), 0.04);
+
+            // Work out the normal orthogonal to the line through the control point and the edge sides intersection
+            nx2 = (nx + nx1);
+            ny2 = (ny + ny1);
+
+            const dist2 = Math.sqrt(nx2 * nx2 + ny2 * ny2);
+
+            if (dist2 !== 0) {
+                nx2 = nx2 / dist2;
+                ny2 = ny2 / dist2;
+
+                // Higher strokewidths require a larger minimum bend, 0.35 covers all but the most extreme cases
+                const strokeWidthFactor = Math.max(tmp, Math.min(1 / 200 + 0.04, 0.35));
+                const angleFactor = (pos !== 0 && isRounded) ? Math.max(0.1, strokeWidthFactor) : Math.max(tmp, 0.06);
+
+                const outX = pts[i + 1].x + ny2 * edgeWidth / 2 / angleFactor;
+                const outY = pts[i + 1].y - nx2 * edgeWidth / 2 / angleFactor;
+                const inX = pts[i + 1].x - ny2 * edgeWidth / 2 / angleFactor;
+                const inY = pts[i + 1].y + nx2 * edgeWidth / 2 / angleFactor;
+
+                if (pos === 0 || !isRounded) {
+                    // If the two segments are aligned, or if we're not drawing curved sections between segments
+                    // just draw straight to the intersection point
+                    c.lineTo(outX, outY);
+
+                    ((x, y) => {
+                        fns.push(() => {
+                            c.lineTo(x, y);
+                        });
+                    })(inX, inY);
+                } else if (pos === -1) {
+                    const c1x = inX + ny * edgeWidth;
+                    const c1y = inY - nx * edgeWidth;
+                    const c2x = inX + ny1 * edgeWidth;
+                    const c2y = inY - nx1 * edgeWidth;
+                    c.lineTo(c1x, c1y);
+                    if (isRounded) {
+                        c.quadraticCurveTo(outX, outY, c2x, c2y); // 圆角
+                    } else {
+                        c.lineTo(outX, outY);
+                    }
+                    ((x, y) => {
+                        fns.push(() => {
+                            c.lineTo(x, y);
+                        });
+                    })(inX, inY);
+                } else {
+                    c.lineTo(outX, outY);
+
+                    ((x, y) => {
+                        const c1x = outX - ny * edgeWidth;
+                        const c1y = outY + nx * edgeWidth;
+                        const c2x = outX - ny1 * edgeWidth;
+                        const c2y = outY + nx1 * edgeWidth;
+
+                        fns.push(() => {
+                            if (isRounded) {
+                                c.quadraticCurveTo(x, y, c1x, c1y);
+                            } else {
+                                c.lineTo(x, y);
+                            }
+                        });
+                        fns.push(() => {
+                            c.lineTo(c2x, c2y);
+                        });
+                            })(inX, inY);
+                }
+
+                nx = nx1;
+                ny = ny1;
+                }
+            }
+        }
+        orthx = edgeWidth * ny1;
+        orthy = - edgeWidth * nx1;
+
+        if (markerEnd && !openEnded) {
+            this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, false);
+        } else {
+            c.lineTo(pe.x - spacing * nx1 + orthx / 2, pe.y - spacing * ny1 + orthy / 2);
+
+            const inStartX = pe.x - spacing * nx1 - orthx / 2;
+            const inStartY = pe.y - spacing * ny1 - orthy / 2;
+
+            if (!openEnded) {
+                c.lineTo(inStartX, inStartY);
+            } else {
+                c.moveTo(inStartX, inStartY);
+
+                fns.splice(0, 0, () => {
+                    c.moveTo(inStartX, inStartY);
+                });
+            }
+        }
+
+        for (let i = fns.length - 1; i >= 0; i--) {
+            fns[i]();
+        }
+        c.closePath();
+        c.endFill();
+        // if (openEnded)
+        // {
+        //     c.end();
+        //     c.stroke();
+        // }
+        // else
+        // {
+        //     c.close();
+        //     c.fillAndStroke();
+        // }
+
+        // Workaround for shadow on top of base arrow
+        // c.setShadow(false);
+
+        // Need to redraw the markers without the low miter limit
+        // c.setMiterLimit(4);
+
+        // if (isRounded)
+        // {
+        //     c.setLineJoin('flat');
+        // }
+
+        // if (pts.length > 2)
+        // {
+        //     // Only to repaint markers if no waypoints
+        //     // Need to redraw the markers without the low miter limit
+        //     c.setMiterLimit(4);
+        //     if (markerStart && !openEnded)
+        //     {
+        //         c.begin();
+        //         this.paintMarker(c, pts[0].x, pts[0].y, startNx, startNy, startSize, startWidth, edgeWidth, spacing, true);
+        //         c.stroke();
+        //         c.end();
+        //     }
+
+        //     if (markerEnd && !openEnded)
+        //     {
+        //         c.begin();
+        //         this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, true);
+        //         c.stroke();
+        //         c.end();
+        //     }
+        // }
+    }
+    /**
+     * Function: paintMarker
+     *
+     * Paints the marker.
+     */
+    paintMarker(c: PIXI.Graphics, ptX: number, ptY: number, nx: number, ny: number,
+                size: number, arrowWidth: number, edgeWidth: number, spacing: number, initialMove: boolean) {
+        const widthArrowRatio = edgeWidth / arrowWidth;
+        const orthx = edgeWidth * ny / 2;
+        const orthy = -edgeWidth * nx / 2;
+
+        const spaceX = (spacing + size) * nx;
+        const spaceY = (spacing + size) * ny;
+
+        if (initialMove) {
+            c.moveTo(ptX - orthx + spaceX, ptY - orthy + spaceY);
+        } else {
+            c.lineTo(ptX - orthx + spaceX, ptY - orthy + spaceY);
+        }
+        c.lineTo(ptX - orthx / widthArrowRatio + spaceX, ptY - orthy / widthArrowRatio + spaceY);
+        c.lineTo(ptX + spacing * nx, ptY + spacing * ny);
+        c.lineTo(ptX + orthx / widthArrowRatio + spaceX, ptY + orthy / widthArrowRatio + spaceY);
+        c.lineTo(ptX + orthx + spaceX, ptY + orthy + spaceY);
+    }
+    /**
+     * Function: relativeCcw
+     *
+     * Returns 1 if the given point on the right side of the segment, 0 if its
+     * on the segment, and -1 if the point is on the left side of the segment.
+     *
+     * Parameters:
+     *
+     * x1 - X-coordinate of the startpoint of the segment.
+     * y1 - Y-coordinate of the startpoint of the segment.
+     * x2 - X-coordinate of the endpoint of the segment.
+     * y2 - Y-coordinate of the endpoint of the segment.
+     * px - X-coordinate of the point.
+     * py - Y-coordinate of the point.
+     */
+    relativeCcw(x1: number, y1: number, x2: number, y2: number, px: number, py: number) {
+        x2 -= x1;
+        y2 -= y1;
+        px -= x1;
+        py -= y1;
+        let ccw = px * y2 - py * x2;
+
+        if (ccw === 0.0) {
+            ccw = px * x2 + py * y2;
+
+            if (ccw > 0.0) {
+                px -= x2;
+                py -= y2;
+                ccw = px * x2 + py * y2;
+
+                if (ccw < 0.0) {
+                    ccw = 0.0;
+                }
+            }
+        }
+        return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0);
+    }
+}
diff --git a/src/app/working-area/working-area.component.html b/src/app/working-area/working-area.component.html
new file mode 100644
index 0000000..ee66290
--- /dev/null
+++ b/src/app/working-area/working-area.component.html
@@ -0,0 +1,2 @@
+<div #content style="width:100%;height:100%;" (mousewheel)='this.mouseWheelHandel($event)'
+    (DOMMouseScroll)='this.mouseWheelHandel($event)'></div>
\ No newline at end of file
diff --git a/src/app/working-area/working-area.component.scss b/src/app/working-area/working-area.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/working-area/working-area.component.spec.ts b/src/app/working-area/working-area.component.spec.ts
new file mode 100644
index 0000000..bd23376
--- /dev/null
+++ b/src/app/working-area/working-area.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { WorkingAreaComponent } from './working-area.component';
+
+describe('WorkingAreaComponent', () => {
+  let component: WorkingAreaComponent;
+  let fixture: ComponentFixture<WorkingAreaComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ WorkingAreaComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(WorkingAreaComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/working-area/working-area.component.ts b/src/app/working-area/working-area.component.ts
new file mode 100644
index 0000000..de84fbb
--- /dev/null
+++ b/src/app/working-area/working-area.component.ts
@@ -0,0 +1,1291 @@
+import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, Input } from '@angular/core';
+import * as PIXI from 'pixi.js';
+import { EventEmitter } from 'events';
+import { EventManager } from '@angular/platform-browser';
+import { OutlineFilter, OldFilmFilter  } from 'pixi-filters';
+import { AssetData, CanvasShareDataService, DisposalNodeData, FloorNodeData } from '../canvas-share-data.service';
+import * as ObjectID from 'bson-objectid';
+import { Charm } from './charm';
+import { SinglePointIcon } from './model/singlePointIcon';
+import { GameMode } from './model/gameMode';
+import { MultipointIcon } from './model/multipointIcon';
+import { PolygonIcon } from './model/polygonIcon';
+import { PutCarArea } from './model/putCarArea';
+import { Arrows } from './model/arrows';
+import { Pipeline } from './model/pipeline';
+import { PaintMode } from './model/paintModel';
+import { WallSpace } from './model/wallSpace';
+import { AxShape } from './model/axShape';
+import { PropertyInfo } from './model/PropertyInfo';
+
+
+@Component({
+  selector: 'app-working-area',
+  templateUrl: './working-area.component.html',
+  styleUrls: ['./working-area.component.scss']
+})
+/**
+ * 工作区
+ */
+export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterViewInit {
+
+  constructor(private eventManager: EventManager, public canvasData: CanvasShareDataService) {
+    super();
+  }
+
+  @ViewChild('content')
+  content: ElementRef;
+  /**
+   * 父组件
+   */
+  @Input() init: any;
+  /**
+   * pixijs 程序
+   */
+  public app: PIXI.Application;
+  /**
+   * 资源加载器
+   */
+  public loader = PIXI.Loader.shared;
+  /**
+   * 背景图
+   */
+  public backgroundImage: PIXI.Sprite;
+  /**
+   * 预览单点图标
+   */
+  public previewSinglePointIcon = new PIXI.Sprite();
+  /**
+   * 预览线段
+   */
+  public previewLineSegment = new PIXI.Graphics();
+  /**
+   * 预览原点
+   */
+  public circleShadow = new PIXI.Graphics();
+  /**
+   * 鼠标位置
+   */
+  public mousePosition: PIXI.Point = new PIXI.Point(0, 0);
+  /**
+   * 绘画模式
+   */
+  private paintMode: PaintMode;
+  /**
+   * 选择器
+   */
+  public selection: Selection = new Selection(this);
+  /**
+   * 当前鼠标的点
+   */
+  public currentClickPoint: PIXI.Graphics = new PIXI.Graphics();
+  /**
+   * 绘制点集合
+   */
+  public paintPoints: PIXI.Point[];
+  /**
+   * 绘制中的管线
+   */
+  public paintingPipeline: Pipeline;
+  /**
+   * 绘制中的箭头
+   */
+  public paintingArrows: Arrows = null;
+  /**
+   * 绘制中的多点图标
+   */
+  public paintingIcon: MultipointIcon;
+  public paintingWall: AxShape;
+  /**
+   * 绘制中的连线
+   */
+  public paintingLine: PIXI.Graphics = new PIXI.Graphics();
+  /**
+   * 绿色描边
+   */
+  public outlineFilterGreen = new OutlineFilter(2, 0x00ff00);
+  /**
+   * 拷贝素材数据
+   */
+  public copyData: any[] = [];
+  /**
+   * 确认绘制按钮
+   */
+  private enterPaintEndButton = PIXI.Sprite.from('assets/images/enterPaintButton.png');
+  /**
+   * 框选工具图形
+   */
+  private rectToolGraphics = new PIXI.Graphics();
+  /**
+   * 初始鼠标位置
+   */
+  private initialScreenMousePos: PIXI.Point = new PIXI.Point();
+  /**
+   * 最终鼠标位置
+   */
+  private finalScreenMousePos: PIXI.Point = new PIXI.Point();
+  /**
+   * 允许编辑
+   */
+  public allowEdit = true;
+  /**
+   * 动画控制器
+   */
+  public animator;
+  public animation;
+  public animationIcon;
+  public animationTime;
+  // 车辆作业面
+  public carAreas: PolygonIcon[];
+  // 车辆数据
+  public carData: Map<string, any> = new Map<string, any>();
+  // 当前选择的车辆id
+  public selectCar: any = null;
+  /**
+   * 数据初始化
+   */
+  ngOnInit(): void {
+    this.eventManager.addGlobalEventListener('window', 'keydown', (event: any) => {
+      if (event.keyCode === 17) {
+        this.selection.isMultiselection = true;
+      }
+    });
+    this.eventManager.addGlobalEventListener('window', 'keyup', (event: any) => {
+      if (event.keyCode === 17) {
+        this.selection.isMultiselection = false;
+        this.rectToolGraphics.visible = false;
+        this.rectToolGraphics.clear();
+      }
+      // 按Del键删除选中的图标
+      if (event.keyCode === 46) {
+        this.selection.objects.forEach(item => {
+          delete this.canvasData.originaleveryStoreyData.data[item.assetData.Id];
+          this.backgroundImage.removeChild(this.backgroundImage.getChildByName(item.assetData.Id));
+          this.canvasData.isChange = true;
+        });
+        this.emit('deleteIcon');
+      }
+    });
+    // 打印当前工作区信息
+    this.eventManager.addGlobalEventListener('window', 'keypress', (event: any) => {
+      // console.log(event.keyCode);
+      if (event.keyCode === 32) {
+        switch (this.paintMode) {
+          case 0:
+            console.log(`当前的绘制模式是:单点图标`);
+            break;
+          case 1:
+            console.log(`当前的绘制模式是:线段图标`);
+            break;
+          case 2:
+            console.log(`当前的绘制模式是:自定义多边形`);
+            break;
+          case 3:
+            console.log(`当前的绘制模式是:水带多边形`);
+            break;
+          case 4:
+            console.log(`当前的绘制模式是:暂无`);
+            break;
+          case 5:
+            console.log(`当前的绘制模式是:暂无`);
+            break;
+          case 6:
+            console.log(`当前的绘制模式是:结束绘制`);
+            break;
+          default:
+            break;
+        }
+        console.log('当前楼层的数据:');
+        console.log(this.canvasData.originaleveryStoreyData.data);
+
+        console.log('绘制中的管线:');
+        console.log(this.paintingPipeline);
+
+        console.log('处置预案数据:');
+        console.log(this.canvasData.selectPanelPoint.Data);
+      }
+    });
+  }
+  /**
+   * 页面初始化
+   */
+  ngAfterViewInit(): void {
+    this.createCanvas();
+    window.onresize = () => {
+      this.resetCanvas();
+    };
+  }
+  /**
+   *
+   * @param event 鼠标滑动事件
+   */
+  public mouseWheelHandel(event) {
+    const delX = this.mousePosition.x - this.backgroundImage.position.x;
+    const delY = this.mousePosition.y - this.backgroundImage.position.y;
+    const pivot = this.backgroundImage.toLocal(this.mousePosition);
+    const delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
+    if (delta > 0) {
+      if (this.backgroundImage.scale.x >= 32) {
+        this.backgroundImage.scale.x = 32;
+        this.backgroundImage.scale.y = 32;
+        this.emit('backgroundScale', this.backgroundImage.scale.x);
+        return;
+      }
+      this.backgroundImage.pivot.set(pivot.x, pivot.y);
+
+      this.backgroundImage.scale.x += this.backgroundImage.scale.x * 0.1;
+      this.backgroundImage.scale.y += this.backgroundImage.scale.y * 0.1;
+
+      this.backgroundImage.position.x += delX;
+      this.backgroundImage.position.y += delY;
+    } else if (delta < 0) {
+      if (this.backgroundImage.scale.x <= 0.1) {
+        this.backgroundImage.scale.x = 0.1;
+        this.backgroundImage.scale.y = 0.1;
+        this.emit('backgroundScale', this.backgroundImage.scale.x);
+        return;
+      }
+      this.backgroundImage.pivot.set(pivot.x, pivot.y);
+
+      this.backgroundImage.scale.x -= this.backgroundImage.scale.x * 0.1;
+      this.backgroundImage.scale.y -= this.backgroundImage.scale.y * 0.1;
+
+      this.backgroundImage.position.x += delX;
+      this.backgroundImage.position.y += delY;
+    }
+    this.emit('backgroundScale', this.backgroundImage.scale.x);
+  }
+  /**
+   *
+   * @param icon 移动到选中车辆到屏幕中心点
+   */
+  public moveIconToScreenCenter(icon) {
+    if (icon.parent === this.backgroundImage && (
+      icon.assetData.Type === 1 ||
+      icon.assetData.Type === 2 ||
+      icon.assetData.Type === 3 ||
+      icon.assetData.Type === 4
+    )) {
+      console.log(this.backgroundImage.position);
+      this.backgroundImage.pivot.set(icon.x, icon.y);
+      this.backgroundImage.position.set(771, 404);
+      clearTimeout(this.animationTime);
+      this.animation?.pause();
+      this.animationIcon?.scale.set(1);
+      this.animation = this.animator.breathe(icon, 10, 10, 30, true, 0);
+      this.animationIcon = icon;
+      this.animationTime = setTimeout(() => {
+        this.animation?.pause();
+        this.animationIcon?.scale.set(1);
+      }, 5000);
+    }
+  }
+  /**
+   * 创建画布
+   */
+  private createCanvas(): void {
+    this.app = new PIXI.Application({
+      width: this.content.nativeElement.clientWidth,
+      height: this.content.nativeElement.clientHeight,
+      antialias: true,
+      transparent: false,
+      resolution: 1,
+      backgroundColor: 0xE9FAFF
+    });
+    this.content.nativeElement.appendChild(this.app.view);
+    this.animator = new Charm(PIXI);
+
+    this.app.ticker.add((delta) => {
+      this.animator.update();
+      this.mousePosition = this.app.renderer.plugins.interaction.mouse.global;
+      if (this.backgroundImage !== undefined) {
+        this.previewSinglePointIcon.position = this.backgroundImage.toLocal(this.mousePosition);
+        this.circleShadow.position = this.backgroundImage.toLocal(this.mousePosition);
+        this.refreshPreviewLineSegment(this.currentClickPoint.position, this.circleShadow.position);
+      }
+      if (this.rectToolGraphics.visible === true) {
+
+        const init = this.initialScreenMousePos;
+        const final = this.finalScreenMousePos;
+
+        this.rectToolGraphics.clear();
+        this.rectToolGraphics.lineStyle(2, 0x00ff00, 1);
+        this.rectToolGraphics.beginFill(0xccccf2, 0.25);
+        this.rectToolGraphics.drawRect(init.x, init.y, final.x - init.x, final.y - init.y);
+        this.rectToolGraphics.endFill();
+        this.rectToolGraphics.closePath();
+      }
+      if (this.paintingArrows !== null) {
+        this.paintingArrows.assetData.pointB = new PIXI.Point(this.circleShadow.position.x, this.circleShadow.position.y);
+        this.paintingArrows.refresh();
+      }
+    });
+    /**
+     * 选中事件
+     */
+    this.on('select', obj => {
+      // this.moveIconToScreenCenter(obj);
+      if (this.allowEdit) {
+        if (obj instanceof MultipointIcon) {
+          if (obj.assetData.GameMode === this.canvasData.gameMode) {
+            obj.setPointVisiable(true);
+          } else {
+            obj.filters = [this.outlineFilterGreen];
+          }
+        } else if (obj instanceof PolygonIcon) {
+          if (obj.assetData.GameMode === this.canvasData.gameMode) {
+            obj.setPointVisiable(true);
+          } else {
+            obj.filters = [this.outlineFilterGreen];
+          }
+        } else {
+          obj.filters = [this.outlineFilterGreen];
+        }
+      } else {
+        obj.filters = [this.outlineFilterGreen];
+      }
+    });
+    /**
+     * 取消选中事件
+     */
+    this.on('deselect', obj => {
+      if (this.allowEdit) {
+        if (obj instanceof MultipointIcon) {
+          obj.setPointVisiable(false);
+        } else if (obj instanceof PolygonIcon) {
+          obj.setPointVisiable(false);
+        } else {
+          obj.filters = [];
+        }
+      } else {
+        obj.filters = [];
+      }
+    });
+    this.on('backgroundScale', scale => {
+      this.previewSinglePointIcon.scale.set((0.5 / scale));
+      this.backgroundImage.children.forEach(item => {
+        if (item instanceof SinglePointIcon) {
+          if (item.assetData.FixedSize) {
+            const data = 1 / scale;
+            item.scale.set(data);
+          } else {
+            const data = 1 / scale;
+            item.text.scale.set(data);
+          }
+        } else if (item instanceof MultipointIcon) {
+          const data = 1 / scale;
+          item.text.scale.set(data);
+        } else if (item instanceof PolygonIcon) {
+          const data = 1 / scale;
+          item.text.scale.set(data);
+        }
+      });
+
+    });
+    this.on('createIcon', obj => {
+      if (obj.assetData.GameMode === GameMode.BasicInformation) {
+        // if (obj.assetData.IsFromBuilding) {
+        //   this.canvasData.originalcompanyBuildingData.data[obj.assetData.Id] = obj.assetData;
+        // } else {
+          this.canvasData.originaleveryStoreyData.data[obj.assetData.Id] = obj.assetData;
+        // }
+      } else {
+        // console.log();
+        if (this.canvasData.selectPanelPoint.Data === undefined
+        || this.canvasData.selectPanelPoint.Data === null) {
+          this.canvasData.selectPanelPoint.Data = new FloorNodeData();
+        }
+        this.canvasData.selectPanelPoint.Data.Stock[obj.assetData.Id] = obj.assetData;
+      }
+      this.canvasData.isChange = true;
+    });
+  }
+  /**
+   * 重置画布
+   */
+  public resetCanvas() {
+    this.app.renderer.resize(this.content.nativeElement.clientWidth, this.content.nativeElement.clientHeight);
+  }
+  /**
+   * 设置名称显示
+   * @param value true  显示 false 隐藏
+   * @param mode BasicInformation = 0 基本信息 Assignment想定作业 = 1 想定作业
+   */
+  public setNameVisible(value: boolean, mode: GameMode): void {
+    this.backgroundImage?.children.forEach(item => {
+      if (item instanceof SinglePointIcon) {
+        item.setNameVisible(value, mode);
+      } else if (item instanceof MultipointIcon) {
+        item.setNameVisible(value, mode);
+      } else if (item instanceof PolygonIcon) {
+        item.setNameVisible(value, mode);
+      }
+    });
+  }
+  /**
+   * 根据id刷新图标
+   * @param id 图标数据id
+   */
+  public refreshIcon(id: string): void {
+    const icon = this.backgroundImage.children.find(item => item.name === id);
+    if (icon instanceof SinglePointIcon) {
+      icon.refresh();
+    } else if (icon instanceof MultipointIcon) {
+      icon.refresh();
+    } else if (icon instanceof PolygonIcon) {
+      icon.refresh();
+    }
+  }
+  /**
+   *
+   * @param value 缩放倍数
+   */
+  public setIconScale(value: number): void {
+    this.backgroundImage.children.forEach(item => {
+      if (item instanceof SinglePointIcon) {
+        item.scale.set(value);
+      } else if (item instanceof MultipointIcon) {
+
+      } else if (item instanceof PolygonIcon) {
+
+      }
+    });
+  }
+  /**
+   * 设置高亮
+   */
+  public setHighlight(ids: string[]): void {
+    this.selection.deselectAll();
+    ids.forEach(item => {
+      let obj = this.backgroundImage.getChildByName(item);
+      if (obj === null) {
+        obj = this.app.stage.getChildByName(item);
+      }
+      this.selection.select(obj);
+    });
+  }
+  /**
+   * 刷新工作区
+   */
+  public async refresh() {
+    this.setPaintMode(PaintMode.endPaint);
+    this.resetCanvas();
+    this.destroyBackgroundImage();
+    await this.createBackgroundImage(this.canvasData.selectStorey.imageUrl);
+
+    // this.refreshBackgroundImage();
+    // this.versionChecking();
+
+
+    const floorData = this.canvasData.originaleveryStoreyData.data;
+    // const buildingData = this.canvasData.originalcompanyBuildingData.data;
+    // const floor = this.canvasData.selectStorey;
+    // // key=>属性名    data[key]=>属性值
+    Object.keys(floorData).forEach((key) => {
+      console.log(floorData[key]);
+      switch (floorData[key].InteractiveMode) {
+        case 0:
+          const singleIcon = new SinglePointIcon(floorData[key], this);
+          break;
+          case 1:
+          const icon = new MultipointIcon(floorData[key], this);
+          break;
+          case 2:
+          const polygonIcon = new PolygonIcon(floorData[key], this);
+          break;
+        }
+    });
+    // Object.keys(buildingData).forEach((key) => {
+    //   if (buildingData[key].FloorId === floor.id) {
+    //     switch (buildingData[key].InteractiveMode) {
+    //       case 0:
+    //         const singleIcon = new SinglePointIcon(buildingData[key], this);
+    //         break;
+    //       case 1:
+    //         const icon = new MultipointIcon(buildingData[key], this);
+    //         break;
+    //       case 2:
+    //         const polygonIcon = new PolygonIcon(buildingData[key], this);
+    //         break;
+    //     }
+    //   }
+    // });
+
+    // 加载处置节点数据
+    const nodeData = this.canvasData.selectPanelPoint.Data;
+    if (nodeData !== undefined && nodeData !== null) {
+      Object.keys(nodeData).forEach((key) => {
+        Object.keys(nodeData[key]).forEach((tempKey) => {
+          switch (nodeData[key][tempKey].InteractiveMode) {
+            case 0:
+              const singleIcon = new SinglePointIcon(nodeData[key][tempKey], this);
+              break;
+            case 1:
+              if (nodeData[key][tempKey].Name === '水带') {
+                const pipeline = new Pipeline(nodeData[key][tempKey], this);
+              } else {
+                const icon = new MultipointIcon(nodeData[key][tempKey], this);
+              }
+              break;
+          case 2:
+              const polygonIcon = new PolygonIcon(nodeData[key][tempKey], this);
+              break;
+        }
+        });
+      });
+    }
+    this.emit('backgroundScale', this.backgroundImage.scale.x);
+  }
+  /**
+   *
+   * @param id 图标ID
+   * @param b 显示/隐藏
+   */
+  public setIconVisible(ids: string[], b: boolean) {
+    ids.forEach(item => {
+      this.backgroundImage.getChildByName(item).visible = b;
+    });
+  }
+  // /**
+  //  * 版本检查
+  //  */
+  // public versionChecking(): void {
+  //   const floorData = this.canvasData.originaleveryStoreyData;
+  //   const buildingData = this.canvasData.originalcompanyBuildingData;
+  //   const nodeData = this.canvasData.selectPanelPoint;
+  //   if (floorData.version && floorData.version === '1.0') {
+  //     floorData.version = '2.0';
+  //     Object.keys(floorData.data).forEach(item => {
+  //       floorData.data[item].Point.y *= -1;
+  //       floorData.data[item].MultiPoint?.forEach(element => {
+  //         element.y *= -1;
+  //       });
+  //     });
+  //   }
+  //   if (buildingData.version && buildingData.version === '1.0') {
+  //     buildingData.version = '2.0';
+  //     Object.keys(buildingData.data).forEach(item => {
+  //       buildingData.data[item].Point.y *= -1;
+  //       buildingData.data[item].MultiPoint?.forEach(element => {
+  //         element.y *= -1;
+  //       });
+  //     });
+  //   }
+  //   if (nodeData.Version && nodeData.Version === '1.0') {
+  //     nodeData.Version = '2.0';
+  //     console.log(this.canvasData.selectPanelPoint.Version);
+  //     Object.keys(nodeData.Data).forEach((key) => {
+  //       Object.keys(nodeData.Data[key]).forEach((tempKey) => {
+  //         nodeData.Data[key][tempKey].Point.y *= -1;
+  //         nodeData.Data[key][tempKey].MultiPoint?.forEach(element => {
+  //           element.y *= -1;
+  //         });
+  //       });
+  //     });
+  //   }
+  // }
+  /**
+   * 创建确认绘制结束按钮
+   */
+  private createEnterPaintEndButton() {
+    this.enterPaintEndButton.width = 60;
+    this.enterPaintEndButton.height = 60;
+    this.enterPaintEndButton.anchor.set(0.5);
+    this.enterPaintEndButton.position =  new PIXI.Point(0, 0);
+    this.enterPaintEndButton.interactive = true;
+    this.enterPaintEndButton.buttonMode = true;
+    this.enterPaintEndButton
+      .on('mousedown', event => {
+        event.stopPropagation();
+        this.enterPaint();
+      });
+    this.backgroundImage.addChild(this.enterPaintEndButton);
+    this.enterPaintEndButton.zIndex = this.backgroundImage.children.length;
+    this.enterPaintEndButton.visible = false;
+  }
+  /**
+   * 创建背景图
+   */
+  private async createBackgroundImage(imageUrl: string): Promise<void> {
+    const image = await PIXI.Texture.fromURL(imageUrl);
+    this.backgroundImage = new PIXI.Sprite(image);
+    this.backgroundImage.anchor.set(0.5);
+    this.backgroundImage.x = this.app.view.width / 2;
+    this.backgroundImage.y = this.app.view.height / 2;
+    this.backgroundImage.interactive = true;
+    this.backgroundImage.name = 'background';
+
+    // const left = this.init.element.nativeElement.querySelector('.functionalDomainLeft').clientWidth;
+    // const right = this.init.element.nativeElement.querySelector('.functionalDomainRight').clientWidth;
+    const imageWidth = this.backgroundImage.texture.width;
+    const imageHeight = this.backgroundImage.texture.height;
+    const appWidth = this.app.view.width - 470;
+    const appHeight = this.app.view.height;
+
+    const wScale = appWidth / imageWidth;
+    const hScale = appHeight / imageHeight;
+
+    const scale = wScale < hScale
+      ? wScale
+      : hScale;
+    this.backgroundImage.scale.set(scale);
+    this.backgroundImage.sortableChildren = true;
+    this.backgroundImage
+      .on('mousedown', event => {
+        if (!event.currentTarget.dragging && this.selection.isMultiselection === false) {
+          event.currentTarget.data = event.data;
+          event.currentTarget.dragging = true;
+          event.currentTarget.dragPoint = event.data.getLocalPosition(event.currentTarget.parent);
+          event.currentTarget.dragPoint.x -= event.currentTarget.x;
+          event.currentTarget.dragPoint.y -= event.currentTarget.y;
+          switch (this.paintMode) {
+            case PaintMode.endPaint:
+              console.log(this.backgroundImage.toLocal(this.mousePosition));
+              break;
+            case PaintMode.singlePointIcon:
+              const json = JSON.parse(JSON.stringify(this.canvasData.selectTemplateData.propertyInfos));
+              const list = [];
+              json.forEach(element => {
+                const property = new PropertyInfo(element);
+                list.push(property);
+              });
+
+              const assetData =  {
+                TemplateId: this.canvasData.selectTemplateData.id,
+                CanConnect: this.canvasData.selectTemplateData.canConnect,
+                Pipelines: new Array(),
+                FloorId: this.canvasData.selectStorey.id,
+                Angle: this.canvasData.selectTemplateData.angle,
+                Color: this.canvasData.selectTemplateData.color,
+                Enabled: this.canvasData.selectTemplateData.enabled,
+                FillMode: this.canvasData.selectTemplateData.fillMode,
+                FireElementId: this.canvasData.selectTemplateData.fireElementId,
+                FixedSize: this.canvasData.selectTemplateData.fixedSize,
+                Height : 32,
+                Width : 32,
+                Id: ObjectID.default.generate(),
+                ImageUrl: this.canvasData.selectTemplateData.imageUrl,
+                InteractiveMode: this.canvasData.selectTemplateData.interactiveMode,
+                MultiPoint : null,
+                Point: new PIXI.Point(this.previewSinglePointIcon.x, this.previewSinglePointIcon.y),
+                Name : this.canvasData.selectTemplateData.name,
+                PropertyInfos: list,
+                Border : this.canvasData.selectTemplateData.border,
+                DrawMode : this.canvasData.selectTemplateData.drawMode,
+                Thickness : this.canvasData.selectTemplateData.thickness,
+                IsFromBuilding : this.canvasData.selectTemplateData.isFromBuilding,
+                GameMode : this.canvasData.gameMode
+              };
+              const singleIcon = new SinglePointIcon(assetData, this);
+              this.emit('createIcon', singleIcon);
+              this.emit('backgroundScale', this.backgroundImage.scale.x);
+              break;
+            case PaintMode.lineIcon:
+              this.previewLineSegment.visible = true;
+              this.currentClickPoint.position = new PIXI.Point(this.circleShadow.x, this.circleShadow.y);
+              this.paintPoints.push(new PIXI.Point(this.circleShadow.x, this.circleShadow.y));
+
+              if (this.paintPoints.length >= 2) {
+                this.enterPaintEndButton.position = this.circleShadow.position;
+                this.enterPaintEndButton.visible = true;
+              }
+
+              if (this.paintingIcon !== null) {
+                this.backgroundImage.removeChild(this.paintingIcon);
+              }
+              const jsonObject = JSON.parse(JSON.stringify(this.canvasData.selectTemplateData.propertyInfos));
+              const propertyList = [];
+              jsonObject.forEach(element => {
+                const property = new PropertyInfo(element);
+                propertyList.push(property);
+              });
+              const assetData1 = {
+                TemplateId: this.canvasData.selectTemplateData.id,
+                FloorId: this.canvasData.selectStorey.id,
+                Angle: this.canvasData.selectTemplateData.angle,
+                Color: this.canvasData.selectTemplateData.color,
+                Enabled: this.canvasData.selectTemplateData.enabled,
+                FillMode: this.canvasData.selectTemplateData.fillMode,
+                FireElementId: this.canvasData.selectTemplateData.fireElementId,
+                FixedSize: this.canvasData.selectTemplateData.fixedSize,
+                Height: 32,
+                Width: 32,
+                Id: ObjectID.default.generate(),
+                ImageUrl: this.canvasData.selectTemplateData.imageUrl,
+                InteractiveMode: this.canvasData.selectTemplateData.interactiveMode,
+                MultiPoint: JSON.parse(JSON.stringify(this.paintPoints)),
+                Point: new PIXI.Point(0, 0),
+                Name: this.canvasData.selectTemplateData.name,
+                PropertyInfos: propertyList,
+                Border: this.canvasData.selectTemplateData.border,
+                DrawMode: this.canvasData.selectTemplateData.drawMode,
+                Thickness: this.canvasData.selectTemplateData.thickness,
+                IsFromBuilding: this.canvasData.selectTemplateData.isFromBuilding,
+                GameMode: this.canvasData.gameMode
+              };
+              // const assetData1 = {
+              //   ImageUrl: this.canvasData.selectTemplateData.imageUrl,
+              //   Point: new PIXI.Point(0, 0),
+              //   Width: 32,
+              //   Height: 32,
+              //   MultiPoint: this.paintPoints,
+              //   Name: this.canvasData.selectTemplateData.name
+              // };
+              this.paintingIcon = new MultipointIcon(assetData1, this);
+              // this.paintingIcon = new MultipointIcon(this.previewSinglePointIcon.texture, new PIXI.Point(0, 0), this.paintPoints, this,
+              //   this.canvasData.selectTemplateData.name);
+              this.emit('backgroundScale', this.backgroundImage.scale.x);
+              break;
+            case PaintMode.polygonIcon:
+              this.previewLineSegment.visible = true;
+              this.currentClickPoint.position = new PIXI.Point(this.circleShadow.x, this.circleShadow.y);
+              this.paintPoints.push(new PIXI.Point(this.circleShadow.x, this.circleShadow.y));
+              if (this.paintPoints.length === 1) {
+                this.enterPaintEndButton.position = this.circleShadow.position;
+              } else if (this.paintPoints.length >= 3) {
+                this.enterPaintEndButton.visible = true;
+              }
+              this.paintPoints.forEach((value, index, array) => {
+                if (index === 0) {
+                  this.paintingLine.clear();
+                  this.paintingLine.lineStyle(1, 0xffd900, 1);
+                  this.paintingLine.moveTo(value.x, value.y);
+                } else {
+                  this.paintingLine.lineTo(value.x, value.y);
+                }
+              });
+
+              // if (this.paintingIcon !== null) {
+              //   this.backgroundImage.removeChild(this.paintingIcon);
+              // }
+              // this.paintingIcon = new PolygonIcon(this.paintPoints, this);
+              break;
+            case PaintMode.Pipeline:
+
+              if (this.paintingPipeline !== null) {
+                this.currentClickPoint.position = new PIXI.Point(this.circleShadow.x, this.circleShadow.y);
+                this.paintPoints.push(new PIXI.Point(this.circleShadow.x, this.circleShadow.y));
+                this.paintingPipeline.assetData.MultiPoint = JSON.parse(JSON.stringify(this.paintPoints));
+                this.paintingPipeline.refresh();
+              }
+              // this.emit('backgroundScale', this.backgroundImage.scale.x);
+              break;
+            case PaintMode.Arrows:
+              if (this.paintingArrows === null) {
+                const data = {
+                   Id: ObjectID.default.generate(),
+                   name: 'string',
+                   point: new PIXI.Point(this.circleShadow.x, this.circleShadow.y),
+                   pointA: new PIXI.Point(this.circleShadow.x, this.circleShadow.y),
+                   pointB: new PIXI.Point(this.circleShadow.x, this.circleShadow.y),
+                   source: 'assets/images/进攻方向.png',
+                };
+                this.paintingArrows = new Arrows(data, this);
+              } else {
+                this.paintingArrows.ready = true;
+                this.paintingArrows = null;
+                this.paintMode = PaintMode.endPaint;
+              }
+              break;
+            case PaintMode.Car:
+              // this.previewLineSegment.visible = true;
+              // this.currentClickPoint.position = new PIXI.Point(this.circleShadow.x, this.circleShadow.y);
+              // this.paintPoints.push(new PIXI.Point(this.circleShadow.x, this.circleShadow.y));
+
+              // if (this.paintPoints.length >= 2) {
+              //   this.enterPaintEndButton.position = this.circleShadow.position;
+              //   this.enterPaintEndButton.visible = true;
+              // }
+
+              // if (this.paintingWall !== null) {
+              //   this.backgroundImage.removeChild(this.paintingWall);
+              // }
+              // const jsonObject1 = JSON.parse(JSON.stringify(this.canvasData.selectTemplateData.propertyInfos));
+              // const propertyList1 = [];
+              // jsonObject1.forEach(element => {
+              //   const property = new PropertyInfo(element);
+              //   propertyList1.push(property);
+              // });
+              // const assetData11 = {
+              //   TemplateId: this.canvasData.selectTemplateData.id,
+              //   FloorId: this.canvasData.selectStorey.id,
+              //   Angle: this.canvasData.selectTemplateData.angle,
+              //   Color: this.canvasData.selectTemplateData.color,
+              //   Enabled: this.canvasData.selectTemplateData.enabled,
+              //   FillMode: this.canvasData.selectTemplateData.fillMode,
+              //   FireElementId: this.canvasData.selectTemplateData.fireElementId,
+              //   FixedSize: this.canvasData.selectTemplateData.fixedSize,
+              //   Height: 32,
+              //   Width: 32,
+              //   Id: ObjectID.default.generate(),
+              //   ImageUrl: this.canvasData.selectTemplateData.imageUrl,
+              //   InteractiveMode: this.canvasData.selectTemplateData.interactiveMode,
+              //   MultiPoint: JSON.parse(JSON.stringify(this.paintPoints)),
+              //   Point: new PIXI.Point(0, 0),
+              //   Name: this.canvasData.selectTemplateData.name,
+              //   PropertyInfos: propertyList1,
+              //   Border: this.canvasData.selectTemplateData.border,
+              //   DrawMode: this.canvasData.selectTemplateData.drawMode,
+              //   Thickness: this.canvasData.selectTemplateData.thickness,
+              //   IsFromBuilding: this.canvasData.selectTemplateData.isFromBuilding,
+              //   GameMode: this.canvasData.gameMode
+              // };
+
+              // this.paintingWall = new WallSpace(assetData11, this);
+              // this.emit('backgroundScale', this.backgroundImage.scale.x);
+              break;
+          }
+        } else if (!event.currentTarget.dragging && this.selection.isMultiselection === true) {
+          this.rectToolGraphics.visible = true;
+          event.currentTarget.dragging = true;
+          this.initialScreenMousePos = this.backgroundImage.toLocal(this.mousePosition);
+          this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition);
+        }
+      })
+      .on('mouseup', event => {
+        if (event.currentTarget.dragging) {
+          event.currentTarget.dragging = false;
+          event.currentTarget.data = null;
+        }
+        if (this.rectToolGraphics.visible === true) {
+          this.backgroundImage.children.forEach(item => {
+            if (item instanceof SinglePointIcon
+              || item instanceof MultipointIcon
+              || item instanceof PolygonIcon) {
+              if (this.rectToolGraphics.getLocalBounds().contains(item.x, item.y)) {
+                this.selection.select(item);
+              }
+            }
+          });
+          this.rectToolGraphics.clear();
+          this.rectToolGraphics.visible = false;
+        }
+      })
+      .on('mouseupoutside', event => {
+        if (event.currentTarget.dragging) {
+          event.currentTarget.dragging = false;
+          event.currentTarget.data = null;
+        }
+      })
+      .on('mousemove', event => {
+        if (event.currentTarget.dragging && this.selection.isMultiselection === false) {
+          const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent);
+          event.currentTarget.x = newPosition.x - event.currentTarget.dragPoint.x;
+          event.currentTarget.y = newPosition.y - event.currentTarget.dragPoint.y;
+        } else if (event.currentTarget.dragging && this.selection.isMultiselection === true) {
+          if (this.rectToolGraphics.visible === true) {
+            this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition);
+          }
+        }
+      })
+      .on('rightclick', event => {
+        event.stopPropagation();
+        this.selection.deselectAll();
+        this.setPaintMode(PaintMode.endPaint);
+      })
+      .on('pointerover', (event) => {
+        this.previewSinglePointIcon.filters = null;
+      })
+      .on('pointerout', (event) => {
+        this.previewSinglePointIcon.filters = null;
+      });
+    this.app.stage.addChild(this.backgroundImage);
+    this.createPreviewSinglePointIcon();
+    this.createPreviewLineSegment();
+    this.createCircleShadow();
+    this.createEnterPaintEndButton();
+    this.backgroundImage.addChild(this.paintingLine);
+
+  }
+
+  /**
+   * 刷新背景图
+   */
+  public refreshBackgroundImage(): void {
+    if (!this.canvasData.selectStorey.imageUrl) {
+      this.backgroundImage.visible = false;
+    } else {
+      this.backgroundImage.texture = PIXI.Texture.from(this.canvasData.selectStorey.imageUrl);
+      this.backgroundImage.angle = this.canvasData.selectStorey.imageAngle;
+      this.backgroundImage.visible = true;
+    }
+  }
+  /**
+   * 清空画布
+   */
+  public destroyBackgroundImage(): void {
+    this.app.stage.removeChild(this.backgroundImage);
+  }
+  /**
+   * 设置背景图缩放
+   * @param scale 缩放系数
+   */
+  public setBackgroundScale(scale: number): void {
+    this.backgroundImage.scale.set(scale);
+    this.emit('backgroundScale', this.backgroundImage.scale.x);
+  }
+  /**
+   * 设置背景图角度
+   * @param imageAngle 角度值
+   */
+  public setBackgroundAngle(imageAngle: number) {
+    this.backgroundImage.angle = imageAngle;
+  }
+  /**
+   * 创建预览单点图标
+   */
+  private createPreviewSinglePointIcon(): void {
+    this.previewSinglePointIcon = PIXI.Sprite.from('assets/images/noImg.png');
+    this.previewSinglePointIcon.width = 32;
+    this.previewSinglePointIcon.height = 32;
+    this.previewSinglePointIcon.alpha = 1;
+    this.previewSinglePointIcon.anchor.set(0.5);
+    this.previewSinglePointIcon.visible = false;
+    this.backgroundImage.addChild(this.previewSinglePointIcon);
+  }
+  /**
+   * 改变预览单点图标
+   * @param uri 图片地址
+   */
+  private changePreviewSinglePointIcon(uri: string): void {
+    this.previewSinglePointIcon.texture = PIXI.Texture.from(uri);
+    this.previewSinglePointIcon.visible = true;
+  }
+  /**
+   * 创建预览线段
+   */
+  private createPreviewLineSegment() {
+    this.previewLineSegment.visible = false;
+    this.backgroundImage.addChild(this.currentClickPoint);
+    this.backgroundImage.addChild(this.previewLineSegment);
+
+    this.backgroundImage.addChild(this.rectToolGraphics);
+    this.rectToolGraphics.visible = false;
+  }
+  /**
+   * 刷新预览线段
+   * @param pointA 点A
+   * @param pointB 点B
+   */
+  private refreshPreviewLineSegment(pointA: PIXI.Point, pointB: PIXI.Point) {
+    this.previewLineSegment.clear();
+    this.previewLineSegment.lineStyle(1, 0xffd900, 1);
+    this.previewLineSegment.moveTo(pointA.x, pointA.y);
+    this.previewLineSegment.lineTo(pointB.x, pointB.y );
+  }
+  /**
+   * 创建半径图标影子
+   * @param x 半径
+   */
+  private createCircleShadow(): void {
+    this.circleShadow.beginFill(0xFFCC5A);
+    this.circleShadow.drawCircle(0, 0, 10);
+    this.circleShadow.endFill();
+    this.circleShadow.visible = false;
+    this.backgroundImage.addChild(this.circleShadow);
+  }
+  showConnectionPoint(b: boolean) {
+    this.backgroundImage?.children.forEach(item => {
+        if (item instanceof SinglePointIcon) {
+          if (item.assetData.CanConnect) {
+            item.showConnectionPoint(b);
+         }
+      }
+    });
+  }
+  /**
+   * 开始绘制
+   */
+  public beginPaint() {
+    if (this.canvasData.selectTemplateData.name === '水带') {
+      this.showConnectionPoint(true);
+      this.setPaintMode(PaintMode.Pipeline);
+      return;
+    }
+    switch (this.canvasData.selectTemplateData.interactiveMode) {
+      case 0:
+        this.setPaintMode(PaintMode.singlePointIcon);
+        break;
+      case 1:
+        this.setPaintMode(PaintMode.lineIcon);
+        break;
+      case 2:
+        this.setPaintMode(PaintMode.polygonIcon);
+        break;
+      case 3:
+        if (this.canvasData.selectTemplateData.name) {
+          this.setPaintMode(PaintMode.Pipeline);
+        }
+        break;
+    }
+  }
+  /**
+   * 初始化管线数据
+   */
+  public initPipelineData(): void {
+    this.paintPoints = [];
+    this.paintingPipeline = null;
+  }
+  public beginPaintingArrows(): void {
+    this.paintMode = PaintMode.Arrows;
+  }
+  /**
+   * 设置绘制状态
+   * @param mode 状态
+   */
+  public setPaintMode(mode: PaintMode) {
+    if (this.paintMode === mode) { return; }
+    this.paintMode = mode;
+    if (this.paintMode !== PaintMode.Pipeline) {
+      this.showConnectionPoint(false);
+    }
+    switch (this.paintMode) {
+      case PaintMode.Pipeline:
+
+        break;
+      case PaintMode.singlePointIcon:
+        this.previewSinglePointIcon.visible = false;
+        this.changePreviewSinglePointIcon(this.canvasData.selectTemplateData.imageUrl);
+        break;
+      case PaintMode.lineIcon:
+        this.circleShadow.visible = false;
+        this.previewLineSegment.visible = false;
+        this.paintPoints.splice(0, this.paintPoints.length);
+        if (this.paintingIcon !== null) {
+          this.backgroundImage.removeChild(this.paintingIcon);
+        }
+        this.previewSinglePointIcon.texture = PIXI.Texture.from(this.canvasData.selectTemplateData.imageUrl);
+        this.circleShadow.visible = true;
+        break;
+      case PaintMode.polygonIcon:
+        this.circleShadow.visible = false;
+        this.previewLineSegment.visible = false;
+        this.paintingIcon = null;
+        this.paintPoints.splice(0, this.paintPoints.length);
+        this.paintingLine.clear();
+        this.circleShadow.visible = true;
+        break;
+      case PaintMode.endPaint:
+        // 重置组件状态
+        if ( this.paintingIcon !== undefined
+          && this.paintingIcon !== null) {
+          this.backgroundImage.removeChild(this.paintingIcon);
+        }
+
+        if (this.paintingPipeline !== undefined
+          && this.paintingPipeline !== null) {
+          this.backgroundImage.removeChild(this.paintingPipeline);
+        }
+        this.paintingLine.clear();
+        this.resetData();
+        break;
+      default:
+        break;
+    }
+  }
+  /**
+   * 获取绘制状态
+   */
+  public getPaintMode(): PaintMode {
+    return this.paintMode;
+  }
+  /**
+   * 重置
+   */
+  public resetData() {
+    this.previewSinglePointIcon.filters = null;
+    this.previewSinglePointIcon.visible = false;
+    this.previewSinglePointIcon.angle = 0;
+
+    this.initPipelineData();
+    //
+    this.circleShadow.visible = false;
+    this.previewLineSegment.visible = false;
+  }
+  /**
+   * 确认绘制
+   */
+  private enterPaint(): void {
+    this.previewLineSegment.visible = false;
+    this.enterPaintEndButton.visible = false;
+    switch (this.paintMode) {
+      case PaintMode.lineIcon:
+        if (this.paintPoints.length >= 2) {
+          this.emit('createIcon', this.paintingIcon);
+          this.paintingIcon = null;
+        }
+        break;
+      case PaintMode.polygonIcon:
+        this.paintingLine.clear();
+        if (this.paintPoints.length >= 3) {
+          const jsonList = JSON.parse(JSON.stringify(this.canvasData.selectTemplateData.propertyInfos));
+          const propertyList = [];
+          jsonList.forEach(element => {
+            const property = new PropertyInfo(element);
+            propertyList.push(property);
+          });
+          const assetData = {
+            TemplateId: this.canvasData.selectTemplateData.id,
+            FloorId: this.canvasData.selectStorey.id,
+            Angle: this.canvasData.selectTemplateData.angle,
+            Color: this.canvasData.selectTemplateData.color,
+            Enabled: this.canvasData.selectTemplateData.enabled,
+            FillMode: this.canvasData.selectTemplateData.fillMode,
+            FireElementId: this.canvasData.selectTemplateData.fireElementId,
+            FixedSize: this.canvasData.selectTemplateData.fixedSize,
+            Height: 32,
+            Width: 32,
+            Id: ObjectID.default.generate(),
+            ImageUrl: this.canvasData.selectTemplateData.imageUrl,
+            InteractiveMode: this.canvasData.selectTemplateData.interactiveMode,
+            MultiPoint: JSON.parse(JSON.stringify(this.paintPoints)),
+            Point: new PIXI.Point(0, 0),
+            Name: this.canvasData.selectTemplateData.name,
+            PropertyInfos: propertyList,
+            Border: this.canvasData.selectTemplateData.border,
+            DrawMode: this.canvasData.selectTemplateData.drawMode,
+            Thickness: this.canvasData.selectTemplateData.thickness,
+            IsFromBuilding: this.canvasData.selectTemplateData.isFromBuilding,
+            GameMode: this.canvasData.gameMode
+          };
+          const polygonIcon = new PolygonIcon(assetData, this);
+          this.emit('createIcon', polygonIcon);
+        }
+        break;
+    }
+    this.paintPoints.splice(0, this.paintPoints.length);
+    this.emit('backgroundScale', this.backgroundImage.scale.x);
+  }
+  /**
+   * 复制
+   */
+  public copy(): void {
+    this.copyData = [];
+    this.selection.objects.forEach(item => {
+      const newData = JSON.parse(JSON.stringify(item.assetData));
+      this.copyData.push(newData);
+    });
+  }
+  /**
+   * 粘贴
+   */
+  public paste(companyId: string, buildingId: string, floorId: string): void {
+    this.copyData.forEach(item => {
+      item.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5);
+      const newData = JSON.parse(JSON.stringify(item));
+      newData.Id = ObjectID.default.generate(),
+      newData.CompanyId = companyId;
+      newData.BuildingId = buildingId;
+      newData.FloorId = floorId;
+      newData.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5);
+      // if (newData.IsFromBuilding) {
+      //   this.canvasData.originalcompanyBuildingData.data[newData.Id] = newData;
+      // } else {
+      this.canvasData.originaleveryStoreyData.data[newData.Id] = newData;
+      // }
+      switch (item.InteractiveMode) {
+        case PaintMode.singlePointIcon:
+          const singleIcon = new SinglePointIcon(newData, this);
+          break;
+        case PaintMode.lineIcon:
+          const lineIcon = new MultipointIcon(newData, this);
+          break;
+        case PaintMode.polygonIcon:
+          const polygonIcon = new PolygonIcon(newData, this);
+          break;
+      }
+      this.selection.select(this.backgroundImage.getChildByName(newData.Id));
+    });
+  }
+
+}
+
+/**
+ * 选择器
+ */
+export class Selection {
+  constructor(private workingArea: WorkingAreaComponent) {}
+  public objects: any[] = [];
+  public isMultiselection = false;
+  /**
+   * 返回选择器中是否包含对象
+   * @param obj 对象
+   */
+  public contains(obj: any): boolean {
+    return this.objects.includes(obj);
+  }
+  /**
+   * 选定对象
+   * @param obj 对象
+   */
+  public select(obj: any) {
+    if (!this.contains(obj)) {
+      this.workingArea.emit('select', obj);
+      this.objects.push(obj);
+    }
+  }
+  /**
+   * 取消选定对象
+   * @param obj 对象
+   */
+  public deselect(obj: any) {
+    if (this.contains(obj)) {
+      this.workingArea.emit('deselect', obj);
+      const idx = this.objects.findIndex(x => x === obj);
+      this.objects.splice(idx, 1);
+    }
+  }
+  /**
+   * 选定或取消选定对象
+   * @param obj 对象
+   */
+  public selectOrDeselect(obj: any) {
+    if (this.contains(obj)) {
+      this.deselect(obj);
+    } else {
+      this.select(obj);
+    }
+  }
+  /**
+   * 取消选定所有已选定对象
+   */
+  public deselectAll() {
+    this.objects.forEach(item => {
+      this.workingArea.emit('deselect', item);
+    });
+    this.objects.splice(0, this.objects.length);
+  }
+  /**
+   * 取消选定所有对象后选定一个对象
+   * @param obj 对象
+   */
+  public selectOne(obj: any) {
+    if (this.isMultiselection) {
+      this.selectOrDeselect(obj);
+    } else {
+      this.deselectAll();
+      this.select(obj);
+    }
+  }
+  /**
+   * 选定对象集合中所有对象
+   * @param objects 对象集合
+   */
+  public selectAll(objects: any[]) {
+    this.objects.forEach(item => {
+      this.select(item);
+    });
+  }
+}
+
+
+/**
+ * 车辆类型
+ */
+export enum Type {
+  水源 = 0,
+  举高喷射消防车 = 1,
+  泡沫消防车 = 2,
+  水罐消防车 = 3,
+  压缩空气泡沫消防车 = 4
+}
diff --git a/src/assets/images/noImg.png b/src/assets/images/noImg.png
new file mode 100644
index 0000000..f9e4db7
Binary files /dev/null and b/src/assets/images/noImg.png differ