feat(import): selective GPX/KML element import and performance improvements

Add type-selector UI in the file import modal letting users choose which
GPX elements (waypoints, routes, tracks) or KML/KMZ elements (points,
paths) to import. KML LineString placemarks are now imported as path
places with route_geometry.

Performance improvements:
- Extract MemoPlaceRow with React.memo and contentVisibility:auto to cut
  unnecessary re-renders in PlacesSidebar
- Add weatherQueue to cap concurrent weather fetches at 3
- Replace sequential per-place deletes with a single bulkDelete API call
  (new DELETE /places/bulk endpoint + deletePlacesMany service)
- Memoize atlas/photo/weather service calls to avoid redundant requests
- Add multi-select mode to PlacesSidebar for bulk operations

Add large GPX/KML/KMZ fixtures for integration/perf testing and two
profiler analysis scripts under scripts/.
This commit is contained in:
jubnl
2026-04-18 01:28:37 +02:00
parent 9a31fcac7b
commit 6a718fccea
45 changed files with 22471 additions and 285 deletions
+18
View File
@@ -10,6 +10,9 @@ const zh: Record<string, string> = {
'common.add': '添加',
'common.loading': '加载中...',
'common.import': '导入',
'common.select': '选择',
'common.selectAll': '全选',
'common.deselectAll': '取消全选',
'common.error': '错误',
'common.unknownError': '未知错误',
'common.tooManyAttempts': '尝试次数过多,请稍后再试。',
@@ -876,6 +879,8 @@ const zh: Record<string, string> = {
'trip.toast.reservationAdded': '预订已添加',
'trip.toast.deleted': '已删除',
'trip.confirm.deletePlace': '确定要删除这个地点吗?',
'trip.confirm.deletePlaces': '删除 {count} 个地点?',
'trip.toast.placesDeleted': '已删除 {count} 个地点',
// Day Plan Sidebar
'dayplan.emptyDay': '当天暂无计划',
@@ -920,6 +925,17 @@ const zh: Record<string, string> = {
'places.importFileError': '导入失败',
'places.importAllSkipped': '所有地点已在行程中。',
'places.gpxImported': '已从 GPX 导入 {count} 个地点',
'places.gpxImportTypes': '要导入什么?',
'places.gpxImportWaypoints': '路点',
'places.gpxImportRoutes': '路线',
'places.gpxImportTracks': '轨迹(含路径几何)',
'places.gpxImportNoneSelected': '请至少选择一种导入类型。',
'places.kmlImportTypes': '要导入什么?',
'places.kmlImportPoints': '点(Placemarks',
'places.kmlImportPaths': '路径(LineStrings',
'places.kmlImportNoneSelected': '请至少选择一种类型。',
'places.selectionCount': '已选 {count} 项',
'places.deleteSelected': '删除所选',
'places.kmlKmzImported': '已从 KMZ/KML 导入 {count} 个地点',
'places.urlResolved': '已从 URL 导入地点',
'places.importList': '列表导入',
@@ -936,6 +952,7 @@ const zh: Record<string, string> = {
'places.assignToDay': '添加到哪一天?',
'places.all': '全部',
'places.unplanned': '未规划',
'places.filterTracks': '路线',
'places.search': '搜索地点...',
'places.allCategories': '所有分类',
'places.categoriesSelected': '个分类',
@@ -1696,6 +1713,7 @@ const zh: Record<string, string> = {
'undo.reorder': '地点已重新排序',
'undo.optimize': '路线已优化',
'undo.deletePlace': '地点已删除',
'undo.deletePlaces': '地点已删除',
'undo.moveDay': '地点已移至另一天',
'undo.lock': '地点锁定已切换',
'undo.importGpx': 'GPX 导入',