后端迁移到fastapi (#3)

Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
2026-07-18 20:49:09 +08:00
parent 217b81c506
commit 51743645e2
71 changed files with 845 additions and 3580 deletions
+24 -31
View File
@@ -3,9 +3,10 @@ import { computed, ref, onMounted, onUnmounted } from 'vue'
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
import MiniLocationMap from '@/components/cloud/MiniLocationMap.vue'
import QuickUploadModal from '@/components/cloud/QuickUploadModal.vue'
import { supabase } from '@/lib/supabase'
import { apiRequest } from '@/lib/api'
import { loadAMap } from '@/lib/amap'
import { useAuthStore } from '@/stores/auth'
import type { ApiCloud } from '@/types/api'
import { NIcon } from 'naive-ui'
import { Adjustments, Calendar, CloudUpload, Refresh, Map, Satellite, X } from '@vicons/tabler'
@@ -212,44 +213,36 @@ function hideHeader() {
window.dispatchEvent(new CustomEvent(HIDE_HEADER_EVENT))
}
function toCloudMarker(row: Record<string, unknown>): CloudMarkerData {
const ct = row.cloud_types as Record<string, unknown> | null
const pf = row.profiles as Record<string, unknown> | null
function toCloudMarker(row: ApiCloud): CloudMarkerData {
return {
id: row.id as string,
id: row.id,
latitude: row.latitude as number,
longitude: row.longitude as number,
imageUrl: row.image_url as string,
thumbnailUrl: (row.thumbnail_url as string | null) ?? null,
locationName: (row.location_name as string | null) ?? null,
description: (row.description as string | null) ?? null,
cloudTypeName: (ct?.name as string) || (row.custom_cloud_type as string) || '未知',
rarity: (ct?.rarity as 'common' | 'uncommon' | 'rare') || 'common',
username: (pf?.username as string) || '匿名',
capturedAt: (row.captured_at as string) || (row.created_at as string),
createdAt: row.created_at as string,
imageUrl: row.image_url,
thumbnailUrl: row.thumbnail_url,
locationName: row.location_name,
description: row.description,
cloudTypeName: row.cloud_type_name || row.custom_cloud_type || '未知',
rarity: row.cloud_type_rarity || 'common',
username: row.username || '匿名',
capturedAt: row.captured_at || row.created_at,
createdAt: row.created_at,
}
}
async function fetchCloudsByRange(field: 'captured_at' | 'created_at', start: Date, end: Date): Promise<CloudMarkerData[]> {
const { data, error } = await supabase
.from('clouds')
.select('id,image_url,thumbnail_url,latitude,longitude,location_name,description,captured_at,created_at,custom_cloud_type,cloud_types(name,rarity),profiles(username)')
.eq('status', 'approved')
.eq('is_hidden', false)
.not('latitude', 'is', null)
.not('longitude', 'is', null)
.gte(field, start.toISOString())
.lt(field, end.toISOString())
.order(field, { ascending: true })
.limit(1000)
if (error) {
statusText.value = `查询失败: ${error.message}`
const params = new URLSearchParams({
start: start.toISOString(),
end: end.toISOString(),
time_field: field,
})
try {
const rows = await apiRequest<ApiCloud[]>(`/clouds/map?${params}`, { auth: false })
return rows.map(toCloudMarker)
} catch (error) {
statusText.value = `查询失败: ${error instanceof Error ? error.message : '未知错误'}`
return []
}
return ((data || []) as Array<Record<string, unknown>>).map(toCloudMarker)
}
async function loadRealtimeClouds() {
@@ -486,7 +479,7 @@ onUnmounted(() => {
</button>
</div>
<div class="absolute bottom-6 right-16 z-10 flex justify-end pointer-events-none">
<div class="absolute bottom-6 right-19 z-10 flex justify-end pointer-events-none">
<div class="pointer-events-auto flex items-center justify-end gap-3">
<Transition name="map-timeline-controls">
<div v-if="timelineControlsOpen" class="flex items-center gap-3">