a0be4b85aa6b3fb55000982b72b8e83285f441c3.svn-base 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <template>
  2. <div class="coord" v-drag="'.coord .ant-card-head'">
  3. <a-card v-show='value' title="定位" style="width: 315px; box-shadow: 0 0 5px #357ee5;">
  4. <a-icon slot="extra" type="close" style='cursor: pointer; color: #FFFFFF;' @click="$emit('input',false)"/>
  5. <a-form-model ref="ruleForm" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
  6. <a-form-model-item style="margin-bottom: 10px" ref="locate" label="输入坐标" prop="locate">
  7. <a-input v-model.trim="form.locate" @pressEnter='onSubmit' placeholder='请输入坐标' allow-clear @blur="
  8. () => {
  9. $refs.locate.onFieldBlur();
  10. }
  11. "/>
  12. </a-form-model-item>
  13. <template v-if="data.length>1">
  14. <a-list style='margin-top: 20px;' item-layout="horizontal" :data-source="data">
  15. <a-list-item @click="handleCoordsItem(index)" slot="renderItem" slot-scope="item, index">
  16. <a-list-item-meta >
  17. <span slot="title">{{ item}}</span>
  18. <a-avatar slot="avatar" class="result" :src="require(`@/assets/result${index+1}.png`)">
  19. </a-avatar>
  20. </a-list-item-meta>
  21. </a-list-item>
  22. </a-list>
  23. </template>
  24. <template v-else>
  25. <a-form-model-item style="margin-bottom: 5px" v-show='!isGeodetic&&coordsArr.length>0' label="经纬度:">
  26. <span class="ant-form-text text">
  27. {{ form.geodetic }}
  28. </span>
  29. </a-form-model-item>
  30. <a-form-model-item style="margin-bottom: 5px" v-show='!isDegree&&coordsArr.length>0' label="度分秒:">
  31. <span class="ant-form-text text">
  32. {{ form.degree }}
  33. </span>
  34. </a-form-model-item>
  35. <a-form-model-item style="margin-bottom: 10px" v-show='!isProjCoord&&coordsArr.length>0' label="投影坐标:">
  36. <span class="ant-form-text text">
  37. {{ form.projCoord }}
  38. </span>
  39. </a-form-model-item>
  40. </template>
  41. <a-form-model-item :wrapper-col="{ span: 17, offset: 6 }">
  42. <a-button type="primary" @click="onSubmit">
  43. 定 位
  44. </a-button>
  45. <a-button style="margin-left: 5px;" @click="resetForm">
  46. 重 置
  47. </a-button>
  48. <a-button style="margin-left: 5px;" type="link" @click="handleHistory" >
  49. 历史记录
  50. </a-button>
  51. </a-form-model-item>
  52. </a-form-model>
  53. <a-list v-show="history" style='margin-top: 10px;border-top: 1px solid #8a8af4; max-height: 550px ' item-layout="horizontal" :data-source="historyList" :pagination="pagination">
  54. <a-list-item slot="renderItem" style="display:block;cursor: auto" slot-scope="item, index" :key="item.id" >
  55. <a-list-item-meta :description="item.inputval" >
  56. <span style=" display: inline-block; word-break: break-all;white-space: normal; cursor: text; color: #333" slot="title">{{ item.name||'未命名'}}</span>
  57. </a-list-item-meta>
  58. <a slot="actions" @click="handleHistoryLocate(item)">定位</a>
  59. <a slot="actions" @click="showModal(item,index)">编辑</a>
  60. <a-popconfirm slot="actions" title="确定删除吗?" @confirm="() => handleRemove(item.id,index)">
  61. <a>删除</a>
  62. </a-popconfirm>
  63. </a-list-item>
  64. </a-list>
  65. <a-modal
  66. title="编辑"
  67. :visible="visible"
  68. :confirm-loading="confirmLoading"
  69. @ok="handleOk"
  70. @cancel="handleCancel"
  71. >
  72. <a-form-model >
  73. <a-row>
  74. <a-col :span="24">
  75. <a-form-model-item label="名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
  76. <a-input v-model="model.name"></a-input>
  77. </a-form-model-item>
  78. </a-col>
  79. <a-col :span="24">
  80. <a-form-model-item label="坐标" :labelCol="labelCol" :wrapperCol="wrapperCol">
  81. <a-input v-model="model.inputval" disabled rows="4" />
  82. </a-form-model-item>
  83. </a-col>
  84. </a-row>
  85. </a-form-model>
  86. </a-modal>
  87. </a-card>
  88. </div>
  89. </template>
  90. <script>
  91. import {transform} from 'ol/proj';
  92. import proj4 from 'proj4';
  93. import {register} from "ol/proj/proj4";
  94. import SimpleMarker from "@/components/BasicMap/Tools/SimpleMarker.vue";
  95. import Feature from "ol/Feature";
  96. import {Vector as VectorLayer} from "ol/layer";
  97. import {Vector as VectorSource, Cluster} from "ol/source";
  98. import Point from "ol/geom/Point";
  99. import {
  100. Icon,
  101. Style,
  102. Fill,
  103. Text,
  104. } from "ol/style";
  105. import * as olEasing from "ol/easing";
  106. import {deleteAction, getAction, postAction} from "../../../api/manage";
  107. export default {
  108. name: 'CoordsLocate',
  109. beforeCreate() {
  110. proj4.defs("EPSG:4548",
  111. "+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs");
  112. proj4.defs("EPSG:4527",
  113. "+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=39500000 +y_0=0 +ellps=GRS80 +units=m +no_defs");
  114. register(proj4);
  115. },
  116. data() {
  117. //校验
  118. let validateLocate = (rule, value, callback) => {
  119. if (value === '') {
  120. callback(new Error('请输入坐标!'));
  121. } else {
  122. // let reg1 = /[^\d\,\,\°\′\″\ \t]/g
  123. let reg2 = /^(\-|\+)?\d+(\.\d+)?[\uff0c\,\ \t](\-|\+)?\d+(\.\d+)?$/;//4180693.903 541105.228
  124. // let reg2 = /^[\-\+]?(180|1?[0-7]?\d)*([.][0-9]+)?[\uff0c\,\ \t][\-\+]?(90|[0-8]?\d)([.][0-9]+)?$/;
  125. let reg3 = this.reg3;
  126. if (value.indexOf(';') !== -1 || value.indexOf(';') !== -1) {
  127. value.replace(';', ';');
  128. this.coordsArr = value.split(';');
  129. this.coordsArr.forEach((item, index) => {
  130. if (reg2.test(item) || reg3.test(item)) {
  131. } else {
  132. callback(new Error('请输入规范的坐标(只允许输入数字,空格,逗号,与度分秒)!多个坐标请用分号(;)分隔'));
  133. }
  134. })
  135. callback();
  136. } else {
  137. if (reg2.test(value) || reg3.test(value)) {
  138. if (reg3.test(value)) {
  139. this.isDegree = true;
  140. } else {
  141. this.isDegree = false;
  142. }
  143. callback();
  144. } else {
  145. callback(new Error('请输入规范的坐标(只允许输入数字,空格,逗号,与度分秒)!多个坐标请用分号(;)分隔'));
  146. }
  147. }
  148. }
  149. }
  150. return {
  151. map: {},
  152. model:{
  153. },
  154. labelCol: {span: 6},
  155. wrapperCol: {span: 17},
  156. visible: false,
  157. confirmLoading: false,
  158. currentIndex:'',
  159. form: {
  160. locate: '',
  161. geodetic: '',
  162. projCoord: '',
  163. degree: '',
  164. },
  165. pagination: {
  166. onChange: page => {
  167. this.pagination.current=page;
  168. this.getHistory();
  169. },
  170. disabled: false,
  171. pageSize: 10,
  172. total: 0,
  173. size: 'small',
  174. current:1
  175. },
  176. reg3: /^[\-\+]?(180|1?[0-7]?\d)°([1-5]?[0-9]?\d)′([1-5]?[0-9]?\d(\.\d+)?)″[\uff0c\,\ \t][\-\+]?(90|[0-8]?\d)°([1-5]?[0-9]?\d)′([1-5]?[0-9]?\d(\.\d+)?)″$/,
  177. coordsArr: [],
  178. coords:'',
  179. clusterSource: '',
  180. clusters: '',
  181. data:[],
  182. isDegree: true,
  183. isGeodetic: true,
  184. isProjCoord: true,
  185. history:false,
  186. historyList:[],
  187. rules: {
  188. locate: [{
  189. validator: validateLocate,
  190. trigger: 'blur'
  191. }],
  192. },
  193. };
  194. },
  195. props: {
  196. value: {
  197. type: Boolean,
  198. default: false,
  199. required: true
  200. }
  201. },
  202. watch:{
  203. value:function (newValue) {
  204. if(newValue===false){
  205. // this.resetForm();
  206. this.history=false;
  207. }
  208. }
  209. },
  210. inject: ['baseMap'],
  211. methods: {
  212. handleHistoryLocate(item){
  213. this.form.locate= item.inputval;
  214. this.onSubmit();
  215. },
  216. onSubmit() {
  217. this.coordsArr = [];
  218. this.map = this.baseMap.map;
  219. this.$refs.ruleForm.validate((valid) => {
  220. if (valid) {
  221. let arrLo;
  222. if (this.coordsArr.length !== 0) {
  223. this.coordsArr.forEach((item, index) => {
  224. this.coordsArr[index] = this.replaceLocate(item);
  225. });
  226. } else {
  227. arrLo = this.replaceLocate(this.form.locate);
  228. this.coordsArr.push(arrLo);
  229. }
  230. if(this.coordsArr.length>10){
  231. this.$message.error('最多支持同时查询十个点!');
  232. return;
  233. }
  234. this.data = this.coordsArr.slice(0);
  235. this.coordsArr.forEach((item, index) => {
  236. if (this.reg3.test(item)) {
  237. this.isGeodetic = false;
  238. this.isDegree = true;
  239. this.isProjCoord = false;
  240. item.forEach((itemA, index) => {
  241. itemA = itemA.replace('°', ',');
  242. itemA = itemA.replace('′', ',');
  243. itemA = itemA.replace('″', ',');
  244. itemA = this.changeDegree(itemA);
  245. item[index] = itemA;
  246. });
  247. } else {
  248. this.isGeodetic = true;
  249. this.isDegree = false;
  250. this.isProjCoord = false;
  251. }
  252. // 将字符串类型转换为数字类型
  253. item.forEach((itemB, index) => {
  254. item[index] = parseFloat(itemB);
  255. })
  256. // 默认按从大到小排
  257. item.sort((a, b) => {
  258. return b - a;
  259. });
  260. let projCoord
  261. if (item[0] > 39000000) {
  262. this.isGeodetic = false;
  263. this.isDegree = false;
  264. this.isProjCoord = true;
  265. item.forEach((itemC, index) => {
  266. item[index] = parseFloat(itemC.toFixed(3));
  267. });
  268. this.form.projCoord = item.join(',').slice(2);
  269. item = transform(item, 'EPSG:4527', 'EPSG:4490');
  270. } else if (item[0] > 180) {
  271. this.isGeodetic = false;
  272. this.isDegree = false;
  273. this.isProjCoord = true;
  274. item.forEach((itemD, index) => {
  275. item[index] = parseFloat(itemD.toFixed(3))
  276. });
  277. this.form.projCoord = item.join(',');
  278. item.reverse();
  279. item = transform(item, 'EPSG:4548', 'EPSG:4490');
  280. } else {
  281. projCoord = transform(item, 'EPSG:4490', 'EPSG:4548');
  282. projCoord.forEach((item, index) => {
  283. projCoord[index] = parseFloat(item.toFixed(3));
  284. });
  285. this.form.projCoord = projCoord.join(',');
  286. }
  287. this.coordsArr[index] = item;
  288. this.form.geodetic = item.map((item, index) => {
  289. return item = parseFloat(item.toFixed(6));
  290. }).join(',');
  291. this.form.degree = item.map((item) => {
  292. return item = this.formatDegree(item)
  293. }).join(',');
  294. if (item[0] > 180 || item[1] > 90) {
  295. this.$message.error('输入数据有误!');
  296. }
  297. });
  298. this.locateByCoords();
  299. } else {
  300. return false;
  301. }
  302. });
  303. },
  304. replaceLocate(loc) {
  305. // 将英文空格转换为,
  306. let locate = loc;
  307. if (loc.includes(' ')) {
  308. locate = loc.replace(' ', ',');
  309. } else if (loc.includes(',')) {
  310. locate = loc.replace(',', ',');
  311. } else if (loc.includes(' ')) {
  312. locate = loc.replace(' ', ',');
  313. }else if(loc.includes('\t')) {
  314. locate = loc.replace('\t', ',');
  315. }
  316. let it = locate.split(',');
  317. return it;
  318. },
  319. //度分秒转换为度
  320. changeDegree(coord) {
  321. let arr = coord.split(',');
  322. let miao = arr[2];
  323. let fen = arr[1];
  324. let du = arr[0];
  325. var mFen = 0;
  326. if (miao != null && miao !== '') {
  327. mFen = Number(miao / 60);
  328. }
  329. var fDu = 0;
  330. if (fen != null && fen != '') {
  331. fDu = (Number(fen) + mFen) / 60;
  332. } else {
  333. fDu = mFen;
  334. }
  335. var lDu = 0;
  336. if (du != null && du != '') {
  337. lDu = (Number(du) + fDu).toFixed(6);
  338. } else {
  339. lDu = fDu.toFixed(6);
  340. }
  341. return lDu;
  342. },
  343. formatDegree(value) {
  344. if (value != null && value != '') {
  345. ///<summary>将度转换成为度分秒</summary>
  346. value = Math.abs(value); //返回数的绝对值
  347. var v1 = Math.floor(value); //度 //对数进行下舍入
  348. var v2 = Math.floor((value - v1) * 60); //分
  349. var v3 = Math.round((value - v1) * 3600 % 60); //秒 //把数四舍五入为最接近的整数
  350. return v1 + '°' + v2 + '′' + v3 + '″';
  351. } else {
  352. return '' + ';' + '' + ';' + '' + ';';
  353. }
  354. },
  355. async locateByCoords() {
  356. if(this.coords.toString()===this.coordsArr.toString()){
  357. this.locateFit();
  358. return;
  359. }
  360. this.coords = this.coordsArr;
  361. let {success,message} = await postAction('hzz.lochistory/locHistory/add',{inputval:this.form.locate});
  362. if(!success){
  363. this.$message.error(message);
  364. return;
  365. }
  366. // 移除之前的
  367. if (this.clusters) {
  368. this.clusterSource.getSource().clear(); //移除聚合标注数据源中的所有要素
  369. this.map.removeLayer(this.clusters);
  370. this.clusters='';
  371. this.clusterSource=''
  372. }
  373. this.locateFit();
  374. setTimeout(() => {
  375. var source = new VectorSource({
  376. features: this.features,
  377. });
  378. // 聚合标注数据源
  379. this.clusterSource = new Cluster({
  380. distance: 0, //这个是通过 distance 来控制两个点聚合的间距
  381. source: source,
  382. });
  383. // 加载聚合标注的矢量图层
  384. var styleCache = {}; //用于保存特定数量的聚合群的要素样式
  385. this.clusters = new VectorLayer({
  386. source: this.clusterSource,
  387. style: function (feature, resolution) {
  388. var size = feature.get("features").length; //获取该要素所在聚合群的要素数量
  389. var style = styleCache[size];
  390. if (!style) {
  391. style = [
  392. new Style({
  393. image: new Icon({
  394. src: require(`../../../assets/result${feature.get("features")[0].values_.name.toString()}.png`),
  395. displacement: [0, 16.5]
  396. })
  397. }),
  398. ];
  399. }
  400. return style;
  401. },
  402. zIndex: 999,
  403. });
  404. this.map.addLayer(this.clusters);
  405. }, 500);
  406. },
  407. handleCoordsItem(index){
  408. let coords = this.coordsArr[index];
  409. let view = this.baseMap.map.getView();
  410. const zoom = view.getZoom();
  411. view.animate({
  412. zoom: zoom - 1,
  413. duration: 300
  414. }, {
  415. center: coords,
  416. zoom: 16,
  417. duration: 2000
  418. })
  419. },
  420. locateFit(){
  421. let lng = [];
  422. let lat = [];
  423. this.features = [];
  424. this.coords.forEach((item, index) => {
  425. lng.push(item[0]);
  426. lat.push(item[1]);
  427. this.features.push(
  428. new Feature({
  429. geometry: new Point(item),
  430. name: index + 1,
  431. })
  432. );
  433. });
  434. lng.sort();
  435. lat.sort();
  436. let extent = [lng[0], lat[0], lng[lat.length-1],lat[lat.length-1] ];
  437. let view = this.baseMap.map.getView();
  438. const zoom = view.getZoom();
  439. view.animate({
  440. zoom: zoom - 1,
  441. duration: 300
  442. });
  443. view.fit(extent, {
  444. // size:[500,300],
  445. duration: 2000,
  446. padding: [100, 100, 100, 100],
  447. // easing: olEasing.inAndOut
  448. })
  449. },
  450. showModal(item,index) {
  451. this.visible = true;
  452. this.model.name = item.name;
  453. this.model.inputval = item.inputval;
  454. this.model.id = item.id;
  455. this.currentIndex = index;
  456. },
  457. async handleOk(e) {
  458. this.confirmLoading = true;
  459. let {success,result,message} = await postAction('hzz.lochistory/locHistory/edit',{...this.model})
  460. if(success){
  461. this.visible = false;
  462. this.confirmLoading = false;
  463. this.historyList[this.currentIndex].name = this.model.name;
  464. }else {
  465. this.$message.error(message)
  466. }
  467. },
  468. handleCancel(e) {
  469. this.visible = false;
  470. },
  471. async handleRemove(id,index) {
  472. let {success,result,message} = await deleteAction('hzz.lochistory/locHistory/delete',{id})
  473. if(success){
  474. this.$message.success('删除成功');
  475. this.historyList.splice(index,1);
  476. }else {
  477. this.$message.error(message);
  478. }
  479. },
  480. resetForm() {
  481. this.$refs.ruleForm.resetFields();
  482. this.isDegree = true;
  483. this.isGeodetic = true;
  484. this.isProjCoord = true;
  485. this.coordsArr=[];
  486. this.data=[];
  487. if (this.clusters) {
  488. this.clusters.setSource(null)
  489. this.map.removeLayer(this.clusters);
  490. this.clusters='';
  491. this.clusterSource=''
  492. }
  493. this.coords = "";
  494. },
  495. handleHistory(){
  496. if(this.history){
  497. this.history=false;
  498. }else {
  499. this.history=true;
  500. this.getHistory();
  501. }
  502. },
  503. async getHistory(){
  504. // console.log(this.$store.getters.userinfo)
  505. let {success,result,message} = await getAction('hzz.lochistory/locHistory/list',{createBy:this.$store.state.user.info.username,pageNo: this.pagination.current,column: 'createTime',order: 'desc',field: 'id,,,createBy,createTime,name,inputval,action',});
  506. if(success){
  507. this.historyList=result.records;
  508. this.pagination.total = parseInt(result.total);
  509. }else {
  510. this.$message.error(message);
  511. }
  512. },
  513. },
  514. }
  515. </script>
  516. <style>
  517. .coord .ant-list-item-meta-description {
  518. word-break: break-all;
  519. white-space: normal;
  520. cursor: text;
  521. user-select: text;
  522. }
  523. .coord .ant-spin-nested-loading {
  524. max-height: 200px;
  525. overflow-y: auto;
  526. }
  527. .coord .ant-card-body {
  528. padding: 1em;
  529. }
  530. .coord .ant-list-item-action {
  531. text-align: end;
  532. }
  533. .coord .ant-form-item-children{
  534. display: flex;
  535. justify-content: space-around;
  536. align-items: center;
  537. }
  538. .coord .text {
  539. cursor: text;
  540. color: rgba(0, 0, 0, .65);
  541. font-weight: 400;
  542. font-size: 14px;
  543. padding: 4px 11px;
  544. background-color: #fafafa;
  545. border: 1px solid #d9d9d9;
  546. border-radius: 4px;
  547. transition: all .3s;
  548. width: 100%;
  549. height: 32px;
  550. line-height: 1.5;
  551. display: inline-block;
  552. user-select: text;
  553. }
  554. </style>