From 611dd8268e40caa4d97e8954b85b5adaaef99afa Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期一, 26 十月 2026 17:45:58 +0800
Subject: [PATCH] feat(hrm): 考勤规则支持地图定位打卡

---
 src/views/hrm/attendance/rule/modules/form.vue |   81 +++++++++++++++++++++++++++++++++++++++-
 1 files changed, 78 insertions(+), 3 deletions(-)

diff --git a/src/views/hrm/attendance/rule/modules/form.vue b/src/views/hrm/attendance/rule/modules/form.vue
index 60bff96..a0a6eb8 100644
--- a/src/views/hrm/attendance/rule/modules/form.vue
+++ b/src/views/hrm/attendance/rule/modules/form.vue
@@ -13,11 +13,21 @@
 import { $t } from '#/locales';
 
 import { useFormSchema } from '../data';
+import LocationMapPicker from './location-map-picker.vue';
 
 const emit = defineEmits(['success']);
 const formType = ref<FormType>('create');
 
+/** 鍦板浘閫夋嫨鍣ㄦ墍闇�鐨勫畾浣嶇姸鎬侊紝闅忚〃鍗曞�煎彉鍖栧悓姝� */
+const locationRequired = ref(false);
+const longitude = ref<number>();
+const latitude = ref<number>();
+const radiusMeters = ref(10);
+
 const isDetail = computed(() => formType.value === 'detail');
+const modalWidth = computed(() =>
+  locationRequired.value ? 'w-[1000px]' : 'w-[800px]',
+);
 const getTitle = computed(() => {
   if (formType.value === 'detail') {
     return '鏌ョ湅鑰冨嫟瑙勫垯';
@@ -27,6 +37,43 @@
   }
   return '鏂板鑰冨嫟瑙勫垯';
 });
+
+function toNumber(value: unknown): number | undefined {
+  return value === null || value === undefined || value === ''
+    ? undefined
+    : Number(value);
+}
+
+/** 琛ㄥ崟鍊煎彉鍖栨椂鍚屾瀹氫綅鐘舵�侊紝鐢ㄤ簬鎺у埗鍦板浘鍖哄煙涓庡脊绐楀搴� */
+function syncLocationState(
+  values: Partial<HrmAttendanceRuleApi.AttendanceRule>,
+) {
+  locationRequired.value = Boolean(values.locationRequired);
+  longitude.value = toNumber(values.longitude);
+  latitude.value = toNumber(values.latitude);
+  radiusMeters.value = toNumber(values.radiusMeters) ?? 10;
+}
+
+/** 鍦板浘閫夌偣鍚庡洖濉粡绾害锛屽苟灏介噺琛ュ叏鍦扮偣鍚嶇О涓庡湴鍧� */
+async function handleMapSelect(location: {
+  address?: string;
+  latitude: number;
+  locationName?: string;
+  longitude: number;
+}) {
+  const currentValues = await formApi.getValues();
+  longitude.value = location.longitude;
+  latitude.value = location.latitude;
+  await formApi.setFieldValue('longitude', location.longitude);
+  await formApi.setFieldValue('latitude', location.latitude);
+  await formApi.setFieldValue('coordinateSystem', 'WGS84');
+  if (location.address) {
+    await formApi.setFieldValue('locationAddress', location.address);
+  }
+  if (!currentValues.locationName && location.locationName) {
+    await formApi.setFieldValue('locationName', location.locationName);
+  }
+}
 
 const [Form, formApi] = useVbenForm({
   commonConfig: {
@@ -40,6 +87,9 @@
   layout: 'horizontal',
   schema: [],
   showDefaultActions: false,
+  handleValuesChange: (values) => {
+    syncLocationState(values);
+  },
 });
 
 const [Modal, modalApi] = useVbenModal({
@@ -54,6 +104,10 @@
     }
     modalApi.lock();
     const data = (await formApi.getValues()) as HrmAttendanceRuleApi.AttendanceRule;
+    // 鍚庣浠呮帴鍙� WGS84锛屼笖鍚敤瀹氫綅鏃惰姹傚潗鏍囩郴蹇呭~
+    if (data.locationRequired) {
+      data.coordinateSystem = 'WGS84';
+    }
     try {
       if (formType.value === 'update') {
         await updateAttendanceRule(data);
@@ -73,15 +127,26 @@
     }
     const data = modalApi.getData<{ formType: FormType; id?: number }>();
     formType.value = data.formType;
+    locationRequired.value = false;
+    longitude.value = undefined;
+    latitude.value = undefined;
+    radiusMeters.value = 10;
     formApi.setState({ schema: useFormSchema(data.formType) });
     formApi.setDisabled(data.formType === 'detail');
     modalApi.setState({ showConfirmButton: data.formType !== 'detail' });
     if (!data?.id) {
+      await formApi.setValues({
+        coordinateSystem: 'WGS84',
+        locationRequired: false,
+        radiusMeters: 10,
+      });
       return;
     }
     modalApi.lock();
     try {
-      await formApi.setValues(await getAttendanceRule(data.id));
+      const rule = await getAttendanceRule(data.id);
+      await formApi.setValues(rule);
+      syncLocationState(rule);
     } finally {
       modalApi.unlock();
     }
@@ -90,7 +155,17 @@
 </script>
 
 <template>
-  <Modal :title="getTitle" class="w-[800px]">
+  <Modal :title="getTitle" :class="modalWidth">
     <Form class="mx-4" />
+    <section v-if="locationRequired" class="mx-4 mt-2">
+      <div class="mb-2 text-sm font-medium">鍦板浘閫夋嫨鎵撳崱鑼冨洿</div>
+      <LocationMapPicker
+        :disabled="isDetail"
+        :latitude="latitude"
+        :longitude="longitude"
+        :radius-meters="radiusMeters"
+        @select="handleMapSelect"
+      />
+    </section>
   </Modal>
-</template>
\ No newline at end of file
+</template>

--
Gitblit v1.9.3