问题:界面无法自动选择位置。它在浏览器中检测用户坐标,但不会在界面上显示位置。期望输出:这是
p4
p5
p6
p7
p8
p9
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Actions, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { FormErrorService, NotifierService } from 'src/app/_services';
import { EventService } from 'src/app/_services/event/event.service';
import { AttendanceService } from 'src/app/_services/attendace/attendance.service';
import { BehaviorSubject, debounceTime, distinctUntilChanged, Subject, Subscription } from 'rxjs';
import { get_project_names, get_project_names_success } from '../state';
import { Pagination } from 'src/app/_common_methods';
import { pagination } from 'src/app/_models';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-add-attendance',
templateUrl: './add-attendance.component.html',
styleUrls: ['./add-attendance.component.scss']
})
export class AddAttendanceComponent implements OnInit {
public reference: BsModalRef;
public locationTypes = [
{
text: "Valora's office (Tecom)",
value: "valora",
coordinates:{lat: 25.099123914389963, lng: 55.17770532680706}
},
{
text: 'The warehouse (Umm Ramool)',
value: 'warehouse',
coordinates:{lat:25.232534003064174, lng:55.371241328836106}
},
{
text: 'Working from home',
value: 'work_from_home'
},
{
text: 'Other',
value: 'other'
},
]
id: number;
operation: string;
form: any;
isB2b: string;
today = new Date();
isLoading = false;
projects$$ = new BehaviorSubject(null);
project_request: Subscription;
project_pagination: Pagination;
project_exclude_ids: number[] = [];
department_pagination: Pagination;
project_typehead: Subject<string> = new Subject();
project_search: string = '';
constructor(
private fb: FormBuilder,
private notifier: NotifierService,
private _actions: Actions,
private _store: Store,
public _form_error_service: FormErrorService,
private attendanceService: AttendanceService,
private eventService: EventService
) {
this.listen_events();
this.department_pagination = new Pagination(10);
this.project_pagination = new Pagination(10);
}
ngOnInit(): void {
this.createForm();
this.get_project(this.department_pagination.page_limit, 0, false);
this.detectLocation();
}
get_project(
limit: number,
offset: number,
is_search: boolean,
search: string = '',
exclude_ids: string = ''
) {
const pagination_config = new pagination.pagination(
limit,
offset,
search,
is_search,
exclude_ids
);
this._store.dispatch(get_project_names({ data: pagination_config }));
}
createForm() {
this.form = this.fb.group({
type: [null, [Validators.required]],
project_id: [null, []],
reason: [null, []],
})
}
listen_events() {
this.project_request = this._actions
.pipe(ofType(get_project_names_success))
.subscribe(({ res, payload }) => {
if (payload.is_search) {
const { search } = payload;
const searchValueArr = search && !res.map(v => v.text.toLowerCase()).includes(search.toLowerCase()) ? [{
text: search,
value: search,
id: search
}] : [];
this.projects$$.next([...searchValueArr, ...res]);
} else {
this.projects$$.next([...(this.projects$$.value ?? []), ...res]);
}
this.project_pagination.update_offset_by_count =
this.projects$$.value.length - this.project_exclude_ids.length;
});
this.project_typehead
.pipe(
debounceTime(environment.default_debounce_time.search),
distinctUntilChanged()
)
.subscribe((search: string) => {
this.project_search = search;
this.project_pagination.update_offset_by_count = 0;
this.project_exclude_ids = [];
this.get_project(
this.project_pagination.page_limit,
this.project_pagination.offset,
true,
this.project_search,
this.project_exclude_ids.join(',')
);
});
}
getCurrentPosition() {
return new Promise( (resolve, reject) => {
navigator.geolocation.getCurrentPosition(
position => resolve(position),
error => reject(error)
)
})
}
calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number {
const toRad = (value: number) => (value * Math.PI) / 180;
const R = 6371; // Radius of the Earth in kilometers
const dLat = toRad(lat2 - lat1);
const dLng = toRad(lng2 - lng1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLng / 2) *
Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c; // Distance in kilometers
return distance;
}
async detectLocation() {
const { success, position } = await this.getLocation();
if (success) {
const { latitude, longitude } = position.coords;
// Find the nearest location within 1 km
let nearestLocation = this.locationTypes.reduce((closest, location) => {
if (!location.coordinates) {
return closest;
}
const distance = this.calculateDistance(
latitude,
longitude,
location.coordinates.lat,
location.coordinates.lng
);
if (distance < closest.distance) {
return {
location,
distance
};
}
return closest;
}, { location: null, distance: Infinity });
if (nearestLocation.distance <= 1) {
console.log('Nearest Location:', nearestLocation.location.value);
this.form.get('type').setValue(nearestLocation.location.value);
} else {
console.log('No location within 1km range');
}
} else {
this.notifier.error('Unable to detect location. Please select manually.');
}
}
async getLocation(): Promise<{ success: boolean, message: string, position?: any }> {
try {
const position = await this.getCurrentPosition();
return { success: true, message: "Location fetched successfully", position}
} catch (error) {
return { success: false, message: "Unable to get location"}
}
}
async addAttendance() {
this.isLoading = false;
if(this.form.invalid) {
this.notifier.error('Invalid Form Data');
return;
}
const req = {
locationtype: this.form.value.type,
project_id: this.form.value.project_id,
reason: this.form.value.reason
}
this.isLoading = true;
const { success, position } = await this.getLocation();
console.log('success', success);
console.log('position', position);
if(success) {
req['lat'] = position.coords.latitude;
req['long'] = position.coords.longitude;
}
this.attendanceService.createAttendance$(req).subscribe((res: any) => {
if(res && res.status === 200) {
this.eventService.publish('attendance_marked', true);
this.reference.hide();
this.isLoading = false;
this.notifier.success(res?.message || 'Attendance marked successfully');
} else {
this.notifier.error(res?.message || 'Unable to mark attendance, please try again!');
}
})
}
}
.labels {
font-size: var(--font-size-16);
color: #474554;
font-family: var(--font_4) ;;
}
.control {
display: flex;
flex-direction: column;
// row-gap: var(--font-size-16);
}
.inputs {
border: var(--border-1) solid #c6c6c5;
border-radius: var(--font-size-6);
height: var(--height-50);
padding: var(--font-size-12) var(--font-size-16);
}
textarea {
min-height: 90px;
}
textarea::placeholder {
color: #bdbdbd;
font-size: var(--font-size-16);
}
.cancel {
border: var(--border-1) solid var(--secondry_border_color);
color: var(--secondry_text_color);
font-size: var(--font-size-16);
padding: var(--font-size-10) var(--font-size-32);
font-family:var(--font_2);
background: none;
border-radius: var(--font-size-8);
}
.send {
border: var(--border-1) solid var(--button_primary_color);
color: white;
font-family: var(--font_4) ;;
padding: var(--font-size-10) var(--width-40);
font-size: var(--font-size-16);
background: var(--button_primary_color);
border-radius: var(--font-size-8);
}
.attachment {
width: fit-content;
position: relative;
padding: var(--font-size-4) var(--font-size-8);
border: 1px solid #c6c6c5;
border-radius: var(--width-50);
display: flex;
column-gap: var(--font-size-8);
cursor: pointer;
.file {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
}
}
.attach {
transform: rotate(45deg);
font-size: var(--font-size-20);
margin-right: var(--font-size-10);
}
input::placeholder {
color: #bdbdbd;
}
.filter-inputs {
height: var(--height-50);
}
.date {
border: var(--border-1) solid #c6c6c5;
border-right: 0 !important;
border-radius: var(--font-size-4);
}
.input-group {
button {
border: var(--border-1) solid #c6c6c5;
border-left: 0 !important;
display: flex;
background: #eadcc699;
align-items: center;
background: var;
span {
font-size: var(--font-size-20);
color: var(--button_primary_color);
}
}
}
.date-value {
font-size: var(--font-size-16);
color: var(--color-3F424B);
}
.date-controls {
display: flex;
column-gap: var(--font-size-24);
}
.add-leave .modal-body {
overflow: scroll;
}
<div>
<div class="modal-header">
<div class="heading-added text-center">
<h5 class="modal-title">
<span>Mark attendance - {{ operation }}</span>
</h5>
</div>
<button
type="button"
class="btn-close close pull-right"
aria-label="Close"
(click)="reference?.hide()"
>
<span aria-hidden="true" class="visually-hidden">×</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="form" class="add-leave">
<div class="form-container">
<div>
<div class="control">
<label class="flex items-center" class="labels" for="">
<span>From where you're clocking ?</span>
</label>
<div class="select-control">
<ng-select
[items]="locationTypes"
class="cursor-pointer"
placeholder="Select"
bindLabel="text"
bindValue="value"
formControlName="type"
>
</ng-select>
</div>
</div>
<p
class="form_error"
*ngIf="
form.get('type')?.touched &&
form.get('type')?.invalid
"
>
{{
_form_error_service.handle_form_error(
form.get("type"),
"ADD_ATTENDANCE_FORM.TYPE"
) | async
}}
</p>
</div>
<div *ngIf="isB2b === 'yes' && operation =='Clock In'" class="mt-3">
<div class="control">
<label class="flex items-center" class="labels" for="">
<span>Which project are you working on ?</span>
</label>
<div class="select-control">
<ng-select
class="cursor-pointer filter-inputs"
placeholder="Select project name"
formControlName="project_id"
[items]="projects$$.value"
bindValue="text"
bindLabel="text"
formControlName="project_id"
(scrollToEnd)="
get_project(
project_pagination.page_limit,
project_pagination.offset,
false,
project_search,
project_exclude_ids.join(',')
)
"
[typeahead]="project_typehead"
>
</ng-select>
</div>
</div>
<p
class="form_error"
*ngIf="
form.get('project_id')?.touched &&
form.get('project_id')?.invalid
"
>
{{
_form_error_service.handle_form_error(
form.get("project_id"),
"ADD_ATTENDANCE_FORM.project_id"
) | async
}}
</p>
</div>
<div class="mt-3">
<div class="control">
<label class="flex items-center" class="labels" for="">
<span>Notes ({{form.get('type').value !== 'other' ? "Optional" : "Optional"}})</span>
</label>
<textarea
class="inputs"
type="text"
[placeholder]="form.get('type').value !== 'other' ? 'You have anything to mention about your attendance?' : 'Please mention the reason for marking your attendance from this location.'"
formControlName="reason"
rows="5"
></textarea>
</div>
</div>
<div class="py-4">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<div class="w-100 actions">
<button class="cancel" (click)="reference.hide()">Cancel</button>
<button
class="send"
(click)="addAttendance()"
[ngClass]="{
cursor_pointer: form.valid || isLoading,
'pointer-events-none': form.invalid || isLoading,
disabled: form.invalid || isLoading
}"
[disabled]="isLoading || form.invalid"
>
{{isLoading ? "Loading ..." : operation}}
</button>
</div>
</div>
</div>
我已经将评论添加到我真正想要的和我面临的问题中,抱歉造成混淆。如果还有其他需要编辑的内容,请告诉我。