8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

对我进行地理定位(自动检测位置,当我打开界面时它不会检测,用户必须选择位置)

Eric Camescasse 1月前

24 0

问题:界面无法自动选择位置。它在浏览器中检测用户坐标,但不会在界面上显示位置。期望输出:这是

  • 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">&times;</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>
帖子版权声明 1、本帖标题:对我进行地理定位(自动检测位置,当我打开界面时它不会检测,用户必须选择位置)
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Eric Camescasse在本站《typescript》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 我已经将评论添加到我真正想要的和我面临的问题中,抱歉造成混淆。如果还有其他需要编辑的内容,请告诉我。

  • 我也添加了这个。是的,我能够获取用户坐标。但是,它不是从给定选项中自动选择的,我必须手动选择它。在数据库中,它显示用户位置。

  • detectLocation() 没有问题。问题在于 calculateDistance()。我们将地球视为球体,但地球不是球体,而是扁球体。所以我的 calcualteDistance() 使用 Haversine 公式,该公式计算两个地理坐标之间的距离,但该公式将地球视为球体,因此计算结果会略有错误。我所要做的就是将 1 公里改为 3 公里,现在它会自动选择。谢谢你的帮助。

返回
作者最近主题: