Vite出勤系統

最近開是在做EIP 系統,因此出勤系統就是要思考邏輯性的呈現

出勤打卡邏輯

打開打卡頁面(獲取出勤設定定位地圖與定位座標)『如果已經在打卡範圍』,出現google Map 地圖 =>獲取當日過濾後的上班/外勤上班打卡列表(包含遲到:『如果已經打過卡了』或獲取當日過濾後的下班/外勤下班打卡列表(包含早退:『如果已經打過卡了』,『點擊』下班打卡或外勤下班打卡:alert 已經打過卡了),如果沒有打過上班卡/出勤上班卡 =>得到 『上班正常』或『外勤上班正常』或『上班遲到』或『外勤上班遲到』

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<template>
<ul class="set_work_check">
<li v-for="(item,id) in setCheckModesMainMenu" :key="id">
<router-link
:to="item.href"
:class="{activePage: item.title===pageName}"> {{item.title}}</router-link>
</li>
</ul>
<div class="setInfo">
<div>{{office}}</div>
<div>上班時間:{{flexibleTime}}</div>
<div>距離:{{distance}}公尺</div>
</div>
<ul class="work_check">
<li>
<div class="work_check_item">
<a @click="checkOnWork" v-show="showOnWorkCheck" >上班 </a>
<div class="already_check" v-for="(item,workCheck_Id) in currentDayonWorkLists" :key="workCheck_Id">
<a :class="{activeCheck: item.onWorkStatus,isLate:item.title==='上班遲到'}" v-show="item.onWorkStatus">上班</a>
<div class="check_info" v-show="item.onWorkStatus">
<div class="text" :class="{isFoul:isLate,isQualified:!isLate}">{{ item.title }}</div>
<div class="text">{{onWorkCheckTime}}</div>
<div class="text">經度:{{item.lat}}</div>
<div class="text">緯度:{{item.lng}}</div>
</div>
</div>
</div>
<div class="work_check_item">
<a v-show="showOffWorkCheck" @click="checkOffWork">下班</a>
<div class="already_check" v-for="(item,workCheck_Id) in currentDayoffWorkLists" :key="workCheck_Id">
<a :class="{activeCheck: item.offWorkStatus,isLate:item.title==='下班早退'}" v-show="item.offWorkStatus">下班</a>
<div class="check_info" v-show="item.offWorkStatus">
<div class="text" :class="{isFoul:isLeaveEarly,isQualified:!isLeaveEarly}">{{ item.title }}</div>
<div class="text">{{offWorkCheckTime}}</div>
<div class="text">經度:{{item.lat}}</div>
<div class="text">緯度:{{item.lng}}</div>
</div>
</div>
</div>
</li>
<li>
<div class="work_check_item">
<a v-show="showOnBusinessWorkCheck" @click="checkOnBusinessWork">外勤上班</a>
<div class="already_check" v-for="(item,workCheck_Id) in currentDayonBusinessworkLists" :key="workCheck_Id">
<a :class="{activeCheck: item.onWorkStatus,isLate:item.title==='外勤上班遲到'}" v-show="item.onWorkStatus">外勤上班</a>
<div class="check_info" v-show="item.onWorkStatus">
<div class="text" :class="{isFoul:isLate,isQualified:!isLate}">{{ item.title }}</div>
<div class="text">{{onBusinessWorkTime}}</div>
<div class="text">經度:{{item.lat}}</div>
<div class="text">緯度:{{item.lng}}</div>
</div>
</div>
</div>
<div class="work_check_item">
<a v-show="showOffBusinessWorkCheck" @click="checkOffBusinessWork">外勤下班</a>
<div class="already_check" v-for="(item,workCheck_Id) in currentDayoffBusinessworkLists" :key="workCheck_Id">
<a :class="{activeCheck: item.offWorkStatus,isLate:item.title==='外勤下班早退'}" v-show="item.offWorkStatus">外勤下班</a>
<div class="check_info" v-show="item.offWorkStatus">
<div class="text" :class="{isFoul:isLeaveEarly,isQualified:!isLeaveEarly}">{{ item.title }}</div>
<div class="text">{{offBusinessWorkTime}}</div>
<div class="text">經度:{{item.lat}}</div>
<div class="text">緯度:{{item.lng}}</div>
</div>
</div>
</div>
</li>
</ul>

<div v-show="showMap">
<iframe :src="setMap" width="100%" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</template>

Route

1
2
3
4
5
<route lang="yaml">
meta:
layout: LayoutBack
requireAutd: true
</route>

關於定位與定位點的圓周距離

Geolocation API 這是瀏覽器內建的 API,getCurrentPosition() 方法傳回裝置的目前位置。
js定位與範圍內 & Vue語法糖寫法 定位與使用套件

Math.PI 3.14
參考資料
算出圓周
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
const radius =(p: number) =>{
console.log('算出圓周',p * Math.PI / 180.0)//2.1058120410500933
return p * Math.PI / 180.0;
}
// 計算距離,參數分別爲第一點的緯度,經度;第二點的緯度,經度
const getDistance =(lat1: number,lng1: number,lat2: number,lng2: number) =>{
const radLat1 = radius(lat1);
const radLat2 = radius(lat2);
// 緯度距離
const a = radLat1 - radLat2;
const b = radius(lng1) - radius(lng2);
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));

