expo-gaode-map 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/useMap.ts DELETED
@@ -1,1360 +0,0 @@
1
- /*
2
- * @Author : 尚博信_王强 wangqiang03@sunboxsoft.com
3
- * @Date : 2024-07-09 16:16:06
4
- * @LastEditors : 尚博信_王强 wangqiang03@sunboxsoft.com
5
- * @LastEditTime : 2025-11-24 10:01:11
6
- * @FilePath : /jintan-app/hook/useMap.ts
7
- * @Description : 地图的hook
8
- *
9
- * Copyright (c) 2024 by 尚博信_王强, All Rights Reserved.
10
- */
11
- import React, { useEffect, useMemo, useRef, useState } from "react";
12
-
13
- import {initSDK, requestLocationPermission, ExpoGaodeMapModule, ReGeocode, Coordinates, checkLocationPermission, getCurrentLocation, addLocationListener, MapViewRef, CameraPosition, configure} from 'expo-gaode-map'
14
-
15
- import { HapticsUtil } from "@/utils/Haptics";
16
- import { router, useLocalSearchParams } from "expo-router";
17
- import useCameraStore, { Photo } from "@/store/useCameraStore";
18
- import usePermissions from "./usePermissions";
19
- import {
20
- ApprovePass,
21
- ApproveReject,
22
- CancelApproval,
23
- FieldworkClockInReSubmit,
24
- GeneratePresignedUri,
25
- GetApprovalDetail,
26
- GetClockCalendar,
27
- GetClockEx,
28
- GetClockRecord,
29
- GetCurrentClock,
30
- GetListForAppAndApprove,
31
- GetListForAppAndStart,
32
- GetRuleForClock,
33
- PunchClock,
34
- ReSubmitApproval,
35
- SubmitForLeave,
36
- } from "@/api";
37
- import useTeamsStore from "@/store/useTeamsStore";
38
- import useAuthStore from "@/store/useAuthStore";
39
- import RNFetchBlob from "react-native-blob-util";
40
- import {
41
- ClockRecordResponse,
42
- CurrentClockRequest,
43
- GetApprovalDetailResponse,
44
- GetClockExResponse,
45
- GetListForAppAndStartRequest,
46
- GetListForAppAndStartResponse,
47
- JintanIdentityServiceClocksDtosClockApplyInfoDto,
48
- JintanIdentityServiceClocksDtosRulePoints,
49
- PunchClockRequest,
50
- RuleForClockResponse,
51
- SubmitForLeaveRequest,
52
- } from "@/types/teams";
53
- import {
54
- formatTime,
55
- formatTimeClockYMD,
56
- formatTimeYMD,
57
- haversineDistance,
58
- isNotEmpty,
59
- } from "@/utils/Fun";
60
- import { startActivityAsync, ActivityAction } from "expo-intent-launcher";
61
- import AttendancePopupModal from "@/lib/Popup/AttendancePopup";
62
- import AttendanceShiftPopupModal from "@/lib/Popup/AttendanceShiftPopup";
63
- import CoustomLoad from "@/lib/Load/CoustomLoad";
64
- import { ShowNormalPicker, ShowTimePicker } from "@/lib/Picker";
65
- import CommonConstants from "@/hook/constants/CommonConstants";
66
- import { useEffectSkipFirst } from "@/components/useEffectSkipFirst";
67
- import ApplyForDialog from "@/lib/Dialog/ApplyForDialog";
68
- import { ApiResponse } from "apisauce";
69
- import { useCallStore } from "@/store/useCallStore";
70
- import CommonDatePopupModal from "@/lib/Popup/CommonDatePopup";
71
- import dayjs from "dayjs";
72
- import { trackEvent } from "@/utils/Umeng";
73
- import { toast } from "sonner-native";
74
- import ConfirmDialog from "@/lib/Dialog/ConfirmDialog";
75
-
76
-
77
- export default function useMap() {
78
- const { checkLocationPermissions, isLocationPermissions } = usePermissions();
79
- const [initialPosition, setInitialPosition] = useState<CameraPosition | null>(null);
80
- const mapViewRef = useRef<MapViewRef>(null);
81
- const { currentTeam, setClockType, clockType } = useTeamsStore();
82
- const { userInfo, appPositions } = useAuthStore();
83
- const { photo, setCameraType, setPhoto } = useCameraStore();
84
- const { setRefreshMap, refreshMap } = useCallStore();
85
- const { status, applyTime, id } = useLocalSearchParams<{
86
- status?: string;
87
- applyTime?: string;
88
- id?: string;
89
- }>();
90
- const [clockInCoord, setClockInCoord] = useState<
91
- JintanIdentityServiceClocksDtosRulePoints[]
92
- >([]);
93
- //是否在打卡范围内
94
- const [isWithinRange, setIsWithinRange] = useState(false);
95
- //考勤规则的data
96
- const [clockRule, setClockRule] = useState<RuleForClockResponse[]>([]);
97
- //选中的考勤规则
98
- const [clockRuleIndex, setClockRuleIndex] = useState<number>(0);
99
- //考勤班次的data
100
- const [shifts, setShifts] = useState<string[]>([]);
101
- //选中的班次
102
- const [shiftIndex, setShiftIndex] = useState<number>(0);
103
- //打卡范围
104
- const [clockRange, setClockRange] = useState<number>(0);
105
- //补卡的考勤规则选中的,支持多选
106
- const [replaceClockRuleIndex, setReplaceClockRuleIndex] = useState<number[]>(
107
- []
108
- );
109
- //补卡的类型选择
110
- const [replaceTypeSelectItem, setReplaceTypeSelectItem] = useState<{
111
- label: string;
112
- value: string;
113
- }>({ label: "", value: "" });
114
- //补卡时间的选择
115
- const [replaceTime, setReplaceTime] = useState<string | undefined>(applyTime);
116
- //补卡天数
117
- const [replaceDay, setReplaceDay] = useState<number>(1);
118
- //申请原因
119
- const [applyReason, setApplyReason] = useState<string>("");
120
-
121
- //审批页状态:提交还是查阅
122
- const [isApprove, setIsApprove] = useState<string>("102");
123
- //补卡按钮是否可以点击
124
- const [isReplaceEnable, setIsReplaceEnable] = useState(false);
125
- //打卡记录列表
126
- const [clockRecordList, setClockRecordList] = useState<ClockRecordResponse[]>(
127
- []
128
- );
129
- //打卡日历
130
- const [clockDayList, setClockDayList] = useState<string[]>([]);
131
- //补卡请假审批日期筛选
132
- const [applyForDate, setApplyForDate] = useState<string | undefined>();
133
- //补卡请假审批审核状态筛选
134
- const [applyForStatus, setApplyForStatus] = useState<{
135
- label: string;
136
- value: string;
137
- }>({ label: "", value: "" });
138
- //补卡请假审批列表
139
- const [applyForList, setApplyForList] = useState<
140
- JintanIdentityServiceClocksDtosClockApplyInfoDto[]
141
- >([]);
142
- //补卡请假审批的审批流水data
143
- const [applyForPreviewList, setApplyForPreviewList] =
144
- useState<GetApprovalDetailResponse>();
145
- const [totalCount, setTotalCount] = useState(0);
146
- const [hasMoreData, setHasMoreData] = useState(true);
147
- const [isLoadingMore, setIsLoadingMore] = useState(false);
148
- const [currentPage, setCurrentPage] = useState(1);
149
- const pageSize = 20;
150
- //考勤详情页的规则名称
151
- const [clockRuleName, setClockRuleName] = useState<string>(
152
- clockRule[clockRuleIndex]?.ruleName || ""
153
- );
154
- const [mapState, setMapState] = useState(true);
155
- //异常规则的数组
156
- const [abnormalRule, setAbnormalRule] = useState<GetClockExResponse[]>([]);
157
- //是否支持外勤打卡
158
- const [isSupportOutWork, setIsSupportOutWork] = useState(false);
159
-
160
-
161
- let timer = useRef<NodeJS.Timeout | number>(0);
162
-
163
- const [data, setData] = useState<{
164
- location: ReGeocode | null;
165
- isListener: boolean;
166
- isStarted: boolean;
167
- isGps: boolean;
168
- isLocationCacheEnable: boolean;
169
- }>({
170
- location: null,
171
- isListener: false,
172
- isStarted: false,
173
- isGps: false,
174
- isLocationCacheEnable: true,
175
- });
176
-
177
- useEffect(() => {
178
- //先判断是否有定位权限
179
- // checkLocationPermissions()
180
- //获取考勤规则
181
- // getClockInRule()
182
- }, []);
183
-
184
- // useEffect(() => {
185
- // if (appPositions[0] !== '专员') {
186
- // return
187
- // }
188
-
189
-
190
- // const initialize = async () => {
191
- // try {
192
- // initSDK({
193
- // androidKey:'dcee3ae4bd6a53b44fbd3d55b9996cbf',
194
- // iosKey:'b09eaba1eeca575db7a89b9738cb0246'
195
- // })
196
-
197
- // // 2. 检查并请求权限
198
- // const status = await checkLocationPermission();
199
- // if (!status.granted) {
200
- // const result = await requestLocationPermission();
201
- // if (!result.granted) {
202
- // setInitialPosition({ target: { latitude: 39.9, longitude: 116.4 }, zoom: 15 });
203
- // return;
204
- // }
205
- // }
206
-
207
- // configure({
208
- // withReGeocode: true,
209
- // interval: 5000,
210
- // // allowsBackgroundLocationUpdates: true,
211
- // distanceFilter: 10,
212
- // accuracy:3
213
- // });
214
-
215
- // const subscription = addLocationListener((location) => {
216
- // if (location.latitude && location.longitude) {
217
- // setData({ ...data, location: location as ReGeocode });
218
- // } else {
219
- // setData({
220
- // ...data,
221
- // //@ts-ignore
222
- // location: {
223
- // ...location,
224
- // latitude: 0,
225
- // longitude: 0,
226
- // },
227
- // });
228
- // }
229
- // });
230
-
231
- // const loc = await getCurrentLocation();
232
- // setData({ ...data, location: loc as ReGeocode });
233
- // setInitialPosition({
234
- // target: { latitude: loc.latitude, longitude: loc.longitude },
235
- // zoom: 16
236
- // });
237
-
238
- // return () => subscription.remove();
239
-
240
-
241
-
242
- // } catch (error) {
243
- // console.log("error:", error);
244
- // }
245
- // }
246
-
247
- // initialize()
248
-
249
- // // 开始定位
250
- // ExpoGaodeMapModule.start();
251
-
252
- // return () => {
253
- // ExpoGaodeMapModule.stop();
254
- // // listener && listener?.remove();
255
- // timer.current && clearTimeout(timer.current);
256
- // };
257
- // }, [isLocationPermissions, appPositions]);
258
-
259
- useEffect(() => {
260
- if (appPositions[0] !== '专员') {
261
- return
262
- }
263
-
264
- const initialize = async () => {
265
- try {
266
- // 1. 初始化 SDK
267
- // initSDK({
268
- // androidKey:'dcee3ae4bd6a53b44fbd3d55b9996cbf',
269
- // iosKey:'b09eaba1eeca575db7a89b9738cb0246'
270
- // })
271
-
272
- // 2. 检查并请求权限
273
- const status = await checkLocationPermission();
274
- if (!status.granted) {
275
- const result = await requestLocationPermission();
276
- if (!result.granted) {
277
- setInitialPosition({
278
- target: { latitude: 39.9, longitude: 116.4 },
279
- zoom: 15
280
- });
281
- return;
282
- }
283
- }
284
-
285
- // 3. 配置定位
286
- configure({
287
- withReGeocode: true,
288
- interval: 5000,
289
- distanceFilter: 1,
290
- accuracy: 3
291
- });
292
-
293
- // 4. 添加监听
294
- const subscription = addLocationListener((location) => {
295
- if (location.latitude && location.longitude) {
296
- setData({ ...data, location: location as ReGeocode });
297
- } else {
298
- setData({
299
- ...data,
300
- location: {
301
- ...location,
302
- latitude: 0,
303
- longitude: 0,
304
- },
305
- });
306
- }
307
- });
308
-
309
- // 5. 获取当前位置
310
- const loc = await getCurrentLocation();
311
- setData({ ...data, location: loc as ReGeocode });
312
- setInitialPosition({
313
- target: { latitude: loc.latitude, longitude: loc.longitude },
314
- zoom: 16
315
- });
316
-
317
- // ✅ 关键:在所有初始化完成后再 start()
318
- ExpoGaodeMapModule.start();
319
-
320
- return subscription;
321
-
322
- } catch (error) {
323
- console.log("error:", error);
324
- }
325
- }
326
-
327
- // 调用初始化函数并保存清理函数
328
- let cleanupFn: (() => void) | undefined;
329
- initialize().then(result => {
330
- // result 是一个 subscription 对象,不是函数
331
- cleanupFn = result ? () => result.remove() : undefined;
332
- });
333
-
334
- // 清理函数
335
- return () => {
336
- ExpoGaodeMapModule.stop();
337
- if (cleanupFn) {
338
- cleanupFn();
339
- }
340
- timer.current && clearTimeout(timer.current);
341
- };
342
- }, [appPositions]);
343
-
344
-
345
- useEffect(() => {
346
- // setIsApprove(status)
347
- if (appPositions[0] === CommonConstants.role_name_attache) {
348
- //如果是专员
349
- //如果是提交,
350
- if (status === "") {
351
-
352
- getClockInRule();
353
- setIsReplaceEnable(true);
354
- } else if (status === "104" || status === "102" || status === "103") {
355
- applyForPreview();
356
- setIsReplaceEnable(false);
357
- }
358
- } else {
359
- //如果是经理或者组长
360
- //如果是要审批
361
- // applyForPreview();
362
- setIsReplaceEnable(false);
363
- }
364
- }, [status]);
365
-
366
- useEffectSkipFirst(() => {
367
- if (refreshMap) {
368
- getClockRecord();
369
- }
370
- }, [refreshMap]);
371
-
372
- const getClockInRule = async () => {
373
- try {
374
- // 设置加载状态
375
- setMapState(true);
376
-
377
- // 准备参数
378
- let params = {
379
- teamId: currentTeam?.id || "",
380
- };
381
-
382
- // 使用 Promise.all 并行请求数据
383
- const ruleResult = await GetRuleForClock(params);
384
-
385
- if (!ruleResult.ok || !ruleResult.data) {
386
- toast.info("提示", {
387
- description: "获取考勤规则失败,请刷新重新获取",
388
- });
389
- return;
390
- }
391
-
392
- if(ruleResult.data.length === 0){
393
- toast.info("提示", {
394
- description: "未找到考勤规则,请先在金探后台配置",
395
- });
396
- return;
397
- }
398
-
399
- // 批量更新状态,减少重渲染次数
400
- const firstRule = ruleResult.data[0];
401
-
402
- // 一次性设置所有状态
403
- setClockRule(ruleResult.data);
404
- if (firstRule.rulePoints) {
405
- setClockInCoord(firstRule.rulePoints);
406
- }
407
- if (firstRule.frequency) {
408
- setShifts(firstRule.frequency);
409
- }
410
- if (firstRule.clockRange) {
411
- setClockRange(firstRule.clockRange);
412
- }
413
- if (firstRule.isFieldworkClockIn) {
414
- setIsSupportOutWork(firstRule.isFieldworkClockIn)
415
- }
416
-
417
- // 获取打卡状态
418
- if (firstRule.id) {
419
- const clockParams = {
420
- userId: userInfo?.id,
421
- ruleId: firstRule.id,
422
- clockSeq: 1,
423
- };
424
-
425
- const clockResult = await GetCurrentClock(clockParams);
426
- if (clockResult.ok && clockResult.data) {
427
- setClockType(clockResult.data);
428
- }
429
- }
430
- } catch (error) {
431
- console.log("获取考勤规则失败", error);
432
- throw error; // 重要:确保错误被传播出去
433
- }
434
- // 无论成功失败都关闭加载状态
435
- setMapState(false);
436
- };
437
-
438
- const getClockInShow = async (params: CurrentClockRequest) => {
439
- // CoustomLoad.show('正在获取打卡状态...')
440
- try {
441
- const result = await GetCurrentClock(params);
442
- if (result.ok && result.data) {
443
- setClockType(result.data);
444
- }
445
- } catch (error) {
446
- console.log(error);
447
- }
448
- CoustomLoad.hide();
449
- };
450
- //item.lat || 0, item.lon || 0, data.location?.latitude || 0, data.location?.longitude || 0
451
-
452
- useEffect(() => {
453
- if (clockInCoord && clockInCoord.length > 0 && data.location) {
454
- // 使用 requestAnimationFrame 优化频繁计算
455
- requestAnimationFrame(() => {
456
- const index = clockInCoord.some((item) => {
457
- return (
458
- haversineDistance(
459
- { latitude: item.lat || 0, longitude: item.lon || 0 },
460
- {
461
- latitude: data.location?.latitude || 0,
462
- longitude: data.location?.longitude || 0,
463
- }
464
- ) <= clockRange
465
- );
466
- });
467
- setIsWithinRange(index);
468
- });
469
- }
470
- }, [
471
- clockInCoord,
472
- data.location?.latitude,
473
- data.location?.longitude,
474
- clockRange,
475
- ]);
476
-
477
- // 计算两个地理坐标之间的距离(单位:米)
478
- const getDistance = (
479
- lat1: number,
480
- lon1: number,
481
- lat2: number,
482
- lon2: number
483
- ) => {
484
- const R = 6371000; // 地球半径(米)
485
- const φ1 = (lat1 * Math.PI) / 180;
486
- const φ2 = (lat2 * Math.PI) / 180;
487
- const Δφ = ((lat2 - lat1) * Math.PI) / 180;
488
- const Δλ = ((lon2 - lon1) * Math.PI) / 180;
489
-
490
- const a =
491
- Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
492
- Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
493
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
494
-
495
- return R * c;
496
- };
497
-
498
- //拍照
499
- const takePhoto = async () => {
500
- setCameraType("CLOCK");
501
- // 在这里清空照片,确保每次拍照都是新的
502
-
503
- HapticsUtil.triggerHaptic();
504
- // //打开相机
505
- router.push({
506
- pathname: "/camera-modal-clock",
507
- params: {
508
- type: "camera",
509
- location: JSON.stringify(data.location),
510
- },
511
- });
512
- };
513
-
514
- //打卡
515
- const ClockIn = async () => {
516
- trackEvent("click_clock_in", "考勤打卡", {
517
- page: "考勤打卡页",
518
- });
519
- //是否在打卡范围内
520
- if (!isWithinRange) {
521
- if (!isSupportOutWork) {
522
- toast.info("打卡提示", {
523
- description: "当前位置不在考勤范围无法打卡",
524
- });
525
- return;
526
- }
527
- }
528
-
529
- console.log("photo:", photo);
530
-
531
- //必须要有照片
532
- if (!isNotEmpty(photo.content)) {
533
- toast.info("打卡提示", {
534
- description: "请先上传打卡照片",
535
- });
536
-
537
- return;
538
- }
539
-
540
- if (clockType === 1001) {
541
- CoustomLoad.show("上班打卡中...");
542
- } else {
543
- CoustomLoad.show("下班打卡中...");
544
- }
545
-
546
- try {
547
- let params: PunchClockRequest = {
548
- clockLat: data.location?.latitude || 0,
549
- clockLon: data.location?.longitude || 0,
550
- clockPositionName: data.location?.address || "",
551
- clockTime: formatTime(new Date()),
552
- teamId: currentTeam?.id,
553
- userId: userInfo?.id,
554
- clockRuleId: clockRule[clockRuleIndex]?.id || "",
555
- clockSeq: shiftIndex + 1,
556
- hasImages: photo ? true : false,
557
- };
558
- //先对图片进行检查,如果没有图片直接提交,如果有图片先上传再提交
559
- if (photo) {
560
- const image_res = await generatePresignedUri([photo]);
561
- if (image_res && image_res.length > 0) {
562
- params.clockPic = image_res[0];
563
- }
564
- }
565
- const result = await PunchClock(params);
566
- if (result.ok) {
567
- CoustomLoad.hide();
568
- toast.success(`${clockType === 1001 ? "上班" : "下班"}打卡成功`, {
569
- onAutoClose: () => {
570
- //如果有拍照,就清空拍照
571
- setPhoto({} as Photo);
572
- getClockInShow({
573
- userId: userInfo?.id,
574
- ruleId: clockRule[clockRuleIndex]?.id,
575
- clockSeq: shiftIndex + 1,
576
- });
577
- setRefreshMap(true);
578
- },
579
- });
580
- } else {
581
- }
582
- } catch (error) {
583
- console.log(error);
584
- CoustomLoad.hide();
585
- }
586
- CoustomLoad.hide();
587
- };
588
-
589
- //移动到当前位置
590
- const moveToCurrentLocation = async() => {
591
- HapticsUtil.triggerHaptic();
592
-
593
- try {
594
- // ✅ 优先使用 data.location(实时更新的位置)
595
- let location = data.location;
596
-
597
- // 如果 data.location 不存在或无效,才重新获取
598
- if (!location || !location.latitude || !location.longitude) {
599
- location = await getCurrentLocation() ;
600
- }
601
-
602
- // 确保有有效的坐标
603
- if (location && location.latitude && location.longitude) {
604
- if (mapViewRef.current) {
605
- await mapViewRef.current.moveCamera({
606
- target: {
607
- latitude: location.latitude,
608
- longitude: location.longitude
609
- },
610
- zoom: 16,
611
- }, 300);
612
- }
613
- } else {
614
- toast.info("提示", {
615
- description: "无法获取当前位置,请稍后再试"
616
- });
617
- }
618
-
619
- } catch (error) {
620
- console.log("moveToCurrentLocation error:", error);
621
- toast.info("提示", {
622
- description: "定位失败,请检查定位权限"
623
- });
624
- }
625
- };
626
-
627
- //生成用于上传前面的url
628
- const generatePresignedUri = async (params: Photo[]) => {
629
- try {
630
- const promise = params.map(async (item) => {
631
- const result = await GeneratePresignedUri("CLOCK");
632
- if (result.ok) {
633
- const res = await uploadPhoto(result.data?.url, item.content);
634
- //fileName的文件名结尾都换成.png
635
- let fileName = item.fileName?.replace(/\.\w+$/, ".png");
636
- return {
637
- fullUrl: result.data?.fullUrl,
638
- fileName: fileName,
639
- };
640
- }
641
- });
642
- const res = await Promise.all(promise);
643
-
644
- return res;
645
- // return res.filter(item => item)
646
- } catch (error) {
647
- console.log(error);
648
- }
649
- };
650
-
651
- //上传图片
652
- const uploadPhoto = async (uri?: string, realPath?: string) => {
653
- if (!uri || !realPath) return false;
654
- RNFetchBlob.fetch(
655
- "PUT",
656
- uri,
657
- { "Content-Type": "application/octet-stream" },
658
- RNFetchBlob.wrap(decodeURIComponent(realPath))
659
- )
660
- .uploadProgress({ count: 10, interval: -1 }, (received, total) => {
661
- console.log(`Received: ${received}, total: ${total}`);
662
- })
663
- .progress({ count: 10, interval: -1 }, (received, total) => {
664
- console.log(`Received: ${received}, total: ${total}`);
665
- })
666
- .then((res) => {
667
- console.log("OSS上传成功", JSON.stringify(res));
668
- return true;
669
- })
670
- .catch((err) => {
671
- console.log("OSS上传失败", JSON.stringify(err));
672
- return false;
673
- });
674
- };
675
-
676
- //选择考勤规则
677
- const chooseClockRule = () => {
678
- //判断是否有考勤规则
679
- if (!clockRule || clockRule.length === 0) {
680
- toast.info("温馨提示", {
681
- description: "未找到考勤规则,请先在金探后台配置",
682
- });
683
-
684
- return;
685
- }
686
-
687
- HapticsUtil.triggerHaptic();
688
- AttendancePopupModal.show({
689
- data: clockRule,
690
- chooseRule: clockRuleIndex,
691
- selectCallBack: () => {
692
- //@ts-ignore
693
- // mapViewRef?.current?.moveCamera({
694
- // zoom:10,
695
- // },100)
696
- },
697
- callBack: (value) => {
698
- //如果考勤规则为空
699
- if (!clockRule[value]) return;
700
-
701
- setClockRuleIndex(value);
702
- //进行考勤班次的赋值
703
- setShifts(clockRule[value]?.frequency || []);
704
- setShiftIndex(0);
705
- //进行打卡点的赋值
706
- setClockInCoord(clockRule[value]?.rulePoints || []);
707
- //进行打卡范围的赋值
708
- setClockRange(clockRule[value]?.clockRange || 0);
709
- getClockInShow({
710
- userId: userInfo?.id,
711
- ruleId: clockRule[value]?.id,
712
- clockSeq: 1,
713
- });
714
- setIsSupportOutWork(clockRule[value]?.isFieldworkClockIn || false);
715
- //@ts-ignore
716
- // mapViewRef?.current?.moveCamera(
717
- // {
718
- // zoom: 16,
719
- // },
720
- // 100
721
- // );
722
- moveToCurrentLocation()
723
- },
724
- });
725
- };
726
-
727
- //选择班次
728
- const chooseShift = () => {
729
- //先判断是否选择了考勤规则
730
- if (!clockRule || clockRule.length === 0) {
731
- toast.info("温馨提示", {
732
- description: "未找到考勤规则,请先在金探后台配置",
733
- });
734
-
735
- return;
736
- }
737
-
738
- HapticsUtil.triggerHaptic();
739
- AttendanceShiftPopupModal.show({
740
- data: shifts,
741
- chooseShift: shiftIndex,
742
- callBack: (value) => {
743
- //如果班次为空
744
- if (!shifts[value]) return;
745
-
746
- setShiftIndex(value);
747
- getClockInShow({
748
- userId: userInfo?.id,
749
- ruleId: clockRule[clockRuleIndex]?.id,
750
- clockSeq: value + 1,
751
- });
752
- },
753
- });
754
- };
755
-
756
- //补卡申请选择考勤规则
757
- const applyForClockRule = () => {
758
- //判断是否有考勤规则
759
- if (!clockRule || clockRule.length === 0) {
760
- toast.info("温馨提示", {
761
- description: "未找到考勤规则,请先在金探后台配置",
762
- });
763
-
764
- return;
765
- }
766
-
767
- HapticsUtil.triggerHaptic();
768
- AttendancePopupModal.show({
769
- data: clockRule,
770
- chooseRule: clockRuleIndex,
771
- selectCallBack: () => { },
772
- callBack: (value) => {
773
- //如果考勤规则为空
774
- if (!clockRule[value]) return;
775
- setClockRuleIndex(value);
776
- setClockRuleName(clockRule[value]?.ruleName || "");
777
- },
778
- });
779
- };
780
-
781
- //补卡申请类型选择
782
- const applyForType = () => {
783
- HapticsUtil.triggerHaptic();
784
- ShowNormalPicker({
785
- title: "请选择申请类型",
786
- array: CommonConstants.applyType,
787
- selectItem: replaceTypeSelectItem,
788
- }).then((value) => {
789
- if (!value) return;
790
- setReplaceTypeSelectItem(value as { label: string; value: string });
791
- });
792
- };
793
-
794
- //补卡时间的选择
795
- const applyForTypeTime = () => {
796
- HapticsUtil.triggerHaptic();
797
-
798
- let chooseTime = {
799
- startId: "",
800
- endId: "",
801
- };
802
-
803
- if (replaceTime?.includes("-")) {
804
- const arr = replaceTime.split("-");
805
- chooseTime = {
806
- startId: arr[0].replaceAll("/", "-"),
807
- endId: arr[1].replaceAll("/", "-"),
808
- };
809
- } else {
810
- chooseTime = {
811
- startId: replaceTime?.replaceAll("/", "-") ?? "",
812
- endId: replaceTime?.replaceAll("/", "-") ?? "",
813
- };
814
- }
815
-
816
- CommonDatePopupModal.show({
817
- title: applyforTime(),
818
- chooseProject: chooseTime,
819
- maxDateIsToday: true,
820
- callBack: (value) => {
821
- if (!value) return;
822
- setReplaceTime(value);
823
- },
824
- normalCallBack: (value) => {
825
- if (!value) return;
826
- let firstDay = value[0].startId;
827
- let lastDay = value[0].endId;
828
- if (lastDay) {
829
- const day = dayjs(lastDay).diff(dayjs(firstDay), "day");
830
- setReplaceDay(day + 1);
831
- } else {
832
- setReplaceDay(1);
833
- }
834
- },
835
- });
836
- };
837
-
838
- //申请时间的不同展示
839
- const applyforTime = () => {
840
- if (replaceTypeSelectItem?.value == "") {
841
- return "请选择申请时间";
842
- } else if (replaceTypeSelectItem?.value === "101") {
843
- return "请选择补卡时间";
844
- } else {
845
- return "请选择请假时间";
846
- }
847
- };
848
-
849
- //不同时间名称的显示
850
- const applyforTimeName = () => {
851
- if (replaceTypeSelectItem?.value == "") {
852
- return "申请";
853
- } else if (replaceTypeSelectItem?.value === "101") {
854
- return "补卡";
855
- } else {
856
- return "请假";
857
- }
858
- };
859
-
860
- useEffectSkipFirst(() => {
861
- if (applyForPreviewList) {
862
- setApplyReason(applyForPreviewList?.applyReson || "");
863
- setReplaceTypeSelectItem({
864
- label: applyForPreviewList?.applyTypeStr || "",
865
- value: "",
866
- });
867
- if (applyForPreviewList?.applyTimeEnd) {
868
- setReplaceTime(
869
- formatTimeClockYMD(applyForPreviewList?.applyTime) +
870
- "-" +
871
- formatTimeClockYMD(applyForPreviewList?.applyTimeEnd)
872
- );
873
- setReplaceDay(
874
- dayjs(applyForPreviewList?.applyTimeEnd).diff(
875
- dayjs(applyForPreviewList?.applyTime),
876
- "day"
877
- ) + 1
878
- );
879
- } else {
880
- setReplaceTime(
881
- formatTimeClockYMD(applyForPreviewList?.applyTime) || ""
882
- );
883
- setReplaceDay(1);
884
- }
885
-
886
- setClockRuleName(applyForPreviewList?.clockRuleName || "");
887
- }
888
- }, [applyForPreviewList]);
889
-
890
- //补卡预览页数据获取
891
- const applyForPreview = async () => {
892
- CoustomLoad.show("加载中...");
893
-
894
- try {
895
- if (id) {
896
- const result = await GetApprovalDetail(id);
897
- if (result.ok && result.data) {
898
- setApplyForPreviewList(result.data);
899
- }
900
- }
901
- } catch (error) {
902
- console.log("applyForPreview error", error);
903
- }
904
- CoustomLoad.hide();
905
- };
906
-
907
- //获取当前登录人某一天的打卡记录
908
- const getClockRecord = async (dtDay?: string) => {
909
- CoustomLoad.show("加载中...");
910
-
911
- try {
912
- const result = await GetClockRecord({
913
- dtDay: dtDay ?? formatTimeYMD(new Date()),
914
- });
915
- if (result.ok && result.data) {
916
- setClockRecordList(result.data);
917
- } else {
918
- setClockRecordList([]);
919
- }
920
- } catch (error) {
921
- console.log("getClockRecord error", error);
922
- }
923
- CoustomLoad.hide();
924
- };
925
-
926
- //获取打卡日历
927
- const getClockCalendar = async (
928
- year: number,
929
- month: number
930
- ) => {
931
- let yearNow = year ?? new Date().getFullYear();
932
- let monthNow = month ?? new Date().getMonth() + 1;
933
- CoustomLoad.show("加载中...");
934
- const result = await GetClockCalendar({ year:yearNow, month:monthNow });
935
- if (result.ok && result.data) {
936
- //对打卡日历进行格式化
937
- const newData = result.data.map((item: string) => {
938
- return formatTimeYMD(item);
939
- });
940
- setClockDayList(newData);
941
- } else {
942
- setClockDayList([]);
943
- }
944
- CoustomLoad.hide();
945
- };
946
-
947
- //补卡请假审批日期筛选
948
- const applyForListTime = () => {
949
- HapticsUtil.triggerHaptic();
950
- ShowTimePicker({
951
- title: "请选择申请时间",
952
- pattern: "yyyy-MM-dd",
953
- selectDate: applyForDate,
954
- }).then((value) => {
955
- if (!value) return;
956
- setApplyForDate(value);
957
- });
958
- };
959
-
960
- //补卡请假审批审核状态筛选
961
- const applyForApprove = () => {
962
- HapticsUtil.triggerHaptic();
963
- ShowNormalPicker({
964
- title: "请选择审核状态",
965
- array: CommonConstants.approveStatus,
966
- selectItem: applyForStatus,
967
- }).then((value) => {
968
- if (!value) return;
969
- setApplyForStatus(value as { label: string; value: string });
970
- });
971
- };
972
-
973
- //补卡申请提交
974
- const applyForSubmit = async () => {
975
- //检查选项是否填写完整
976
- //1.考勤规则是否已选
977
-
978
- if (!isNotEmpty(clockRuleName)) {
979
- toast.info("补卡提示", {
980
- description: "请选择考勤规则",
981
- });
982
-
983
- return;
984
- }
985
-
986
- //2.补卡类型是否已选
987
- if (!replaceTypeSelectItem?.value) {
988
- toast.info("补卡提示", {
989
- description: "请选择申请类型",
990
- });
991
-
992
- return;
993
- }
994
- //3.补卡时间是否已选
995
- if (!replaceTime) {
996
- toast.info("补卡提示", {
997
- description: applyforTime(),
998
- });
999
-
1000
- return;
1001
- }
1002
-
1003
- //4.补卡原因是否已填
1004
- if (!applyReason) {
1005
- toast.info("补卡提示", {
1006
- description: `请填写${applyforTimeName()}原因`,
1007
- });
1008
-
1009
- return;
1010
- }
1011
-
1012
- HapticsUtil.triggerHaptic();
1013
- CoustomLoad.show("正在提交申请...");
1014
- try {
1015
- let params: SubmitForLeaveRequest = {
1016
- applyReson: applyReason,
1017
- applyTime: applyTime,
1018
- applyType: Number(replaceTypeSelectItem?.value),
1019
- clockRuleId: clockRule[clockRuleIndex].id,
1020
- teamId: currentTeam?.id || "",
1021
- };
1022
-
1023
- //对replaceTime进行处理,他的格式是 2022/11/11-2022/11/11 或者是 2022/11/11
1024
- if (replaceTime) {
1025
- //处理两个时间段的情况,默认的格式是 2024-08-01
1026
- if (replaceTime.includes("-")) {
1027
- const [start, end] = replaceTime.split("-");
1028
- params.applyTime = start;
1029
- params.applyTimeEnd = end;
1030
- } else {
1031
- params.applyTime = replaceTime;
1032
- }
1033
- }
1034
-
1035
- const result = await SubmitForLeave(params);
1036
-
1037
- if (result.ok) {
1038
- toast.success("补卡提示", {
1039
- description: "提交成功",
1040
- onAutoClose: () => {
1041
- router.back();
1042
- // navigation.goBack();
1043
- },
1044
- });
1045
- }
1046
- } catch (error) {
1047
- console.log("applyForSubmit error", error);
1048
- }
1049
- CoustomLoad.hide();
1050
- };
1051
-
1052
- //外勤驳回后的重新申请
1053
- const applyForResubmit = async (clockRecordId: string) => {
1054
- CoustomLoad.show("正在重新提交...");
1055
- try {
1056
- // let params: SubmitForLeaveRequest = {
1057
- // applyReson: '外勤打卡',
1058
- // applyTime: clockTime,
1059
- // applyType: 110,
1060
- // clockRuleId: clockRuleId,
1061
- // clockRecordId: clockRecordId,
1062
- // teamId: currentTeam?.id || "",
1063
- // };
1064
- // console.log('params',params)
1065
- const result = await FieldworkClockInReSubmit(clockRecordId);
1066
-
1067
- if (result.ok) {
1068
- toast.success("提示", {
1069
- description: "提交成功",
1070
- onAutoClose: () => {
1071
- //刷新当前页
1072
- getApplyForListRefresh();
1073
- },
1074
- });
1075
- }
1076
- } catch (error) {
1077
- console.log("applyForResubmit error", error);
1078
- }
1079
- CoustomLoad.hide();
1080
- };
1081
-
1082
- //补卡请假审批列表下拉刷新
1083
- const getApplyForListRefresh = async () => {
1084
- CoustomLoad.show("加载中...");
1085
- setCurrentPage(1);
1086
- getApplyForList(1);
1087
- };
1088
-
1089
- useEffectSkipFirst(() => {
1090
- getApplyForListRefresh();
1091
- }, [applyForStatus?.value, applyForDate]);
1092
-
1093
- //补卡请假审批列表(发起人)
1094
- const getApplyForList = async (page: number = 1) => {
1095
- try {
1096
- let params: GetListForAppAndStartRequest = {
1097
- pageIndex: page,
1098
- pageSize: 20,
1099
- };
1100
- if (Number(applyForStatus?.value)) {
1101
- params.applyStatus = Number(applyForStatus?.value);
1102
- }
1103
- if (applyForDate) {
1104
- params.applyTime = applyForDate;
1105
- }
1106
- let result: ApiResponse<GetListForAppAndStartResponse>;
1107
- if (appPositions[0] === CommonConstants.role_name_attache) {
1108
- result = await GetListForAppAndStart(params);
1109
- } else {
1110
- result = await GetListForAppAndApprove(params);
1111
- }
1112
-
1113
- const newData = result.data?.items;
1114
- const totalCount = result.data?.totalCount;
1115
- if (result.ok && newData) {
1116
- setHasMoreData(false);
1117
- setTotalCount(totalCount || 0);
1118
- if (newData && newData?.length < pageSize) {
1119
- setIsLoadingMore(true);
1120
- setHasMoreData(true);
1121
- } else {
1122
- setIsLoadingMore(false);
1123
- }
1124
- if (page === 1) {
1125
- setApplyForList(newData);
1126
- } else {
1127
- setApplyForList((prevData) => [
1128
- ...(prevData || []),
1129
- ...(newData || []),
1130
- ]);
1131
- }
1132
- } else {
1133
- setIsLoadingMore(true);
1134
- setHasMoreData(false);
1135
- }
1136
- } catch (error) {
1137
- console.log("getApplyForList error", error);
1138
- }
1139
- CoustomLoad.hide();
1140
- };
1141
-
1142
- //补卡请假审批列表(审批人)
1143
- const getApplyForListForApp = async (page: number = 1) => { };
1144
-
1145
- //补卡请假审批列表加载更多
1146
- const fetchMoreData = async () => {
1147
- let page = currentPage + 1;
1148
- setCurrentPage(page);
1149
- getApplyForList(page);
1150
- };
1151
-
1152
- //获取补卡请假审批的流水
1153
- const getApplyForWater = async () => { };
1154
-
1155
- //申请驳回
1156
- const applyForReject = async (id: string) => {
1157
- HapticsUtil.triggerHaptic();
1158
- ApplyForDialog.show({
1159
- title: "请填写驳回原因",
1160
- callBack(value) {
1161
- if (!value) return;
1162
- approveReject(id, value);
1163
- },
1164
- });
1165
- };
1166
-
1167
- //申请撤销(换成了重新申请)
1168
- const applyForCancel = async (id: string) => {
1169
- HapticsUtil.triggerHaptic();
1170
- ConfirmDialog.show({
1171
- title: "提示",
1172
- message: "确定撤销申请?",
1173
- onConfirm: () => {
1174
- if (id) approveResubmit(id);
1175
- },
1176
- });
1177
- };
1178
-
1179
- //申请同意
1180
- const applyForAgree = async (id: string) => {
1181
- HapticsUtil.triggerHaptic();
1182
- ConfirmDialog.show({
1183
- title: "提示",
1184
- message: "确定同意申请?",
1185
- onConfirm: () => {
1186
- if (id) approvePass(id);
1187
- },
1188
- });
1189
- };
1190
-
1191
- //审批通过
1192
- const approvePass = async (id: string) => {
1193
- CoustomLoad.show("正在提交...");
1194
- try {
1195
- const result = await ApprovePass(id);
1196
- if (result.ok) {
1197
- toast.success("审核通过", {
1198
- onAutoClose: () => {
1199
- //刷新列表
1200
- getApplyForListRefresh();
1201
- //刷新界面
1202
- applyForPreview()
1203
- },
1204
- });
1205
- }
1206
- } catch (error) {
1207
- console.log("approvePass error", error);
1208
- }
1209
- CoustomLoad.hide();
1210
- };
1211
-
1212
- //审核驳回
1213
- const approveReject = async (id: string, reason?: string) => {
1214
- CoustomLoad.show("正在驳回...");
1215
- try {
1216
- let params = {
1217
- id: id,
1218
- content: reason,
1219
- };
1220
- const result = await ApproveReject(params);
1221
- if (result.ok) {
1222
- toast.success("驳回成功", {
1223
- onAutoClose: () => {
1224
- //刷新列表
1225
- getApplyForListRefresh();
1226
- //刷新界面
1227
- applyForPreview();
1228
- },
1229
- });
1230
- }
1231
- } catch (error) {
1232
- console.log("approveReject error", error);
1233
- }
1234
- CoustomLoad.hide();
1235
- };
1236
-
1237
- //审批撤回
1238
- const approveWithdraw = async (id: string) => {
1239
- CoustomLoad.show("正在撤回...");
1240
- try {
1241
- const result = await CancelApproval(id);
1242
- if (result.ok) {
1243
- } else {
1244
- }
1245
- } catch (error) {
1246
- console.log("approveWithdraw error", error);
1247
- }
1248
- CoustomLoad.hide();
1249
- };
1250
-
1251
- //审批重新提交
1252
- const approveResubmit = async (id: string) => {
1253
- CoustomLoad.show("正在重新提交...");
1254
- try {
1255
- const result = await ReSubmitApproval(id);
1256
- if (result.ok) {
1257
- toast.success("重新提交成功", {
1258
- onAutoClose: () => {
1259
- //刷新列表
1260
- getApplyForListRefresh();
1261
- },
1262
- });
1263
- }
1264
- } catch (error) {
1265
- console.log("approveResubmit error", error);
1266
- }
1267
- CoustomLoad.hide();
1268
- };
1269
-
1270
- // 获取当前登录人异常规则判定
1271
- const getClockEx = async (dtDay?: string) => {
1272
- try {
1273
- const result = await GetClockEx({
1274
- dtDay: dtDay ?? formatTimeYMD(new Date()),
1275
- });
1276
- if (result.data) {
1277
- setAbnormalRule(result.data);
1278
- if (result.data.length > 0) {
1279
- for (let index = 0; index < result.data.length; index++) {
1280
- const item = result.data[index];
1281
- if (item.isException) {
1282
- setClockRuleIndex(index);
1283
- setClockRuleName(item.ruleName || "");
1284
- break; // 找到第一个异常规则后结束循环
1285
- }
1286
- }
1287
- }
1288
- }
1289
- } catch (error) {
1290
- console.log("getClockEx error", error);
1291
- }
1292
- };
1293
-
1294
- return {
1295
- mapViewRef,
1296
- data,
1297
- takePhoto,
1298
- ClockIn,
1299
- moveToCurrentLocation,
1300
- generatePresignedUri,
1301
- clockInCoord,
1302
- isWithinRange,
1303
- chooseClockRule,
1304
- clockRuleIndex,
1305
- clockRule,
1306
- chooseShift,
1307
- shifts,
1308
- shiftIndex,
1309
- clockRange,
1310
- getClockInRule,
1311
- getDistance,
1312
- setIsWithinRange,
1313
- applyForClockRule,
1314
- replaceClockRuleIndex,
1315
- setReplaceClockRuleIndex,
1316
- applyForType,
1317
- replaceTypeSelectItem,
1318
- applyForTypeTime,
1319
- replaceTime,
1320
- isReplaceEnable,
1321
- isApprove,
1322
- setApplyReason,
1323
- applyReason,
1324
- applyForPreview,
1325
- getClockRecord,
1326
- clockRecordList,
1327
- getClockCalendar,
1328
- clockDayList,
1329
- applyForListTime,
1330
- applyForDate,
1331
- applyForApprove,
1332
- applyForStatus,
1333
- getApplyForList,
1334
- applyForList,
1335
- hasMoreData,
1336
- isLoadingMore,
1337
- fetchMoreData,
1338
- getApplyForWater,
1339
- applyForPreviewList,
1340
- applyForReject,
1341
- applyForCancel,
1342
- applyForAgree,
1343
- applyForSubmit,
1344
- applyforTime,
1345
- getApplyForListForApp,
1346
- getApplyForListRefresh,
1347
- approvePass,
1348
- clockRuleName,
1349
- approveResubmit,
1350
- getClockEx,
1351
- abnormalRule,
1352
- applyforTimeName,
1353
- replaceDay,
1354
- mapState,
1355
- setMapState,
1356
- isSupportOutWork,
1357
- applyForResubmit,
1358
- initialPosition
1359
- };
1360
- }