SameScreenComparison.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <template>
  2. <!-- 同屏对比弹窗 -->
  3. <el-dialog
  4. title="同屏对比"
  5. fullscreen
  6. v-loading="doPrintLoading"
  7. :visible.sync="dialogVisible"
  8. :before-close="handleClose"
  9. @mousedown="changeMouseDownStatus(true)"
  10. @mouseup="changeMouseDownStatus(false)"
  11. >
  12. <template slot="title">
  13. <div class="dialogTitle">
  14. <div class="dialogTitleIcon"></div>
  15. 同屏对比
  16. </div>
  17. </template>
  18. <!-- 同屏对比主题 -->
  19. <div class="ssc_main">
  20. <!-- 地图列表 -->
  21. <div class="ssc_main_mapList" id="doPrint" ref="doPrint">
  22. <div
  23. class="ssc_main_mapList_showList"
  24. :id="'map' + index"
  25. v-for="(mapIndex, index) in checkedCities"
  26. :key="index"
  27. :style="mapListBoxStyle()"
  28. @mouseover="changeMouseOverIndex(index)"
  29. >
  30. <span>{{ mapList[mapIndex].mapName }}</span>
  31. <Map
  32. ref="mapBox"
  33. :mapUrl="mapList[mapIndex].mapUrl"
  34. :index="index"
  35. :mouseIndex="mouseIndex"
  36. :centerZoom="centerZoom"
  37. :centerZoomInit="centerZoomInit"
  38. @changeCenterZoom="changeCenterZoom"
  39. />
  40. </div>
  41. </div>
  42. <!-- 底图列表 -->
  43. <div class="ssc_main_aimapList">
  44. <span>地图底图(最多选择6个) </span>
  45. <div>
  46. 已选择<span class="checkLength">{{ checkedCities.length }}</span
  47. >个
  48. </div>
  49. <el-checkbox-group id="mapCheckBox" v-model="checkedCities" @change="handleCheckedCitiesChange" :min="2" :max="6">
  50. <el-checkbox
  51. class="mapCheckBox"
  52. :disabled="checkedCities.length >= 6 && checkedCities.indexOf(index) == -1"
  53. v-for="(city, index) in mapList"
  54. :checked="city.active"
  55. :label="index"
  56. :key="index"
  57. >{{ city.mapName }}</el-checkbox
  58. >
  59. </el-checkbox-group>
  60. </div>
  61. </div>
  62. <!-- 底部文本域 -->
  63. <div id="textareaBox">
  64. <div class="doPrintInfo">时间:{{ newTime }}</div>
  65. <div class="textareaTitle">描述记录:</div>
  66. <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" placeholder="请输入内容" v-model="textarea1"> </el-input>
  67. </div>
  68. <!-- 打印区域 -->
  69. <div id="imgBox">
  70. <span>基本农田被侵占</span>
  71. <img src="" id="imgOut" style="width: 1240px; background-repeat: no-repeat; background-size: 100% 100%" />
  72. <div class="doPrintInfo2">时间:{{ newTime }}</div>
  73. <span><div>描述记录:</div></span>
  74. <div>{{ textarea1 }}</div>
  75. </div>
  76. <span slot="footer" class="dialog-footer">
  77. <el-button @click="clearDialogVisible()">取 消</el-button>
  78. <el-button type="primary" @click="subMitDialogVisible()">打 印</el-button>
  79. </span>
  80. </el-dialog>
  81. </template>
  82. <script>
  83. /**
  84. * 底部菜单(同屏对比)组件
  85. * @author: LiuMengxiang
  86. * @Date: 2022年11月21-25日
  87. */
  88. import Map from "../../map/Map.vue";
  89. import html2canvas from "html2canvas";
  90. import { Canvg } from "canvg";
  91. export default {
  92. name: "SameScreenComparison",
  93. components: { Map },
  94. data() {
  95. return {
  96. doPrintLoading: false,
  97. // 同屏对比弹窗显示状态
  98. dialogVisible: false,
  99. // 默认选中第一个
  100. checkedCities: [],
  101. // 定时器暂存
  102. interval: "",
  103. // 时间定时器暂存
  104. timeInterval: "",
  105. newTime: "",
  106. // 地图暂存显示变量
  107. centerZoom: {},
  108. // 外部地图暂存中心、层级信息
  109. centerZoomInit: {},
  110. // 当前光标是否为按下状态
  111. MouseDownStatus: false,
  112. // 光标作用的地图下标
  113. mouseIndex: -1,
  114. // 底部文本域
  115. textarea1: "",
  116. // 地图底图列表
  117. mapList: []
  118. };
  119. },
  120. created() {
  121. this.getBaseMap();
  122. },
  123. mounted() {
  124. // 同屏对比事件监听
  125. this.$bus.$off("tpdb");
  126. this.$bus.$on("tpdb", () => {
  127. this.changeShowBottomMenusStatus();
  128. });
  129. // 刷新实时时间
  130. if (!this.timeInterval) {
  131. this.timeInterval = setInterval(() => {
  132. this.newTime = this.$dayjs().format("YYYY-MM-DD HH:mm:ss");
  133. }, 1000);
  134. }
  135. },
  136. destroy() {
  137. // 当容器销毁时,需要停止监听该事件
  138. this.$bus.$off("tpdb");
  139. if (this.timeInterval) {
  140. clearInterval(this.timeInterval);
  141. this.timeInterval = "";
  142. }
  143. },
  144. props: [],
  145. methods: {
  146. // 获取地图底图服务
  147. getBaseMap() {
  148. this.mapList = [];
  149. let params = new FormData();
  150. params = {
  151. columnId: 35,
  152. states: 3,
  153. pageSize: 20,
  154. page: 0
  155. };
  156. this.$Post(this.urlsCollection.selectContentList, params).then(res => {
  157. if (res.code === 200 && res.content.data.length > 0) {
  158. let data = res.content.data;
  159. data.forEach(v => {
  160. this.mapList.unshift({
  161. mapName: v.title,
  162. mapUrl: v.c_url + "?AccessKey=lUaEMxqqhZKLSImGuP/Ergx47orYVyIqHVgxfyGpIurKAy9kdq5uU1cWuTuIXeOM",
  163. active: v.c_defined_show ? v.c_defined_show : false
  164. });
  165. });
  166. }
  167. });
  168. },
  169. // 当用户点击svg底座时,切换底部菜单显示隐藏状态。
  170. changeShowBottomMenusStatus() {
  171. // 打开弹窗
  172. this.dialogVisible = true;
  173. this.$emit("changeShowBottomMenusStatus", false);
  174. // 打开弹窗前首先同步一下地图定位
  175. let mapCenter = map2DViewer.map.getCenter();
  176. let mapZoom = map2DViewer.map.getZoom();
  177. if (mapCenter.lat && mapCenter.lng && mapZoom != undefined) {
  178. this.centerZoomInit = {
  179. lat: mapCenter.lat,
  180. lng: mapCenter.lng,
  181. zoom: mapZoom
  182. };
  183. }
  184. this.handleCheckedCitiesChange();
  185. },
  186. // 弹窗关闭询问
  187. handleClose() {
  188. if (this.dialogVisible) {
  189. this.clearDialogVisible();
  190. }
  191. },
  192. // 同屏对比取消
  193. clearDialogVisible() {
  194. // 关闭弹窗
  195. this.dialogVisible = false;
  196. // 获取地图光标暂存对象
  197. this.mouseIndex = -1;
  198. // 修改父级菜单变量(弹窗显示状态和显示底部菜单)
  199. this.$emit("changeShowBottomMenusStatus", true);
  200. },
  201. // 同屏对比表单提交
  202. subMitDialogVisible() {
  203. if (this.interval) {
  204. this.$message.info("正在打印,请稍后操作!");
  205. } else {
  206. this.cutDiv();
  207. }
  208. },
  209. // div转图片
  210. cutDiv() {
  211. try {
  212. this.doPrintLoading = true;
  213. var container = document.getElementById("doPrint").innerHTML;
  214. const _width = container.offsetWidth;
  215. const _height = container.offsetHeight;
  216. const ops = {
  217. _width,
  218. _height,
  219. useCORS: true,
  220. allowTaint: true
  221. };
  222. let that = this;
  223. let parentNodeList = [];
  224. let svgElemNodeList = [];
  225. let canvasList = [];
  226. //以下是对svg的处理(主要处理html2canvas插件显示SVG标签异常的问题)
  227. var svgElem = $("#doPrint").find("svg");
  228. svgElem.each(function (index, node) {
  229. var parentNode = node.parentNode;
  230. parentNodeList.push(parentNode);
  231. var svg = node.outerHTML.trim();
  232. svgElemNodeList.push(node);
  233. var canvas = document.createElement("canvas");
  234. canvas.className = "mapSvgCanvas";
  235. let cxt = canvas.getContext("2d");
  236. if (node.style.position) {
  237. canvas.style.position += node.style.position;
  238. canvas.style.left += node.style.left;
  239. canvas.style.top += node.style.top;
  240. }
  241. if (node.clientWidth && node.clientHeight) {
  242. canvas.setAttribute("width", node.clientWidth + "px");
  243. canvas.setAttribute("height", node.clientHeight + "px");
  244. }
  245. if (node.style.transform) {
  246. canvas.style.transform = node.style.transform;
  247. }
  248. const cv = Canvg.fromString(cxt, svg);
  249. cv.render();
  250. canvasList.push(canvas);
  251. // 隐藏原本的node元素
  252. parentNode.removeChild(node);
  253. // 添加svg转换后的canvas元素
  254. parentNode.appendChild(canvas);
  255. });
  256. // html2canvas插件实现dom转imageBase64
  257. html2canvas(this.$refs.doPrint, ops).then(
  258. canvas => {
  259. document.getElementById("imgOut").setAttribute("src", canvas.toDataURL("image/png"));
  260. setTimeout(() => {
  261. if (that.doPrintLoading) {
  262. that.doPrint(parentNodeList, svgElemNodeList, canvasList);
  263. }
  264. }, 1000);
  265. },
  266. error => {
  267. that.$message.error(error);
  268. this.showMapSvgAndRemoveMapCanvas(parentNodeList, svgElemNodeList, canvasList);
  269. this.doPrintLoading = false;
  270. }
  271. );
  272. setTimeout(() => {
  273. if (this.doPrintLoading) {
  274. this.$message.error("打印超时请稍后重试!");
  275. this.showMapSvgAndRemoveMapCanvas(parentNodeList, svgElemNodeList, canvasList);
  276. this.doPrintLoading = false;
  277. }
  278. }, 10000);
  279. } catch (e) {
  280. if (this.doPrintLoading) {
  281. this.$message.error("打印异常,异常信息:" + e);
  282. this.showMapSvgAndRemoveMapCanvas(parentNodeList, svgElemNodeList, canvasList);
  283. this.doPrintLoading = false;
  284. }
  285. }
  286. },
  287. // 显示原本的svg并删除svg转换为canvas的元素
  288. showMapSvgAndRemoveMapCanvas(parentNodeList, svgElemNodeList, canvasList) {
  289. try {
  290. parentNodeList.forEach((parentNodeListItem, index) => {
  291. parentNodeListItem.appendChild(svgElemNodeList[index]);
  292. parentNodeListItem.removeChild(canvasList[index]);
  293. });
  294. } catch (e) {
  295. this.$message.error("打印异常,异常信息:" + e);
  296. this.doPrintLoading = false;
  297. }
  298. },
  299. // div打印(iframe的方式为最优)
  300. doPrint(parentNodeList, svgElemNodeList, canvasList) {
  301. // 将包含转为图片的div获取。
  302. var new_iframe = document.createElement("IFRAME");
  303. var doc = null;
  304. document.body.appendChild(new_iframe);
  305. doc = new_iframe.contentWindow.document;
  306. var obj = document.getElementById("imgBox");
  307. doc.write(obj.innerHTML);
  308. doc.close();
  309. this.showMapSvgAndRemoveMapCanvas(parentNodeList, svgElemNodeList, canvasList);
  310. this.doPrintLoading = false;
  311. new_iframe.contentWindow.focus();
  312. new_iframe.contentWindow.print();
  313. document.body.removeChild(new_iframe);
  314. },
  315. // 修改选中地图
  316. handleCheckedCitiesChange(e) {
  317. setTimeout(() => {
  318. this.checkedCities.forEach((item, index) => {
  319. this.$refs["mapBox"][index].$nextTick(() => {
  320. this.$refs["mapBox"][index].changeMapSize();
  321. this.$refs["mapBox"][index].addSinglePolygon(false);
  322. });
  323. });
  324. }, 300);
  325. },
  326. // 根据地图列表,返回合适的样式
  327. mapListBoxStyle() {
  328. if (this.checkedCities.length >= 5) {
  329. return "width:calc(33% - 2px);height:calc(50% - 2px);";
  330. } else if (this.checkedCities.length === 4) {
  331. return "width:calc(50% - 2px);height:calc(50% - 2px);";
  332. } else if (this.checkedCities.length === 3) {
  333. return "width:calc(33% - 2px);height:calc(100%);";
  334. } else if (this.checkedCities.length === 2) {
  335. return "width:calc(50% - 2px);height:calc(100%);";
  336. } else {
  337. return "width:calc(100%);height:calc(100%);";
  338. }
  339. },
  340. // 当其中一个地图移动或缩放时
  341. changeCenterZoom(data) {
  342. this.centerZoom = data;
  343. },
  344. // 暂存当前光标所在map组件的下标
  345. changeMouseDownStatus(status) {
  346. this.MouseDownStatus = status;
  347. },
  348. changeMouseOverIndex(mouseIndex) {
  349. // 当鼠标按下后(this.MouseDownStatus = true)暂存当前光标所在map组件的下标将不再修改
  350. if (!this.MouseDownStatus) {
  351. this.mouseIndex = mouseIndex;
  352. }
  353. }
  354. },
  355. watch: {}
  356. };
  357. </script>
  358. <style lang="less" scoped>
  359. .ssc_main {
  360. display: flex;
  361. width: 100%;
  362. height: calc(100vh - 310px);
  363. position: relative;
  364. #textareaBox {
  365. width: calc(100% - 20px);
  366. height: 100px;
  367. position: absolute;
  368. bottom: 0;
  369. }
  370. &_mapList {
  371. width: calc(100% - 20rem);
  372. height: 100%;
  373. display: flex;
  374. flex-wrap: wrap;
  375. padding: 10px;
  376. box-sizing: border-box;
  377. overflow-x: hidden;
  378. overflow-y: auto;
  379. scrollbar-width: none; /* Firefox */
  380. -ms-overflow-style: none; /* IE 10+ */
  381. &::-webkit-scrollbar {
  382. display: none; /* Chrome Safari */
  383. }
  384. &_showList {
  385. border: 1px solid #ccc;
  386. text-align: right;
  387. position: relative;
  388. span {
  389. position: absolute;
  390. right: 10px;
  391. top: 10px;
  392. font-size: 22px;
  393. font-weight: bold;
  394. color: #fff;
  395. z-index: 999;
  396. -moz-user-select: none;
  397. -webkit-user-select: none;
  398. -ms-user-select: none;
  399. -khtml-user-select: none;
  400. user-select: none;
  401. }
  402. }
  403. }
  404. &_aimapList {
  405. width: 20rem;
  406. height: 100%;
  407. font-size: 20px;
  408. color: #fff;
  409. padding: 10px;
  410. box-sizing: border-box;
  411. background: #002645 !important;
  412. .checkLength {
  413. font-size: 24px;
  414. color: #74ffff;
  415. }
  416. #mapCheckBox {
  417. height: calc(100% - 58px);
  418. width: 100%;
  419. overflow-x: hidden;
  420. overflow-y: auto;
  421. scrollbar-width: none; /* Firefox */
  422. -ms-overflow-style: none; /* IE 10+ */
  423. border: 1px solid #fff;
  424. &::-webkit-scrollbar {
  425. display: none; /* Chrome Safari */
  426. }
  427. }
  428. .mapCheckBox:hover {
  429. color: #ffffff;
  430. background: #002645 !important;
  431. }
  432. .mapCheckBox[disabled] {
  433. color: #666666;
  434. cursor: not-allowed;
  435. }
  436. .mapCheckBox {
  437. width: 100%;
  438. padding: 10px 5px;
  439. border-bottom: 1px solid;
  440. color: #cccccc;
  441. font-size: 18px;
  442. background: #001e37 !important;
  443. .el-checkbox__label {
  444. font-size: 18px;
  445. }
  446. }
  447. }
  448. }
  449. .textareaTitle {
  450. font-size: 20px;
  451. font-weight: 400;
  452. }
  453. #imgBox {
  454. display: none;
  455. font-size: 16px;
  456. color: #000;
  457. font-weight: 400;
  458. span {
  459. font-size: 20px;
  460. color: #000000;
  461. font-weight: bold;
  462. padding: 20px 10px;
  463. }
  464. }
  465. .doPrintInfo {
  466. color: #ffffff;
  467. }
  468. .doPrintInfo2 {
  469. color: #000000;
  470. }
  471. </style>