s = s * 6378.137 ;// 地球半徑;
s = Math.round(s * 10000) / 10000; //單位為 km
return s;
}
// 定位
const showMap=ref<boolean>(false);
const getMap=ref<string>('');
const lat = ref<string>('');
const lng = ref<string>('');
const latNow = ref<number| null>(null);
const lngNow = ref<number | null>(null);
// 圓周距離
const distance =ref<number>(500);
const location =() =>{
if ("geolocation" in navigator) {
try {
setTimeout(() =>{
// 獲取當前位置
navigator.geolocation.getCurrentPosition((position) => {
// 解構方式獲取
const { latitude, longitude } = position.coords;
latNow.value=latitude;
lngNow.value=longitude;
if(setMap.value.length===0){
alert('定位不成功!');
}else{
// b單位為 km
let dis = getDistance(Number(lat.value) ,Number(lng.value),latitude,longitude);
// 距離公尺
let removing =dis * 100;
// 距離公尺<圓周距離限制
if(removing<distance.value){
showMap.value=true;
}else{
alert('你不在打卡範圍內,請檢查定位設定並重新登入!')
}
}
})
}, 5000)
}
catch (error:any) {
switch(error.code){
case error.PERMISSION_DENIED: alert('使用者拒絕了地理定位請求。');
break;
case error.POSITION_UNAVAILABLE: alert('位置資訊不可用。');
break;
case error.TIMEOUT:alert("取得用戶位置的請求逾時。");
break;
case error.UNKNOWN_ERROR:alert("出現未知錯誤。");
break;
}
}
}else{
alert("Sorry, 你的裝置不支援地理位置功能。")
}
}

表單type 取得的 time 為 ‘10:00’這種格式如何改為時間搓,才能夠判斷遲到或是早退

邏輯:獲得現在時間 =>切割時間與分鐘 => 設定時間與分鐘 =>獲得的時間字符getTime()
1
2
3
4
5
6
7
8
9
10
11
let now = new Date();
//表單type 取得的 time 為 '10:00'
const hourMinute = '10:00';
//split() 方法可以用來根據你指定的分隔符號,將字串切割成一個字串陣列。
var [hour, minute] = hourMinute.split(':');
now.setHours(hour) ;//setHours()方法依據本地時間來設定日期物件中的小時
now.setMinutes(minute);//setMinutes()方法依據本地時間來設定日期物件中的分鐘
now.setSeconds(0);//setMinutes()方法用於設定日期物件的秒字段
now.setMilliseconds(0);//方法用於指定時間的毫秒字段
console.log('塞入的彈性時間',now) // Sat Apr 20 2024 10:00:00 GMT+0800 (台北標準時間);
console.log('時間搓',now.getTime()) // now.getTime()
參考資料 上班與下班打卡邏輯大致上是過濾當日的全部上班與下班,來判斷是否出現警告是否打過卡: 過濾當日上班正常或是上班遲到都必須顯示於畫面上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
<script setup lang="ts">
import axios from "axios";
import {ref,onMounted} from 'vue';
import moment from 'moment';
import dayjs from 'dayjs'
const getToken =localStorage.getItem("token");
const headers = {"Authorization": 'Bearer '+getToken};
const pageName=ref<string>('出勤打卡');
interface menuType{
id:string,
title:string,
href:string,
}
const setCheckModesMainMenu=ref<menuType[]>([
{ id: 'setWorkCheckList', title: '出勤設定列表', href: '/admin/set_work_check' },
{ id: 'checkWorkCheck', title: '出勤打卡', href: '/admin/checkWorkCheck' },
{ id: 'checkWorkLists', title: '出勤列表', href: '/admin/checkWorkLists' },
{ id: 'checkCalendar', title: '出勤行事曆', href: '/admin/calendar' },
])
// 獲取 ip
const ip = ref<string>('');
const getIpClient = async () =>{
try {
const api='https://api.ipify.org?format=json';
const res = await axios.get(api);
if(res.status===200) {
ip.value = res.data.ip;
}
}
catch (error) {
console.error(error);
}
}

