From 84abbd63fa039a39358fb8db7526b567422f3406 Mon Sep 17 00:00:00 2001
From: tuqin <tuqin@tuqin.cn>
Date: 星期四, 05 十一月 2026 16:38:49 +0800
Subject: [PATCH] fix(hrm): 百度地图不可用时降级为手工填坐标,不再阻塞考勤规则保存

---
 src/views/hrm/attendance/rule/modules/location-map-picker.vue |  169 ++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 139 insertions(+), 30 deletions(-)

diff --git a/src/views/hrm/attendance/rule/modules/location-map-picker.vue b/src/views/hrm/attendance/rule/modules/location-map-picker.vue
index 63014eb..7793ee1 100644
--- a/src/views/hrm/attendance/rule/modules/location-map-picker.vue
+++ b/src/views/hrm/attendance/rule/modules/location-map-picker.vue
@@ -2,7 +2,7 @@
 <script lang="ts" setup>
 import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
 
-import { Alert, Button, Input, Spin, message } from 'ant-design-vue';
+import { Alert, Button, Input, InputNumber, Spin, message } from 'ant-design-vue';
 
 import { loadBaiduMapSdk } from '#/components/map';
 
@@ -46,6 +46,20 @@
 const loading = ref(false);
 const searchKeyword = ref('');
 const selectedAddress = ref('');
+
+/**
+ * 鎵嬪伐濉啓妯″紡銆�
+ *
+ * 瑙﹀彂鍦烘櫙锛氱櫨搴﹀湴鍥� SDK 鎷夸笉鍒帮紙浼佷笟缃戠粶鎷︽埅 api.map.baidu.com銆�
+ * 娴忚鍣ㄦ彃浠舵嫤鎴�乤k 鍩熷悕鐧藉悕鍗曟湭鏀惧紑绛夛級銆�
+ * 姝ゅ墠杩欑鎯呭喌浼氱洿鎺ユ姏 "Cannot read properties of null (reading 'Map')" 鎴�
+ * "鐧惧害鍦板浘 SDK 鍔犺浇澶辫触"锛岀敤鎴疯繛鑰冨嫟瑙勫垯閮藉瓨涓嶄簡銆�
+ * 鐜板湪鏀逛负锛氬湴鍥句笉鍙敤鏃惰嚜鍔ㄥ垏鎹㈠埌鎵嬪伐杈撳叆缁忕含搴︼紝淇濊瘉涓氬姟涓嶄腑鏂��
+ */
+const manualMode = ref(false);
+const manualName = ref('');
+const manualLongitude = ref<number>();
+const manualLatitude = ref<number>();
 
 let map: AnyMap;
 let marker: AnyMap;
@@ -254,6 +268,57 @@
   });
 }
 
