Files
akmon/pages/info/types.uts
2026-01-20 08:04:15 +08:00

709 lines
20 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { tt } from '@/utils/i18nfun.uts'
// 获取语言本地化 key如 mt.language.zh-CN用于 $t()
export const getLanguageDisplayNameKey = (code: string): string => {
if (code === 'zh-CN') return 'mt.language.zh-CN'
if (code === 'zh-TW') return 'mt.language.zh-TW'
if (code === 'en-US') return 'mt.language.en-US'
if (code === 'ja-JP') return 'mt.language.ja-JP'
if (code === 'ko-KR') return 'mt.language.ko-KR'
if (code === 'fr-FR') return 'mt.language.fr-FR'
if (code === 'de-DE') return 'mt.language.de-DE'
if (code === 'es-ES') return 'mt.language.es-ES'
return code
}
// 基础数据类型 - 全部使用强类型定义,便于类型推断和类型安全
export type InfoContent = {
id: string
title?: string
summary?: string | null
content?: string
author?: string
trans_data?:TranslationData
published_at?: string
quality_score: number
view_count?: number
like_count?: number
share_count?: number
category_id?: string
category_name?: CategoryTranslation
category_name_text?: string // 兼容性字段,用于简单的字符串显示
original_language?: string
source_url?: string | null
tags?: string[] | null
created_at?: string
updated_at?: string
is_liked?: boolean
loading?: boolean // UI状态字段用于加载状态显示
// 扩展字段 - 支持视频、音频和图集模式
raw_content_id?: string | null
keywords?: string[] | null
entities?: any | null // jsonb
sentiment_score?: number | null
readability_score?: number | null
credibility_score?: number | null
comment_count?: number | null
featured_until?: string | null
status?: string | null
ai_processed_at?: string | null
favorite_count?: number | null
is_featured?: boolean | null
content_type?: string | null
// 视频相关字段
video_url?: string | null
video_duration?: number | null
video_poster?: string | null
video_width?: number | null
video_height?: number | null
video_size?: number | null
video_format?: string | null
video_quality?: string | null
// 音频相关字段
audio_url?: string | null
audio_duration?: number | null
audio_size?: number | null
audio_format?: string | null
audio_bitrate?: number | null
audio_sample_rate?: number | null
audio_cover?: string | null
// 图片相关字段
image_url?: string | null
image_width?: number | null
image_height?: number | null
image_size?: number | null
image_format?: string | null
image_quality?: string | null
image_alt_text?: string | null
images?: any | null // jsonb - 图集模式
// 多媒体设置
allow_danmu?: boolean | null
allow_download?: boolean | null
media_metadata?: any | null // jsonb
// 序列号字段
cid?: number | null
}
export type TranslationData = {
id: string
content_id: string
language_id: string
title: string
content: string
summary: string | null
human_verified: boolean
created_at: string
updated_at: string
}
// 新增CategoryTranslation 类型
export type CategoryTranslation = {
name: string
}
// 新增CategoryData 类型,严格对应 Supabase 返回结构
export type CategoryData = {
id: string
name_key: string
parent_id: string | null
level?: number
ai_keywords?: string[]
confidence_threshold?: number
sort_order?: number
is_active?: boolean
created_at?: string
updated_at?: string
translations?: CategoryTranslation[]
}
// 新增专题相关类型
export type TopicData = {
id: string
title: string
description: string
created_by: string
is_active: boolean
content_count: number
created_at: string
updated_at: string
}
export type TopicContentData = {
id: string
topic_id: string
content_id: string
display_order: number
created_at: string
updated_at: string
is_liked?:boolean
}
export type TopicTimelineData = {
id: string
topic_id: string
event: string
event_time: string
created_at: string
updated_at: string
}
// 评论系统相关类型
export type CommentData = {
id: string
content_id: string
user_id: string
user_name: string
content: string
like_count: number
reply_count: number
status: string
created_at: string
updated_at: string
is_liked?: boolean
level?: number // 多级评论层级0为主评论1为一级回复依此类推
is_author?: boolean // 是否为当前用户本人评论
}
export type CommentReplyData = {
id: string
comment_id: string
user_id: string
user_name: string
content: string
created_at: string
updated_at: string
is_liked?:boolean
}
export type CommentReactionData = {
id: string
comment_id: string
user_id: string
reaction_type: string
created_at: string
is_liked?:boolean
}
export type UserBehaviorData = {
id: string
user_id: string
content_id: string
behavior_type: string
behavior_data: any
duration_seconds: number | null
scroll_percentage: number | null
device_type: string
source: string
session_id: string
ip_address: string
user_agent: string
created_at: string
}
export type RecommendationData = {
id: string
user_id: string
content_id: string
algorithm_type: string
score: number
reason: string
position: number
shown_at: string
clicked_at: string
feedback_score: number
feedback_reason: string
created_at: string
}
export type ChatSessionData = {
id: string
user_id: string
session_name: string
language: string
context: any
ai_model: string
total_messages: number
total_tokens: number
cost_usd: number
last_message_at: string
is_active: boolean
created_at: string
updated_at: string
}
export type ChatMessageData = {
id: string
session_id: string
message_type: string
content: string
intent?: string
attachments?: any
ai_provider?: string
tokens_used?: number
processing_time_ms?: number
cost_usd?: number
feedback_score?: number
feedback_reason?: string
created_at?: string
}
export type LanguageData = {
id: string
code: string
name: string
native_name: string
is_active: boolean
}
export type UserSettingsData = {
id: string
user_id: string
preferred_languages: string[]
preferred_categories: string[]
reading_mode: string
font_size: string
auto_translate: boolean
notification_enabled: boolean
created_at: string
updated_at: string
}
export type SearchHistoryData = {
id: string
user_id: string
keyword: string
searched_at: string
}
// 已合并到 TranslationData避免重复定义
// export type Translation = TranslationData
export type Topic = {
id: string
title: string
description: string
topic_type?: string
status?: string
cover_image?: string
created_by?: string
is_active?: boolean
content_count?: number
view_count?: number
created_at?: string
updated_at?: string
}
export type Comment = {
id: string
content_id: string
user_id: string
user_name: string
content: string
like_count: number
reply_count: number
created_at: string
updated_at: string
}
export type Language = {
id: string
code: string
name: string
native_name: string
is_active: boolean
}
// 状态和UI类型 - 与template交互的变量使用1维变量
export type PageState = {
loading: boolean
error: string | null
currentPage: number
pageSize: number
total: number
}
export type StatsData = {
total_contents: number
published_contents: number
trending_contents: number
avg_quality_score: string
}
export type ResponsiveState = {
isLargeScreen: boolean
isSmallScreen: boolean
screenWidth: number
cardColumns: number
}
// 选择器选项类型 - UTS Android支持的简单类型
export type PickerOption = {
value: string
text: string
}
export type SortOption = {
column: string
ascending: boolean
}
// 表单数据类型 - 避免复杂嵌套,使用简单类型
export type ContentFormData = {
title: string
content: string
summary: string
category_id: string
tags: string // 改为字符串,用逗号分隔
source_url: string
author: string
content_type?: string
// 视频相关字段
video_url?: string
video_duration?: number
video_poster?: string
video_width?: number
video_height?: number
video_quality?: string
// 音频相关字段
audio_url?: string
audio_duration?: number
audio_cover?: string
audio_format?: string
// 图片相关字段
image_url?: string
image_alt_text?: string
images?: string // JSON字符串存储图集数据
// 多媒体设置
allow_danmu?: boolean
allow_download?: boolean
}
export type TranslationFormData = {
content_id: string
language_id: string
title: string
content: string
summary: string
}
// 筛选器类型 - 使用简单字符串类型
export type ContentFilterData = {
category_id: string | null
language: string | null
status: string
quality_min: string | null
date_from: string | null
date_to: string | null
search_text: string | null
date_range?: string | null
content_type?: string | null // 新增:按内容类型筛选
is_featured?: boolean | null // 新增:是否精选
has_video?: boolean | null // 新增:是否包含视频
has_audio?: boolean | null // 新增:是否包含音频
has_images?: boolean | null // 新增:是否包含图片
}
// 聊天相关类型
export type ChatState = {
isTyping: boolean
currentSession: string |null
messageCount: number
}
// 用户偏好类型 - 使用字符串存储数组数据
export type UserPreferences = {
preferred_languages: string // JSON字符串存储数组
preferred_categories: string // JSON字符串存储数组
reading_mode: string // 'light', 'dark', 'auto'
font_size: string // 'small', 'medium', 'large'
auto_translate: boolean
notification_enabled: boolean
}
// 常量定义 - 内容状态
export const CONTENT_STATUS = {
DRAFT: 'draft',
PUBLISHED: 'published',
ARCHIVED: 'archived',
DELETED: 'deleted'
}
// 内容类型常量 - 支持多媒体
export const CONTENT_TYPES = {
TEXT: 'text', // 纯文本
IMAGE: 'image', // 图片
VIDEO: 'video', // 视频
AUDIO: 'audio', // 音频
GALLERY: 'gallery', // 图集
MIXED: 'mixed' // 混合内容
}
// 通用选项类型(用于 value/text 结构的所有 option
export type OptionItem = {
value: string
text: string
}
// 视频质量选项
export const VIDEO_QUALITY_OPTIONS: Array<OptionItem> = [
{ value: '4K', text: 'mt.video.quality.4k' },
{ value: '1080P', text: 'mt.video.quality.1080p' },
{ value: '720P', text: 'mt.video.quality.720p' },
{ value: '480P', text: 'mt.video.quality.480p' },
{ value: '360P', text: 'mt.video.quality.360p' }
]
// 音频格式选项
export const AUDIO_FORMAT_OPTIONS: Array<OptionItem> = [
{ value: 'mp3', text: 'mt.audio.format.mp3' },
{ value: 'wav', text: 'mt.audio.format.wav' },
{ value: 'flac', text: 'mt.audio.format.flac' },
{ value: 'aac', text: 'mt.audio.format.aac' },
{ value: 'm4a', text: 'mt.audio.format.m4a' }
]
// 行为类型常量
export const BEHAVIOR_TYPES = {
VIEW: 'view',
LIKE: 'like',
SHARE: 'share',
COMMENT: 'comment',
SAVE: 'save',
CLICK: 'click'
}
// 消息类型常量
export const MESSAGE_TYPES = {
USER: 'user',
ASSISTANT: 'assistant',
SYSTEM: 'system'
}
// 专题类型常量
export const TOPIC_TYPES: Array<OptionItem> = [
{ value: 'breaking', text: 'mt.topicType.breaking' },
{ value: 'trending', text: 'mt.topicType.trending' },
{ value: 'series', text: 'mt.topicType.series' },
{ value: 'analysis', text: 'mt.topicType.analysis' },
{ value: 'guide', text: 'mt.topicType.guide' },
{ value: 'interview', text: 'mt.topicType.interview' },
{ value: 'report', text: 'mt.topicType.report' },
{ value: 'timeline', text: 'mt.topicType.timeline' }
]
// 专题状态常量
export const TOPIC_STATUS = {
DRAFT: 'draft',
ACTIVE: 'active',
FEATURED: 'featured',
ARCHIVED: 'archived',
CLOSED: 'closed'
}
// 评论状态常量
export const COMMENT_STATUS = {
ACTIVE: 'active',
HIDDEN: 'hidden',
DELETED: 'deleted',
PENDING_REVIEW: 'pending_review',
REJECTED: 'rejected'
}
// 评论类型常量
export const COMMENT_TYPES = {
CONTENT: 'content', // 内容评论
TOPIC: 'topic', // 专题评论
REPLY: 'reply' // 回复评论
}
// 评论排序选项
export const COMMENT_SORT_OPTIONS: Array<OptionItem> = [
{ value: 'created_at_desc', text: 'mt.comment.sort.latest' },
{ value: 'created_at_asc', text: 'mt.comment.sort.earliest' },
{ value: 'like_count_desc', text: 'mt.comment.sort.mostLiked' },
{ value: 'reply_count_desc', text: 'mt.comment.sort.mostReplied' }
]
// 评论举报类型
export const COMMENT_REPORT_TYPES: Array<OptionItem> = [
{ value: 'spam', text: 'mt.comment.report.spam' },
{ value: 'inappropriate', text: 'mt.comment.report.inappropriate' },
{ value: 'harassment', text: 'mt.comment.report.harassment' },
{ value: 'misinformation', text: 'mt.comment.report.misinformation' },
{ value: 'copyright', text: 'mt.comment.report.copyright' },
{ value: 'other', text: 'mt.comment.report.other' }
]
// 语言选项常量
export const LANGUAGE_OPTIONS: Array<LanguageData> = [
{ id: 'zh-CN', code: 'zh-CN', name: 'mt.language.zh-CN', native_name: 'mt.language.zh-CN', is_active: true },
{ id: 'zh-TW', code: 'zh-TW', name: 'mt.language.zh-TW', native_name: 'mt.language.zh-TW', is_active: true },
{ id: 'en-US', code: 'en-US', name: 'mt.language.en-US', native_name: 'mt.language.en-US', is_active: true },
{ id: 'ja-JP', code: 'ja-JP', name: 'mt.language.ja-JP', native_name: 'mt.language.ja-JP', is_active: true },
{ id: 'ko-KR', code: 'ko-KR', name: 'mt.language.ko-KR', native_name: 'mt.language.ko-KR', is_active: true },
{ id: 'fr-FR', code: 'fr-FR', name: 'mt.language.fr-FR', native_name: 'mt.language.fr-FR', is_active: true },
{ id: 'de-DE', code: 'de-DE', name: 'mt.language.de-DE', native_name: 'mt.language.de-DE', is_active: true },
{ id: 'es-ES', code: 'es-ES', name: 'mt.language.es-ES', native_name: 'mt.language.es-ES', is_active: true }
]
export const SORT_OPTIONS: Array<OptionItem> = [
{ value: 'published_at_desc', text: 'mt.sort.latest' },
{ value: 'published_at_asc', text: 'mt.sort.earliest' },
{ value: 'quality_score_desc', text: 'mt.sort.highestScore' },
{ value: 'view_count_desc', text: 'mt.sort.mostViewed' },
{ value: 'like_count_desc', text: 'mt.sort.mostLiked' },
{ value: 'share_count_desc', text: 'mt.sort.mostShared' }
]
export const getCommentStatusTextKey = (status: string): string => {
console.log(status,COMMENT_STATUS.HIDDEN)
if (status === COMMENT_STATUS["ACTIVE"]) return 'mt.comment.status.active'
if (status === COMMENT_STATUS.HIDDEN) return 'mt.comment.status.hidden'
if (status === COMMENT_STATUS.DELETED) return 'mt.comment.status.deleted'
if (status === COMMENT_STATUS.PENDING_REVIEW) return 'mt.comment.status.pending'
if (status === COMMENT_STATUS.REJECTED) return 'mt.comment.status.rejected'
return 'mt.comment.status.unknown'
}
// 格式化相对时间,返回 i18n key
export const formatRelativeTimeKey = (dateString: string | null): string => {
if (dateString == null || dateString === '') return ''
const now = new Date()
const date = new Date(dateString)
const diff = now.getTime() - date.getTime()
const seconds = Math.floor(diff / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
const days = Math.floor(hours / 24)
if (days > 0) return days + tt('mt.time.daysAgo')
if (hours > 0) return hours + tt('mt.time.hoursAgo')
if (minutes > 0) return minutes + tt('mt.time.minutesAgo')
return tt('mt.time.justNow')
}
// 语言显示名称,返回本地化字符串
export const getLanguageDisplayName = (code: string): string => {
const map = {
'zh-CN': 'mt.language.zh-CN',
'zh-TW': 'mt.language.zh-TW',
'en-US': 'mt.language.en-US',
'ja-JP': 'mt.language.ja-JP',
'ko-KR': 'mt.language.ko-KR',
'fr-FR': 'mt.language.fr-FR',
'de-DE': 'mt.language.de-DE',
'es-ES': 'mt.language.es-ES'
}
const key = map[code] ?? code
return tt(key)
}
// 质量分数对应颜色,返回颜色字符串(如需 className 可调整)
export const getQualityScoreColor = (score: number): string => {
if (score >= 90) return '#4CAF50' // excellent - green
if (score >= 75) return '#8BC34A' // good - light green
if (score >= 60) return '#FFC107' // normal - amber
return '#F44336' // poor - red
}
// 质量分数对应文本,返回 i18n key
export const getQualityScoreText = (score: number): string => {
if (score >= 90) return tt('mt.quality.excellent')
if (score >= 75) return tt('mt.quality.good')
if (score >= 60) return tt('mt.quality.normal')
return tt('mt.quality.poor')
}
// 专题类型显示名称,返回本地化字符串
export const getTopicTypeDisplayName = (typeCode: string): string => {
const typeItem = TOPIC_TYPES.find(item => item.value === typeCode);
return typeItem != null ? tt(typeItem.text) : typeCode;
};
// 专题状态对应颜色,返回颜色字符串
export const getTopicStatusColor = (status: string): string => {
if (status === TOPIC_STATUS.FEATURED) return '#FF6B35' // featured - orange
if (status === TOPIC_STATUS.ACTIVE) return '#4CAF50' // active - green
if (status === TOPIC_STATUS.DRAFT) return '#9E9E9E' // draft - gray
if (status === TOPIC_STATUS.ARCHIVED) return '#607D8B' // archived - blue gray
if (status === TOPIC_STATUS.CLOSED) return '#F44336' // closed - red
return '#9E9E9E' // default - gray
}
// 获取内容类型显示名称,返回本地化字符串
export const getContentTypeDisplayName = (contentType: string | null): string => {
if (contentType === CONTENT_TYPES.TEXT) return tt('mt.content.type.text')
if (contentType === CONTENT_TYPES.IMAGE) return tt('mt.content.type.image')
if (contentType === CONTENT_TYPES.VIDEO) return tt('mt.content.type.video')
if (contentType === CONTENT_TYPES.AUDIO) return tt('mt.content.type.audio')
if (contentType === CONTENT_TYPES.GALLERY) return tt('mt.content.type.gallery')
if (contentType === CONTENT_TYPES.MIXED) return tt('mt.content.type.mixed')
return tt('mt.content.type.text') // 默认为文本
}
// 获取内容类型对应图标
export const getContentTypeIcon = (contentType: string | null): string => {
if (contentType === CONTENT_TYPES.TEXT) return 'text-outline'
if (contentType === CONTENT_TYPES.IMAGE) return 'image-outline'
if (contentType === CONTENT_TYPES.VIDEO) return 'videocam-outline'
if (contentType === CONTENT_TYPES.AUDIO) return 'volume-high-outline'
if (contentType === CONTENT_TYPES.GALLERY) return 'images-outline'
if (contentType === CONTENT_TYPES.MIXED) return 'layers-outline'
return 'text-outline' // 默认图标
}
// 格式化文件大小
export const formatFileSize = (bytes: number | null): string => {
if (bytes == null || bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
// 格式化时长(秒转为 mm:ss 或 hh:mm:ss
export const formatDuration = (seconds: number | null): string => {
if (seconds == null || seconds === 0) return '00:00'
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const remainingSeconds = Math.floor(seconds % 60)
if (hours > 0) {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`
} else {
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`
}
}
// 检查内容是否有多媒体
export const hasMultimedia = (content: InfoContent): boolean => {
return !!(content.video_url || content.audio_url || content.image_url || (content.images && content.images !== null))
}
// 获取主要媒体类型
export const getPrimaryMediaType = (content: InfoContent): string => {
if (content.video_url) return CONTENT_TYPES.VIDEO
if (content.audio_url) return CONTENT_TYPES.AUDIO
if (content.images && content.images !== null) return CONTENT_TYPES.GALLERY
if (content.image_url) return CONTENT_TYPES.IMAGE
return CONTENT_TYPES.TEXT
}