const isOnWorkMessage=ref<string>('');
const isOnBusinessWorkMessage = ref<string>('');
const isLate=ref<boolean>(true);
const borderColor=ref<string>('');
// 是否遲到
const isLateHow = (time:any) => {
const i=time;
console.log('i',i);
i > timeStamp.value ? isOnWorkMessage.value = '上班遲到' : isOnWorkMessage.value = '上班正常';
i > timeStamp.value ? isOnBusinessWorkMessage.value = '外勤上班遲到' : isOnBusinessWorkMessage.value = '外勤上班正常';
i > timeStamp.value ? isLate.value = true : isLate.value = false;
i > timeStamp.value ? borderColor.value = 'red' : borderColor.value = '#00bcd4';
}
interface checkWorkList{
_id:string,//id
workCheck_Id:number,//打卡id
userId:string,//使用者id
userName:string,//使用者名稱
title:string,//打卡名稱
start:string,//打卡時間
ip:string, // 打卡ip
onWorkStatus:boolean,//是否已上班
offWorkStatus:boolean,//是否已下班
borderColor:string, // 外框顏色
lat:string,//經度
lng:string,//緯度
}
const showOnWorkCheck=ref<boolean>(true);
const currentDayonWorkLists=ref<checkWorkList[]>([]);
const currentDayoffWorkLists=ref<checkWorkList[]>([]);
const currentDayonBusinessworkLists = ref<checkWorkList[]> ([]);
const currentDayoffBusinessworkLists= ref<checkWorkList[]> ([]);
const onWorkCheckTime=ref<string>('');
const offWorkCheckTime=ref<string>('');
const onBusinessWorkTime=ref<string>('');
const offBusinessWorkTime=ref<string>('');
let currentDay =dayjs(Date.now()).format('YYYY-MM-DD');
const getCurrentDayWork = async () => {
try{
const api=`${import.meta.env.VITE_API_URL}/auth/workChecks`;
const res = await axios.get(api,{ headers });
if(res.status===200){
//獲取當天上班打卡
currentDayonWorkLists.value = res.data.filter(function (item: any) {
if (
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.onWorkStatus != null
&& item.title === '上班正常'
||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.onWorkStatus != null
&& item.title === '上班遲到'
) {
showOnWorkCheck.value = false;
onWorkCheckTime.value = dayjs(item.start).format('YYYY-MM-DD HH:mm:ss');
return item;
}
})
console.log('166',currentDayonWorkLists.value)
// 獲取當天下班打卡
currentDayoffWorkLists.value = res.data.filter(function (item: any) {
if (
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.offWorkStatus != null && item.title === '下班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.offWorkStatus != null && item.title === '下班早退'
) {
showOffWorkCheck.value = false;
offWorkCheckTime.value = dayjs(item.start).format('YYYY-MM-DD HH:mm:ss');
return item;
}
})
// 獲取當天外勤上班打卡
currentDayonBusinessworkLists.value = res.data.filter(function (item: any) {
if (
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.onWorkStatus != null && item.title === '外勤上班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.onWorkStatus != null && item.title === '外勤上班遲到'
) {
showOnBusinessWorkCheck.value = false;
onBusinessWorkTime.value = dayjs(item.start).format('YYYY-MM-DD HH:mm:ss')
return item;
}
})

// 外勤下班打卡列表
currentDayoffBusinessworkLists.value = res.data.filter(function (item: any) {
if (
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.offWorkStatus != null && item.title === '外勤下班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.offWorkStatus != null && item.title === '外勤下班早退'
) {
showOffBusinessWorkCheck.value = false;
offBusinessWorkTime.value = dayjs(item.start).format('YYYY-MM-DD HH:mm:ss')
return item;
}
})
}

}
catch(err){
console.log('err',err)
}
}
const currentDayAllOnWorkList =ref<checkWorkList[]>([]);
const currentDayAllOffWorkList =ref<checkWorkList[]>([])
// 獲取當日所有上班打卡列表(包含出勤上班與上班)=>必須加入判斷 是否是本人的數據
const getCurrentDayAllWork = async () => {
try {
const api = `${import.meta.env.VITE_API_URL}/auth/workChecks`;
const res = await axios.get(api,{ headers });
if (res.status === 200) {
// 獲取當日所有上班打卡列表
currentDayAllOnWorkList.value = res.data.filter(function (item: any) {
if (
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.title === '上班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.title === '上班遲到' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.title === '外勤上班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.onWorkStatus && item.title === '外勤上班遲到'
) {
console.log('item',item)
return item;
}

})
console.log('獲取當日所有上班打卡列表', currentDayAllOnWorkList.value);
// 獲取當日所有下班列表
currentDayAllOffWorkList.value = res.data.filter(function (item: any) {
if (
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.title === '下班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.title === '下班早退' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.title === '外勤下班正常' ||
item.userName===localStorage.getItem('userName') &&
dayjs(item.start).format('YYYY-MM-DD').indexOf(currentDay) !== -1 && item.offWorkStatus && item.title === '外勤下班早退'
) {
return item;
}
})
}
}
catch (err) {
console.error(err);
}
}

