You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ember组件功能咨询:基于地理位置的兴趣点排序问题

嘿,我看了你当前的代码,发现问题出在距离计算的架构上——每个miles-away组件各自单独计算对应酒庄的距离,这就导致你在控制器或路由层面拿不到所有酒庄的距离数据,自然没法对整个列表进行排序。咱们换个思路,把距离计算移到控制器里批量处理,这样就能一次性获取所有数据来排序了。

核心思路调整

把原本分散在组件里的距离计算逻辑,集中到控制器中批量调用Google Maps的DistanceMatrixService,这样能一次性拿到所有酒庄的距离数据,之后就可以轻松对列表进行排序。同时简化miles-away组件,让它只负责显示已计算好的距离。

具体代码修改

1. 更新控制器(处理批量计算与排序)

// controllers/vineyard/index.js
import Ember from 'ember';
import { inject as service } from '@ember/service';
import { later } from '@ember/runloop';

export default Ember.Controller.extend({
  userLocation: null,
  locationIsLoading: true,
  failState: false,
  googleMapsApi: service(),
  geolocation: service(),
  panelActions: Ember.inject.service(),

  // 自动根据距离排序的酒庄列表
  sortedVineyards: Ember.computed('userLocation', 'vineyards.@each.distance', function() {
    // 过滤出能计算出距离的酒庄,按距离升序排列(最近的在前)
    return this.get('vineyards')
      .filter(vineyard => vineyard.get('distance') !== Infinity)
      .sortBy('distance');
  }),

  userLocationChanged: function () {
    this.toggleProperty('locationIsLoading');
  }.observes('userLocation'),

  actions: {
    distanceFrom: function() {
      this.get('geolocation').trackLocation().then((geoObject) => {
        let currentLocation = this.get('geolocation.currentLocation');
        this.set('userLocation', currentLocation);
        // 触发批量距离计算
        this._calculateAllDistances(currentLocation);
      }, (reason) => {
        this.set('failState', true);
        console.log('Geolocation failed because ' + reason);
      });
    },

    stopError: function() {
      this.set('failState', false);
      $('.error').css('height', '0');
      $('.location-loader').animate({opacity: '0'}, 1000);
    }
  },

  // 私有方法:批量计算所有酒庄的距离
  _calculateAllDistances(userLocation) {
    if (!userLocation || !this.get('vineyards.length')) return;

    const origin = `${userLocation[0]},${userLocation[1]}`;
    const destinations = this.get('vineyards').map(vineyard => vineyard.get('location'));

    this.get('googleMapsApi.google').then((google) => {
      const distanceMatrixService = new google.maps.DistanceMatrixService();
      distanceMatrixService.getDistanceMatrix({
        origins: [origin],
        destinations: destinations,
        travelMode: google.maps.TravelMode.DRIVING,
        unitSystem: google.maps.UnitSystem.IMPERIAL,
        avoidHighways: false,
        avoidTolls: false
      }, (response, status) => {
        if (status !== google.maps.DistanceMatrixStatus.OK) {
          this.set('failState', true);
          return;
        }

        // 遍历结果,给每个酒庄添加距离数据
        const rows = response.rows[0].elements;
        this.get('vineyards').forEach((vineyard, index) => {
          const element = rows[index];
          if (element.status === "OK") {
            // 保存数值型距离(用于排序)和文本型距离(用于显示)
            vineyard.set('distance', element.distance.value);
            vineyard.set('distanceText', element.distance.text);
          } else {
            // 无法计算距离的酒庄,放到排序最后
            vineyard.set('distance', Infinity);
            vineyard.set('distanceText', 'N/A');
          }
        });

        // 计算完成后更新加载状态
        later(() => {
          this.set('locationIsLoading', false);
        }, 45);
      });
    });
  }
});

2. 简化miles-away组件

现在组件只需要负责显示已计算好的距离:

// components/miles-away.js
import Component from '@ember/component';

export default Component.extend({
  // 接收父组件传递的距离文本
  distanceText: null
});

组件模板也简化:

{{!-- components/miles-away.hbs --}}
<div class="miles-away-container">
  <p class="miles-away fade-in">{{distanceText}}</p>
</div>
{{yield}}

3. 更新模板渲染排序后的列表

{{!-- templates/vineyard/index.hbs --}}
<div class="distance">
  {{#if failState}}
    {{svg-jar 'fail'}}
  {{/if}}

  {{#if locationIsLoading}}
    {{location-loader}}
  {{else}}
    {{#each sortedVineyards as |vineyard|}}
      <!-- 这里替换成你的酒庄卡片结构 -->
      <div class="vineyard-item">
        <h3>{{vineyard.name}}</h3>
        <p>{{vineyard.location}}</p>
        {{miles-away distanceText=vineyard.distanceText}}
      </div>
    {{/each}}
  {{/if}}
</div>

额外说明

  • 效率提升:批量调用DistanceMatrixService比单个组件调用更高效,减少了网络请求次数,也更符合Google Maps API的使用规范。
  • 模型属性:如果你的vineyard是Ember Data模型,不需要为distance和distanceText添加持久化字段,直接用set临时设置即可,因为这是计算出来的非持久化数据。
  • 异常处理:对无法计算距离的酒庄做了特殊处理,会把它们放到排序列表的最后,避免影响正常显示。

内容的提问来源于stack exchange,提问作者Chris Johnson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:33:33