locationSearch.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <el-select class="location-search" v-model="locationVal" filterable remote clearable reserve-keyword :placeholder="$t('搜索位置')"
  3. :remote-method="remoteMethod" :loading="loading" @change="handleSearchRes" popper-class="location-search-popper"
  4. :class="customClass" @clear="handleClear">
  5. <el-option v-for="(item, index) in locationOptions.list" :key="index" :label="item.title"
  6. :value="{ value: item.point, item }">
  7. <span>{{ item.title }}</span>
  8. <span class="sub-title">{{ item.province }}{{ item.city }}{{ item.district }}</span>
  9. </el-option>
  10. <template #prefix>
  11. <el-icon><Search /></el-icon>
  12. </template>
  13. </el-select>
  14. </template>
  15. <script setup>
  16. import { ref, reactive } from "vue";
  17. import { transformFromGCJToWGS } from "@/utils/WSCoordinate.js";
  18. const MAP_KEY = "CZLBZ-LJICQ-R4A5J-BN62X-YXCRJ-GNBUT";
  19. const props = defineProps({
  20. userLocation: {
  21. type: String,
  22. default: "113.61702297075017,23.584863449735067",
  23. },
  24. customClass: {
  25. type: String,
  26. default: "",
  27. },
  28. });
  29. const emit = defineEmits(["change"]);
  30. const locationVal = ref(null);
  31. const locationOptions = reactive({
  32. list: [],
  33. });
  34. const loading = ref(false);
  35. const remoteMethod = async (keyword) => {
  36. if (keyword) {
  37. locationOptions.list = [];
  38. loading.value = true;
  39. const params = {
  40. key: MAP_KEY,
  41. keyword,
  42. location: props.userLocation,
  43. };
  44. await VE_API.old_mini_map.getCtiyList({ word: keyword }).then(({ data }) => {
  45. if (data && data.length) {
  46. data.forEach((item) => {
  47. item.point = item.location.lat + "," + item.location.lng;
  48. locationOptions.list.push(item);
  49. });
  50. }
  51. });
  52. VE_API.old_mini_map.search(params).then(({ data }) => {
  53. loading.value = false;
  54. data.forEach((item) => {
  55. item.point = item.location.lat + "," + item.location.lng;
  56. locationOptions.list.push(item);
  57. });
  58. });
  59. } else {
  60. locationOptions.list = [];
  61. }
  62. };
  63. const handleSearchRes = (v) => {
  64. if (!v) return;
  65. const parts = v.value.split(",");
  66. let { latitude, longitude } = transformFromGCJToWGS(parseFloat(parts[0]), parseFloat(parts[1]));
  67. const coordinateArray = [longitude, latitude];
  68. emit("change", {
  69. coordinateArray,
  70. point: `POINT (${coordinateArray[0]} ${coordinateArray[1]})`,
  71. address: v.item?.title || v.item?.address,
  72. pointAddress: v.item?.province + v.item?.city + v.item?.district,
  73. city: v.item?.city + v.item?.district || '',
  74. adcode: v.item?.adcode != null ? String(v.item.adcode) : "",
  75. item: v.item,
  76. });
  77. };
  78. const handleClear = () => {
  79. emit("change", {
  80. coordinateArray: null,
  81. point: null,
  82. address: null,
  83. });
  84. locationVal.value = null;
  85. locationOptions.list = [];
  86. loading.value = false;
  87. };
  88. </script>
  89. <style lang="scss">
  90. .location-search {
  91. width: 100%;
  92. .el-select__wrapper {
  93. background: rgba(0, 0, 0, 0.7);
  94. box-shadow: none;
  95. border-radius: 25px;
  96. border: 1px solid rgba(255, 255, 255, 0.4);
  97. }
  98. .el-select__placeholder{
  99. color: rgba(255, 255, 255, 0.7);
  100. }
  101. .el-select__input {
  102. color: rgba(255, 255, 255, 0.9);
  103. }
  104. .el-select__prefix,
  105. .el-select__caret,
  106. .el-select__clear {
  107. color: rgba(255, 255, 255, 0.45);
  108. }
  109. }
  110. .location-search-popper {
  111. background: #1c1c1c !important;
  112. border-color: rgba(255, 255, 255, 0.12) !important;
  113. border-radius: 10px !important;
  114. .el-select-dropdown__wrap {
  115. max-height: 280px;
  116. }
  117. .el-select-dropdown__list {
  118. max-width: 96vw;
  119. padding: 4px 0;
  120. overflow-x: auto;
  121. }
  122. .el-select-dropdown__item {
  123. display: flex;
  124. align-items: center;
  125. color: rgba(255, 255, 255, 0.85);
  126. height: auto;
  127. min-height: 40px;
  128. line-height: 20px;
  129. padding: 8px 12px;
  130. &.is-hovering,
  131. &:hover {
  132. background: rgba(232, 197, 106, 0.12);
  133. }
  134. &.is-selected {
  135. color: #e8c56a;
  136. font-weight: 500;
  137. background: rgba(232, 197, 106, 0.08);
  138. }
  139. }
  140. .sub-title {
  141. padding-left: 6px;
  142. font-size: 12px;
  143. color: rgba(255, 255, 255, 0.4);
  144. flex-shrink: 0;
  145. }
  146. .el-select-dropdown__empty,
  147. .el-select-dropdown__loading {
  148. color: rgba(255, 255, 255, 0.4);
  149. }
  150. .el-popper__arrow::before {
  151. background: #1c1c1c !important;
  152. border-color: rgba(255, 255, 255, 0.12) !important;
  153. }
  154. }
  155. </style>