// 上班打卡
const checkOnWork = async () =>{
try {
if( currentDayAllOnWorkList.value.length>0 ){
alert('已經打過上班卡了')
}
else{
let time=Date.now();
isLateHow(time);
const api=`${import.meta.env.VITE_API_URL}/auth/workCheck`;
let query ={
workCheck_Id: Date.now(),
userId: localStorage.getItem('userId'),
userName: localStorage.getItem('userName'),
start: dayjs(Date.now()).format('YYYY-MM-DD HH:mm'),
onWorkStatus: true,
offWorkStatus: false,
title: isOnWorkMessage.value,
borderColor: borderColor.value,
ip: String(ip.value),
lat:latNow.value,
lng:lngNow.value,
}

console.log(query)
const res = await axios.post(api,query, { headers });

if(res.status===200){
alert('打卡成功!');
getCurrentDayWork();
getCurrentDayAllWork();
}
}
}
catch(err){
console.log('err',err)
}
}
const showOnBusinessWorkCheck =ref<boolean>(true);
// 外勤上班打卡
const checkOnBusinessWork = async () =>{
try {
if( currentDayAllOnWorkList.value.length>0 ){
alert('已經打過上班卡了')
}
else{
let time=Date.now();
isLateHow(time);
const api=`${import.meta.env.VITE_API_URL}/auth/workCheck`;
let query ={
workCheck_Id: Date.now(),
userId: localStorage.getItem('userId'),
userName: localStorage.getItem('userName'),
start: dayjs(Date.now()).format('YYYY-MM-DDTHH:mm'),
onWorkStatus: true,
offWorkStatus: false,
title: isOnBusinessWorkMessage.value,
borderColor: borderColor.value,
ip: String(ip.value),
lat:latNow.value,
lng:lngNow.value,
}

console.log(query)
const res = await axios.post(api,query, { headers });

if(res.status===200){
showOnBusinessWorkCheck.value = false;
alert('打卡成功!');
getCurrentDayWork();
getCurrentDayAllWork();
}
}
}
catch(err){
console.log('err',err)
}
}
// 是否早退
const isOffWorkMessage=ref<string>('');
const isOffBusinessWorkMessage=ref<string>('');
const isLeaveEarly=ref<boolean>(true);
const isLeaveEarlyHow = (time:any) => {
const i=time;
i < endTimeStamp.value ? isOffWorkMessage.value = '下班早退' : isOffWorkMessage.value = '下班正常';
i < endTimeStamp.value ? isOffBusinessWorkMessage.value = '外勤下班早退' : isOffBusinessWorkMessage.value = '外勤下班正常';
i < endTimeStamp.value ? isLeaveEarly.value = true : isLeaveEarly.value = false;
i < endTimeStamp.value ? borderColor.value = 'red' : borderColor.value = '#00bcd4';
}
const showOffWorkCheck =ref<boolean>(true);
// 下班打卡
const checkOffWork =async()=>{
try {
if( currentDayAllOffWorkList.value.length>0){
alert('已經打過下班卡了')
}
else{
let time=Date.now();
isLeaveEarlyHow(time);
const api=`${import.meta.env.VITE_API_URL}/auth/workCheck`;
let query ={
workCheck_Id: Date.now(),
userId: localStorage.getItem('userId'),
userName: localStorage.getItem('userName'),
start: dayjs(Date.now()).format('YYYY-MM-DDTHH:mm'),
onWorkStatus: true,
offWorkStatus: true,
title: isOffWorkMessage.value,
borderColor: borderColor.value,
ip: String(ip.value),
lat:latNow.value,
lng:lngNow.value,
}
console.log(query)
const res = await axios.post(api,query, { headers });

if(res.status===200){
showOffWorkCheck.value = false;
getCurrentDayWork();
getCurrentDayAllWork();
alert('打卡成功!');
}
}
}
catch(err){
console.log('err',err)
}
}
const showOffBusinessWorkCheck =ref<boolean>(true);
//外勤下班打卡
const checkOffBusinessWork =async()=>{
try {
if( currentDayAllOffWorkList.value.length>0){
alert('已經打過下班卡了')
}
else{
let time=Date.now();
isLeaveEarlyHow(time);
const api=`${import.meta.env.VITE_API_URL}/auth/workCheck`;
let query ={
workCheck_Id: Date.now(),
userId: localStorage.getItem('userId'),
userName: localStorage.getItem('userName'),
start: dayjs(Date.now()).format('YYYY-MM-DDTHH:mm'),
onWorkStatus: true,
offWorkStatus: true,
title: isOffBusinessWorkMessage.value,
borderColor: borderColor.value,
ip: String(ip.value),
lat:latNow.value,
lng:lngNow.value,
}
console.log(query)
const res = await axios.post(api,query, { headers });

if(res.status===200){
showOffBusinessWorkCheck.value = false;
getCurrentDayWork();
getCurrentDayAllWork();
alert('打卡成功!');
}
}
}
catch(err){
console.log(err)
}
}
//
const getUsers = async () =>{
try {
const api=`${import.meta.env.VITE_API_URL}/auth/users`;
let query ={
workCheck_Id:Date.now(),
userId:localStorage.getItem('userId'),
userName:localStorage.getItem('userName'),
start:Date.now(),
onWorkStatus:true,
title:'上班打卡',
borderColor:'red',
ip: String(ip.value),
lat:latNow.value,
lng:lngNow.value,
}
const res = await axios.post(api,query, { headers });
if(res.status===200){

}
}
catch (error) {
console.error(error);
}
}