+/**
+ * 鍒囨崲鍒版墜宸ュ~鍐欐ā寮忋��
+ *
+ * 鐧惧害鍦板浘渚濊禆绗笁鏂� CDN锛坅pi.map.baidu.com锛夛紝鍦ㄥ唴缃戙�佽闃茬伀澧欐嫤鎴��
+ * 娴忚鍣ㄦ彃浠舵嫤鎴�乤k 鍩熷悕鐧藉悕鍗曟湭鏀惧紑鐨勫満鏅笅閮芥嬁涓嶅埌 SDK銆�
+ * 姝ゆ椂蹇呴』闄嶇骇锛屽惁鍒欑敤鎴疯繛鑰冨嫟瑙勫垯閮藉瓨涓嶄簡锛堝悗绔姹傚畾浣嶅紑鍚椂鍦扮偣鍚嶇О+缁忕含搴﹀繀濉級銆�
+ */
+function switchToManual(reason: string) {
+  if (disposed) {
+    return;
+  }
+  manualMode.value = true;
+  loading.value = false;
+  if (!manualName.value) {
+    manualName.value = selectedAddress.value || '';
+  }
+  if (!Number.isFinite(manualLongitude.value) && Number.isFinite(props.longitude)) {
+    manualLongitude.value = Number(props.longitude);
+  }
+  if (!Number.isFinite(manualLatitude.value) && Number.isFinite(props.latitude)) {
+    manualLatitude.value = Number(props.latitude);
+  }
+  message.warning(`${reason}锛屽凡鍒囨崲涓烘墜宸ュ~鍐欏潗鏍嘸);
+}
+
+/** 鎵嬪伐濉啓鍧愭爣鍚庡洖濉〃鍗� */
+function applyManual() {
+  const longitude = Number(manualLongitude.value);
+  const latitude = Number(manualLatitude.value);
+  if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
+    message.warning('璇峰~鍐欑粡搴﹀拰绾害');
+    return;
+  }
+  if (longitude < -180 || longitude > 180) {
+    message.warning('缁忓害搴斿湪 -180 ~ 180 涔嬮棿');
+    return;
+  }
+  if (latitude < -90 || latitude > 90) {
+    message.warning('绾害搴斿湪 -90 ~ 90 涔嬮棿');
+    return;
+  }
+  const name = manualName.value.trim();
+  emit('select', {
+    address: name || undefined,
+    latitude: Number(latitude.toFixed(7)),
+    locationName: name || undefined,
+    longitude: Number(longitude.toFixed(7)),
+  });
+  message.success('鍧愭爣宸插簲鐢�');
+}
+
 async function handleSearch() {
   const keyword = searchKeyword.value.trim();
   if (!keyword || !geocoder) {
@@ -295,7 +360,7 @@
     }
     // SDK 鍥炶皟瑙﹀彂浣� BMapGL 鏈寕杞斤紙甯歌浜� ak 鏃犳晥 / 鍩熷悕鏈巿鏉� / 琚綉缁滄嫤鎴級
     if (!window.BMapGL) {
-      message.error('鐧惧害鍦板浘鍔犺浇澶辫触锛岃妫�鏌ュ湴鍥� Key 閰嶇疆鎴栫綉缁�');
+      switchToManual('鍦板浘缁勪欢鏈氨缁紝鍙墜宸ュ~鍐欏潗鏍�');
       return;
     }
     const hasCoordinates =
@@ -329,7 +394,8 @@
       selectedAddress.value = location.address ?? '';
     }
   } catch (error: any) {
-    message.error(error?.message || '鍦板浘鍒濆鍖栧け璐�');
+    // 鍦板浘涓嶅彲鐢紙缃戠粶鎷︽埅 / ak 鏈巿鏉� / 瓒呮椂锛夋椂闄嶇骇涓烘墜宸ュ~鍐欙紝涓嶈兘闃诲涓氬姟
+    switchToManual(error?.message || '鍦板浘鍔犺浇澶辫触');
   } finally {
     loading.value = false;
   }
@@ -384,33 +450,76 @@
     type="warning"
     show-icon
     message="鐧惧害鍦板浘灏氭湭閰嶇疆"
-    description="璇烽厤缃� VITE_BAIDU_MAP_KEY 鍚庨噸鏂版瀯寤哄墠绔紱鏈厤缃椂鍙墜宸ュ~鍐欑粡绾害銆�"
+    description="鏈厤缃� VITE_BAIDU_MAP_KEY锛岃鎵嬪伐濉啓鎵撳崱鍦扮偣鐨勭粡绾害銆�"
   />
-  <Spin v-else :spinning="loading">
-    <div v-if="!disabled" class="mb-2 flex gap-2">
-      <Input
-        v-model:value="searchKeyword"
-        allow-clear
-        placeholder="杈撳叆鐪併�佸競銆佸尯鍜屽叿浣撳湴鐐癸紝渚嬪锛氫箤椴佹湪榻愬競鍘傚尯"
-        @press-enter="handleSearch"
+  <template v-else>
+    <!-- 鍦板浘涓嶅彲鐢ㄦ椂鐨勬墜宸ュ~鍐欐ā寮忥紝淇濊瘉涓氬姟涓嶈绗笁鏂� CDN 鍗′綇 -->
+    <template v-if="manualMode">
+      <Alert
+        type="info"
+        show-icon
+        class="mb-2"
+        message="鍦板浘涓嶅彲鐢紝宸插垏鎹负鎵嬪伐濉啓鍧愭爣"
+        description="濡傞暱鏈熸棤娉曞姞杞藉湴鍥撅紝璇锋鏌ユ湇鍔″櫒/娴忚鍣ㄦ槸鍚﹀厑璁歌闂� api.map.baidu.com锛屾垨鑱旂郴绠$悊鍛樼‘璁ょ櫨搴﹀湴鍥� Key 鐨勫煙鍚嶇櫧鍚嶅崟銆�"
       />
-      <Button :loading="loading" @click="handleSearch">鎼滅储鍦板潃</Button>
-    </div>
-    <div
-      ref="mapContainer"
-      class="h-[360px] w-full overflow-hidden rounded border border-gray-200"
-    ></div>
-    <div
-      class="mt-2 flex flex-wrap justify-between gap-2 text-xs text-gray-500"
-    >
-      <span>
-        {{
-          disabled
-            ? '鎵撳崱浣嶇疆鍙婂厑璁歌寖鍥�'
-            : '鐐瑰嚮鍦板浘閫夋嫨鎵撳崱涓績鐐癸紱钃濊壊鍦嗗湀琛ㄧず鍏佽鎵撳崱鑼冨洿'
-        }}
-      </span>
-      <span v-if="selectedAddress">{{ selectedAddress }}</span>
-    </div>
-  </Spin>
+      <div class="grid grid-cols-3 gap-2">
+        <Input
+          v-model:value="manualName"
+          :disabled="disabled"
+          placeholder="鎵撳崱鍦扮偣鍚嶇О锛屽锛氬巶鍖哄ぇ闂�"
+        />
+        <InputNumber
+          v-model:value="manualLongitude"
+          :disabled="disabled"
+          :precision="7"
+          :step="0.0001"
+          class="w-full"
+          placeholder="缁忓害锛屽 87.616848"
+        />
+        <InputNumber
+          v-model:value="manualLatitude"
+          :disabled="disabled"
+          :precision="7"
+          :step="0.0001"
+          class="w-full"
+          placeholder="绾害锛屽 43.825592"
+        />
+      </div>
+      <div class="mt-2 flex items-center justify-between">
+        <span class="text-xs text-gray-500">
+          鍧愭爣椤讳负 WGS84锛圙PS 鍘熷鍧愭爣锛夛紝鍏佽鑼冨洿 {{ radiusMeters }} 绫�
+        </span>
+        <Button v-if="!disabled" type="primary" @click="applyManual">
+          搴旂敤鍧愭爣
+        </Button>
+      </div>
+    </template>
+    <Spin v-else :spinning="loading">
+      <div v-if="!disabled" class="mb-2 flex gap-2">
+        <Input
+          v-model:value="searchKeyword"
+          allow-clear
+          placeholder="杈撳叆鐪併�佸競銆佸尯鍜屽叿浣撳湴鐐癸紝渚嬪锛氫箤椴佹湪榻愬競鍘傚尯"
+          @press-enter="handleSearch"
+        />
+        <Button :loading="loading" @click="handleSearch">鎼滅储鍦板潃</Button>
+      </div>
+      <div
+        ref="mapContainer"
+        class="h-[360px] w-full overflow-hidden rounded border border-gray-200"
+      ></div>
+      <div
+        class="mt-2 flex flex-wrap justify-between gap-2 text-xs text-gray-500"
+      >
+        <span>
+          {{
+            disabled
+              ? '鎵撳崱浣嶇疆鍙婂厑璁歌寖鍥�'
+              : '鐐瑰嚮鍦板浘閫夋嫨鎵撳崱涓績鐐癸紱钃濊壊鍦嗗湀琛ㄧず鍏佽鎵撳崱鑼冨洿'
+          }}
+        </span>
+        <span v-if="selectedAddress">{{ selectedAddress }}</span>
+      </div>
+    </Spin>
+  </template>
 </template>

--
Gitblit v1.9.3