Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

708
pages/info/types.uts Normal file
View File

@@ -0,0 +1,708 @@
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
}