// 算出圓周
const radius =(p: number) =>{
return p * Math.PI / 180.0;
}
// 計算距離,參數分別爲第一點的緯度,經度;第二點的緯度,經度
const getDistance =(lat1: number,lng1: number,lat2: number,lng2: number) =>{
const radLat1 = radius(lat1);
const radLat2 = radius(lat2);
// 緯度距離
const a = radLat1 - radLat2;
const b = radius(lng1) - radius(lng2);
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));

s = s * 6378.137 ;// 地球半徑;
s = Math.round(s * 10000) / 10000; //單位為 km
return s;
}
// 定位
const showMap=ref<boolean>(false);
const getMap=ref<string>('');
const lat = ref<string>('');
const lng = ref<string>('');
const latNow = ref<number| null>(null);
const lngNow = ref<number | null>(null);
// 圓周距離
const distance =ref<number>(500);
const location =() =>{
if ("geolocation" in navigator) {
try {
setTimeout(() =>{
// 獲取當前位置
navigator.geolocation.getCurrentPosition((position) => {
// 解構方式獲取
const { latitude, longitude } = position.coords;
latNow.value=latitude;
lngNow.value=longitude;
if(setMap.value.length===0){
alert('定位不成功!');
}else{
// b單位為 km
let dis = getDistance(Number(lat.value) ,Number(lng.value),latitude,longitude);
// 距離公尺
let removing =dis * 100;
// 距離公尺<圓周距離限制
if(removing<distance.value){
showMap.value=true;
}else{
alert('你不在打卡範圍內,請檢查定位設定並重新登入!')
}
}
})
}, 5000)
}
catch (error:any) {
switch(error.code){
case error.PERMISSION_DENIED: alert('使用者拒絕了地理定位請求。');
break;
case error.POSITION_UNAVAILABLE: alert('位置資訊不可用。');
break;
case error.TIMEOUT:alert("取得用戶位置的請求逾時。");
break;
case error.UNKNOWN_ERROR:alert("出現未知錯誤。");
break;
}
}
}else{
alert("Sorry, 你的裝置不支援地理位置功能。")
}
}
interface checkSetingListType {
_id:string,
mode:number,//模式
name:string,//地點
url:string,//地圖
flexibleTime:string,//彈性上班
flexibleHour:string,//工時
distance:number,//圓周
lat:string,//經度
lng:string,//緯度
personGroup:Object,//群組
buildDate:number,
updataDate:number,
}
const checkSetingList=ref<checkSetingListType[]>([]);
const flexibleTime=ref<number | any >(null);
const setMap=ref<string>('');
const office=ref<string>('');
const flexibleHour =ref<string>('');
// 獲得出勤設定
const getSetMode = async() =>{
try {
const api=`${import.meta.env.VITE_API_URL}/auth/setCheckModes`;
const res = await axios.get(api, { headers });
if (res.status === 200) {
setTimeout(() =>{
checkSetingList.value=res.data.filter((element:any)=>{
if(element.name==='台中大墩11街'){
return element
}else if (element.name!='台中大墩11街'){
return element
}
})
console.log('台中辦公室',checkSetingList.value);
lat.value=checkSetingList.value[0].lat;
lng.value=checkSetingList.value[0].lng;
flexibleTime.value=checkSetingList.value[0].flexibleTime;
setMap.value=checkSetingList.value[0].url;
console.log(setMap.value)
distance.value=checkSetingList.value[0].distance;
office.value=checkSetingList.value[0].name;
flexibleHour.value=checkSetingList.value[0].flexibleHour;
// 將彈性時間改為時間搓
formatFlexibleTime(flexibleTime.value);
}, 3000)
location()
}
}
catch(err){
console.log('err',err)
}
}

