refactor(api): 对齐后端字段,移除前端臆造字段

This commit is contained in:
2026-08-21 02:46:28 +08:00
parent b0759becbb
commit 306d709cc7
15 changed files with 31 additions and 66 deletions
+1 -1
View File
@@ -141,7 +141,7 @@ watch(() => props.initialValue, value => {
> >
<option value="" disabled>请选择</option> <option value="" disabled>请选择</option>
<option v-for="ct in cloudTypes" :key="ct.id" :value="ct.id"> <option v-for="ct in cloudTypes" :key="ct.id" :value="ct.id">
{{ ct.name }}({{ ct.name_en }}) {{ ct.name }}{{ ct.genus ? '(' + ct.genus + ')' : '' }}
</option> </option>
</select> </select>
</div> </div>
+1 -1
View File
@@ -271,7 +271,7 @@ onBeforeUnmount(() => {
> >
<option value="" disabled>请选择</option> <option value="" disabled>请选择</option>
<option v-for="cloudType in cloudsStore.cloudTypes" :key="cloudType.id" :value="cloudType.id"> <option v-for="cloudType in cloudsStore.cloudTypes" :key="cloudType.id" :value="cloudType.id">
{{ cloudType.name }}({{ cloudType.name_en }}) {{ cloudType.name }}{{ cloudType.genus ? '(' + cloudType.genus + ')' : '' }}
</option> </option>
</select> </select>
<p v-if="activeItem.errors.cloudCategory" class="oc-field-error">{{ activeItem.errors.cloudCategory }}</p> <p v-if="activeItem.errors.cloudCategory" class="oc-field-error">{{ activeItem.errors.cloudCategory }}</p>
+2 -13
View File
@@ -141,16 +141,11 @@ export function toAuthUser(profile: BackendUserProfile): AuthUser {
return { return {
id: profile.id, id: profile.id,
email: profile.email, email: profile.email,
// Protected profile routes reject unverified accounts, so a successfully
// loaded current profile is necessarily email-verified.
email_verified: true,
username: profile.name, username: profile.name,
avatar_url: null, avatar_id: profile.avatar_id,
role: profile.role, role: profile.role,
is_disabled: profile.is_disabled, is_disabled: profile.is_disabled,
cloud_count: profile.cloud_count, cloud_count: profile.cloud_count,
public_cloud_count: profile.cloud_count,
collection_count: 0,
received_like_count: profile.received_like_count, received_like_count: profile.received_like_count,
created_at: profile.created_at, created_at: profile.created_at,
} }
@@ -160,14 +155,12 @@ export function toCloudType(row: BackendCloudType): CloudType {
return { return {
id: row.id, id: row.id,
name: row.name, name: row.name,
name_en: row.genus || '',
genus: row.genus || '', genus: row.genus || '',
// Compatibility value for the excluded legacy admin view. Migrated pages // Compatibility value for the excluded legacy admin view. Migrated pages
// display rarity_value directly because API.md defines no semantic mapping. // display rarity_value directly because API.md defines no semantic mapping.
rarity: 'common', rarity: 'common',
rarity_value: row.rarity, rarity_value: row.rarity,
description: row.description, description: row.description,
icon_url: null,
created_at: row.created_at, created_at: row.created_at,
} }
} }
@@ -177,12 +170,10 @@ export function toApiCloud(row: BackendCloudInfo): ApiCloud {
id: row.id, id: row.id,
user_id: row.owner.id, user_id: row.owner.id,
cloud_type_id: row.type.id, cloud_type_id: row.type.id,
custom_cloud_type: null,
image_url: imageUrl(row.id, 'original'), image_url: imageUrl(row.id, 'original'),
thumbnail_url: imageUrl(row.id, 'preview'), thumbnail_url: imageUrl(row.id, 'preview'),
latitude: row.latitude, latitude: row.latitude,
longitude: row.longitude, longitude: row.longitude,
location_name: null,
description: row.description, description: row.description,
captured_at: row.captured_at, captured_at: row.captured_at,
status: row.status, status: row.status,
@@ -192,7 +183,6 @@ export function toApiCloud(row: BackendCloudInfo): ApiCloud {
cloud_type: { cloud_type: {
id: row.type.id, id: row.type.id,
name: row.type.name, name: row.type.name,
name_en: row.type.genus || '',
rarity: 'common', rarity: 'common',
rarity_value: row.type.rarity, rarity_value: row.type.rarity,
}, },
@@ -201,7 +191,6 @@ export function toApiCloud(row: BackendCloudInfo): ApiCloud {
cloud_type_rarity: 'common', cloud_type_rarity: 'common',
cloud_type_rarity_value: row.type.rarity, cloud_type_rarity_value: row.type.rarity,
username: row.owner.name, username: row.owner.name,
favorite_count: row.received_like_count, received_like_count: row.received_like_count,
is_favorited: false,
} }
} }
+10 -10
View File
@@ -1,14 +1,14 @@
export const CLOUD_TYPES = [ export const CLOUD_TYPES = [
{ id: 1, name: '积云', nameEn: 'Cumulus' }, { id: 1, name: '积云' },
{ id: 2, name: '层云', nameEn: 'Stratus' }, { id: 2, name: '层云' },
{ id: 3, name: '卷云', nameEn: 'Cirrus' }, { id: 3, name: '卷云' },
{ id: 4, name: '积雨云', nameEn: 'Cumulonimbus' }, { id: 4, name: '积雨云' },
{ id: 5, name: '层积云', nameEn: 'Stratocumulus' }, { id: 5, name: '层积云' },
{ id: 6, name: '高积云', nameEn: 'Altocumulus' }, { id: 6, name: '高积云' },
{ id: 7, name: '高层云', nameEn: 'Altostratus' }, { id: 7, name: '高层云' },
{ id: 8, name: '雨层云', nameEn: 'Nimbostratus' }, { id: 8, name: '雨层云' },
{ id: 9, name: '卷层云', nameEn: 'Cirrostratus' }, { id: 9, name: '卷层云' },
{ id: 10, name: '卷积云', nameEn: 'Cirrocumulus' }, { id: 10, name: '卷积云' },
] as const ] as const
export function getCloudTypeName(id: string | string[]) { export function getCloudTypeName(id: string | string[]) {
+1 -1
View File
@@ -19,7 +19,7 @@ export const useAuthStore = defineStore('auth', () => {
profile.value = { profile.value = {
id: normalized.id, id: normalized.id,
username: normalized.username, username: normalized.username,
avatar_url: normalized.avatar_url, avatar_id: normalized.avatar_id,
role: normalized.role, role: normalized.role,
is_disabled: normalized.is_disabled, is_disabled: normalized.is_disabled,
created_at: normalized.created_at, created_at: normalized.created_at,
+3 -9
View File
@@ -8,12 +8,10 @@ import type { Profile } from '@/types/database'
export interface ProfileCloudItem { export interface ProfileCloudItem {
id: string id: string
cloud_type_id: number cloud_type_id: number
custom_cloud_type: null
image_url: string image_url: string
thumbnail_url: string thumbnail_url: string
latitude: number latitude: number
longitude: number longitude: number
location_name: null
description: string | null description: string | null
captured_at: string | null captured_at: string | null
created_at: string created_at: string
@@ -21,20 +19,17 @@ export interface ProfileCloudItem {
is_hidden: boolean is_hidden: boolean
cloudTypeName: string cloudTypeName: string
cloudTypeRarity: number cloudTypeRarity: number
favoriteCount: number receivedLikeCount: number
isFavorited: false
} }
function toProfileCloud(row: ApiCloud): ProfileCloudItem { function toProfileCloud(row: ApiCloud): ProfileCloudItem {
return { return {
id: row.id, id: row.id,
cloud_type_id: row.cloud_type_id as number, cloud_type_id: row.cloud_type_id as number,
custom_cloud_type: null,
image_url: row.image_url, image_url: row.image_url,
thumbnail_url: row.thumbnail_url as string, thumbnail_url: row.thumbnail_url as string,
latitude: row.latitude as number, latitude: row.latitude as number,
longitude: row.longitude as number, longitude: row.longitude as number,
location_name: null,
description: row.description, description: row.description,
captured_at: row.captured_at, captured_at: row.captured_at,
created_at: row.created_at, created_at: row.created_at,
@@ -42,8 +37,7 @@ function toProfileCloud(row: ApiCloud): ProfileCloudItem {
is_hidden: row.is_hidden, is_hidden: row.is_hidden,
cloudTypeName: row.cloud_type_name || '未知', cloudTypeName: row.cloud_type_name || '未知',
cloudTypeRarity: row.cloud_type_rarity_value ?? 0, cloudTypeRarity: row.cloud_type_rarity_value ?? 0,
favoriteCount: row.favorite_count, receivedLikeCount: row.received_like_count,
isFavorited: false,
} }
} }
@@ -112,7 +106,7 @@ export const useProfileStore = defineStore('profile-page', () => {
profilesByKey.value[identifier] = { profilesByKey.value[identifier] = {
id: owner?.id || identifier, id: owner?.id || identifier,
username: owner?.name || identifier, username: owner?.name || identifier,
avatar_url: null, avatar_id: null,
role: 'user', role: 'user',
is_disabled: false, is_disabled: false,
created_at: '', created_at: '',
+2 -7
View File
@@ -65,14 +65,11 @@ export interface CloudCreateResponse extends ActionResponse {
export interface AuthUser { export interface AuthUser {
id: string id: string
email: string email: string
email_verified: boolean
username: string username: string
avatar_url: string | null avatar_id: string | null
role: Profile['role'] role: Profile['role']
is_disabled: boolean is_disabled: boolean
cloud_count: number cloud_count: number
public_cloud_count: number
collection_count: number
received_like_count: number received_like_count: number
created_at: string created_at: string
} }
@@ -88,7 +85,6 @@ export interface ApiPage<T> {
export interface ApiCloudTypeSummary { export interface ApiCloudTypeSummary {
id: number id: number
name: string name: string
name_en: string
rarity: CloudType['rarity'] rarity: CloudType['rarity']
rarity_value: number rarity_value: number
} }
@@ -106,8 +102,7 @@ export interface ApiCloud extends Cloud {
cloud_type_rarity: CloudType['rarity'] | null cloud_type_rarity: CloudType['rarity'] | null
cloud_type_rarity_value: number | null cloud_type_rarity_value: number | null
username: string | null username: string | null
favorite_count: number received_like_count: number
is_favorited: boolean
} }
export interface AdminStats { export interface AdminStats {
+1 -5
View File
@@ -1,12 +1,10 @@
export interface CloudType { export interface CloudType {
id: number id: number
name: string name: string
name_en: string
genus: string genus: string
rarity: 'common' | 'uncommon' | 'rare' rarity: 'common' | 'uncommon' | 'rare'
rarity_value: number rarity_value: number
description: string | null description: string | null
icon_url: string | null
created_at: string created_at: string
} }
@@ -14,12 +12,10 @@ export interface Cloud {
id: string id: string
user_id: string user_id: string
cloud_type_id: number | null cloud_type_id: number | null
custom_cloud_type: string | null
image_url: string image_url: string
thumbnail_url: string | null thumbnail_url: string | null
latitude: number | null latitude: number | null
longitude: number | null longitude: number | null
location_name: string | null
description: string | null description: string | null
captured_at: string | null captured_at: string | null
status: 'pending' | 'approved' | 'rejected' status: 'pending' | 'approved' | 'rejected'
@@ -30,7 +26,7 @@ export interface Cloud {
export interface Profile { export interface Profile {
id: string id: string
username: string username: string
avatar_url: string | null avatar_id: string | null
role: 'user' | 'admin' role: 'user' | 'admin'
is_disabled: boolean is_disabled: boolean
created_at: string created_at: string
+3 -3
View File
@@ -178,18 +178,18 @@ function toAdminCloud(row: ApiCloud): AdminCloud {
id: row.id, id: row.id,
user_id: row.user_id, user_id: row.user_id,
cloud_type_id: row.cloud_type_id, cloud_type_id: row.cloud_type_id,
custom_cloud_type: row.custom_cloud_type, custom_cloud_type: null,
image_url: row.image_url, image_url: row.image_url,
thumbnail_url: row.thumbnail_url, thumbnail_url: row.thumbnail_url,
latitude: row.latitude, latitude: row.latitude,
longitude: row.longitude, longitude: row.longitude,
location_name: row.location_name, location_name: null,
description: row.description, description: row.description,
captured_at: row.captured_at, captured_at: row.captured_at,
created_at: row.created_at, created_at: row.created_at,
status: row.status, status: row.status,
is_hidden: row.is_hidden, is_hidden: row.is_hidden,
cloudTypeName: row.cloud_type_name || row.custom_cloud_type || '未知云型', cloudTypeName: row.cloud_type_name || '未知云型',
cloudTypeRarity: row.cloud_type_rarity || 'common', cloudTypeRarity: row.cloud_type_rarity || 'common',
username: row.username || '匿名用户', username: row.username || '匿名用户',
} }
+1 -1
View File
@@ -90,7 +90,7 @@ watch(() => route.params.id, () => loadPage())
<div class="absolute bottom-7 left-7 right-7 text-white"> <div class="absolute bottom-7 left-7 right-7 text-white">
<span class="border border-white/35 bg-slate-950/25 px-2.5 py-1 text-xs backdrop-blur">稀有度 {{ cloudType.rarity_value }}</span> <span class="border border-white/35 bg-slate-950/25 px-2.5 py-1 text-xs backdrop-blur">稀有度 {{ cloudType.rarity_value }}</span>
<h1 class="mt-4 text-4xl font-bold">{{ cloudType.name }}</h1> <h1 class="mt-4 text-4xl font-bold">{{ cloudType.name }}</h1>
<p class="mt-2 text-white/80">{{ cloudType.name_en || '未提供云属名称' }}</p> <p class="mt-2 text-white/80">{{ cloudType.genus || '未提供云属名称' }}</p>
</div> </div>
</div> </div>
<div class="p-8"> <div class="p-8">
+1 -1
View File
@@ -44,7 +44,7 @@ onMounted(() => encyclopediaStore.fetchCloudTypes())
<p class="oc-section-label">Cloud Genus</p> <p class="oc-section-label">Cloud Genus</p>
<h2 class="mt-2 text-xl font-bold text-slate-900">{{ cloudType.name }}</h2> <h2 class="mt-2 text-xl font-bold text-slate-900">{{ cloudType.name }}</h2>
<p class="mt-1 font-mono text-xs uppercase tracking-[0.12em] text-slate-500"> <p class="mt-1 font-mono text-xs uppercase tracking-[0.12em] text-slate-500">
{{ cloudType.name_en || '未提供云属名称' }} {{ cloudType.genus || '未提供云属名称' }}
</p> </p>
</div> </div>
<span class="shrink-0 border border-slate-200 bg-slate-50 px-2.5 py-1 text-xs font-medium text-slate-600"> <span class="shrink-0 border border-slate-200 bg-slate-50 px-2.5 py-1 text-xs font-medium text-slate-600">
+1 -6
View File
@@ -17,10 +17,8 @@ interface GalleryCloud {
id: string id: string
user_id: string user_id: string
cloud_type_id: number | null cloud_type_id: number | null
custom_cloud_type: string | null
image_url: string image_url: string
thumbnail_url: string | null thumbnail_url: string | null
location_name: string | null
description: string | null description: string | null
latitude: number | null latitude: number | null
longitude: number | null longitude: number | null
@@ -88,10 +86,8 @@ function toGalleryCloud(row: ApiCloud) {
id: row.id, id: row.id,
user_id: row.user_id, user_id: row.user_id,
cloud_type_id: row.cloud_type_id, cloud_type_id: row.cloud_type_id,
custom_cloud_type: row.custom_cloud_type,
image_url: row.image_url, image_url: row.image_url,
thumbnail_url: row.thumbnail_url, thumbnail_url: row.thumbnail_url,
location_name: row.location_name,
description: row.description, description: row.description,
latitude: row.latitude, latitude: row.latitude,
longitude: row.longitude, longitude: row.longitude,
@@ -99,7 +95,7 @@ function toGalleryCloud(row: ApiCloud) {
created_at: row.created_at, created_at: row.created_at,
status: row.status, status: row.status,
is_hidden: row.is_hidden, is_hidden: row.is_hidden,
cloudTypeName: row.cloud_type_name || row.custom_cloud_type || '未知', cloudTypeName: row.cloud_type_name || '未知',
cloudTypeRarity: row.cloud_type_rarity_value ?? 0, cloudTypeRarity: row.cloud_type_rarity_value ?? 0,
username: row.username || '匿名', username: row.username || '匿名',
} satisfies GalleryCloud } satisfies GalleryCloud
@@ -554,7 +550,6 @@ onUnmounted(() => {
<MiniLocationMap <MiniLocationMap
:latitude="selectedCloud.latitude" :latitude="selectedCloud.latitude"
:longitude="selectedCloud.longitude" :longitude="selectedCloud.longitude"
:location-name="selectedCloud.location_name"
/> />
<template v-if="selectedCloudIsMine" #actions> <template v-if="selectedCloudIsMine" #actions>
+1 -4
View File
@@ -16,7 +16,6 @@ interface CloudMarkerData {
longitude: number longitude: number
imageUrl: string imageUrl: string
thumbnailUrl: string | null thumbnailUrl: string | null
locationName: string | null
description: string | null description: string | null
cloudTypeName: string cloudTypeName: string
rarity: number rarity: number
@@ -412,9 +411,8 @@ function toCloudMarker(row: ApiCloud): CloudMarkerData {
longitude: row.longitude as number, longitude: row.longitude as number,
imageUrl: row.image_url, imageUrl: row.image_url,
thumbnailUrl: row.thumbnail_url, thumbnailUrl: row.thumbnail_url,
locationName: row.location_name,
description: row.description, description: row.description,
cloudTypeName: row.cloud_type_name || row.custom_cloud_type || '未知', cloudTypeName: row.cloud_type_name || '未知',
rarity: row.cloud_type_rarity_value ?? 0, rarity: row.cloud_type_rarity_value ?? 0,
username: row.username || '匿名', username: row.username || '匿名',
capturedAt: row.captured_at || row.created_at, capturedAt: row.captured_at || row.created_at,
@@ -863,7 +861,6 @@ onUnmounted(() => {
<MiniLocationMap <MiniLocationMap
:latitude="previewCloud.latitude" :latitude="previewCloud.latitude"
:longitude="previewCloud.longitude" :longitude="previewCloud.longitude"
:location-name="previewCloud.locationName"
/> />
</ImageDetailModal> </ImageDetailModal>
+2 -3
View File
@@ -439,8 +439,8 @@ watch(selectedUploadDate, async newValue => {
class="profile-avatar flex h-20 w-20 shrink-0 items-center justify-center border border-slate-300 bg-[linear-gradient(135deg,#eef9f3_0%,#d9efe3_100%)] text-3xl font-bold text-slate-900" class="profile-avatar flex h-20 w-20 shrink-0 items-center justify-center border border-slate-300 bg-[linear-gradient(135deg,#eef9f3_0%,#d9efe3_100%)] text-3xl font-bold text-slate-900"
> >
<img <img
v-if="profileData?.avatar_url" v-if="profileData?.avatar_id"
:src="profileData.avatar_url" :src="profileData.avatar_id"
:alt="profileData.username" :alt="profileData.username"
class="h-full w-full object-cover" class="h-full w-full object-cover"
/> />
@@ -756,7 +756,6 @@ watch(selectedUploadDate, async newValue => {
<MiniLocationMap <MiniLocationMap
:latitude="selectedCloud.latitude" :latitude="selectedCloud.latitude"
:longitude="selectedCloud.longitude" :longitude="selectedCloud.longitude"
:location-name="selectedCloud.location_name"
/> />
<template v-if="isOwnProfile" #actions> <template v-if="isOwnProfile" #actions>
+1 -1
View File
@@ -279,7 +279,7 @@ onUnmounted(() => {
> >
<option value="" disabled>请选择</option> <option value="" disabled>请选择</option>
<option v-for="ct in cloudsStore.cloudTypes" :key="ct.id" :value="ct.id"> <option v-for="ct in cloudsStore.cloudTypes" :key="ct.id" :value="ct.id">
{{ ct.name }}({{ ct.name_en }}) {{ ct.name }}{{ ct.genus ? '(' + ct.genus + ')' : '' }}
</option> </option>
</select> </select>
<p v-if="activeItem.errors.cloudCategory" class="oc-field-error">{{ activeItem.errors.cloudCategory }}</p> <p v-if="activeItem.errors.cloudCategory" class="oc-field-error">{{ activeItem.errors.cloudCategory }}</p>