const changeFlexibleTime =ref<any>('');
const timeStamp=ref<any>(null);
const endTimeStamp=ref<any>(null);//下班的時間搓
// 將彈性時間改為時間搓 塞入timeStamp 1.獲取flexibleTime
const formatFlexibleTime =(flexibleTime:any) =>{
let now = new Date();
// 原生時間表單表單type 取得的 time 為 '10:00'
const hourMinute = flexibleTime;
//split() 方法可以用來根據你指定的分隔符號,將字串切割成一個字串陣列。
var [hour, minute] = hourMinute.split(':');
// 設定日期的小時、分鐘、秒和毫秒
now.setHours(hour) ;//setHours()方法依據本地時間來設定日期物件中的小時
now.setMinutes(minute);//setMinutes()方法依據本地時間來設定日期物件中的分鐘
now.setSeconds(0);//setMinutes()方法用於設定日期物件的秒字段
now.setMilliseconds(0);//方法用於指定時間的毫秒字段
changeFlexibleTime.value = now;
// console.log('changeFlexibleTime',changeFlexibleTime.value);
// Sat Apr 20 2024 10:00:00 GMT+0800 (台北標準時間);
const changeFormat =dayjs(changeFlexibleTime.value).format('YYYY-MM-DD HH:mm:ss');
console.log('changeFormat',changeFormat);
timeStamp.value=now.getTime()//
console.log('timeStamp.value',timeStamp.value);
//算出下班時間
if(changeFormat!=''){
const endTmeFormat =dayjs(changeFormat).add(Number(flexibleHour.value), 'hours').format('YYYY-MM-DD HH:mm:ss');
console.log('下班時間',endTmeFormat);
const endTime=dayjs(endTmeFormat,'YYYY-MM-DD HH:mm:ss');
// console.log('endTime',endTime);
endTimeStamp.value=endTime.valueOf();
console.log('endTimeStamp.value',endTimeStamp.value);
}
// console.log('changeFormat',changeFormat);
const time = dayjs(changeFormat,'YYYY-MM-DD HH:mm:ss');
//**valueOf() 方法傳回字串的原始值。
timeStamp.value=time.valueOf();
//console.log('timeStamp.value',timeStamp.value);
}
onMounted(()=>{
getSetMode(); // 出勤設定
getIpClient();
getCurrentDayWork();
getCurrentDayAllWork();

})
</script>

json-server 模擬Api

json-server db.json

1
2
3
4
5
// 安裝
npm install --save json-server
// json-server版本號
json-server -v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let fruits = [
{item: 'apple', quantity: 0},
{item: 'banana', quantity: 1},
{item: 'cherry', quantity: 2}
]
// 這面這行就是告訴電腦我想要怎麼過濾
let fruitsNew = fruits.filter(e => e.quantity > 0)
console.log(fruitsNew)
/*
印出:
[
{ item: 'banana', quantity: 1 },
{ item: 'cherry', quantity: 2 }
]
*/

// 過濾重複元素

1
2
3
4
5
6
var array=["a", 1, 2, 3, 2, 3, "b", "a"]; 
var result=array.filter(function(element, index, arr){
return arr.indexOf(element) === index; });
console.log(result);
// ["a", 1, 2, 3, "b"];