feat: 新增皮肤站管理台聊天室房间管理
Sync source to CNB / Sync Git ref (push) Skipped

- 房间列表:头像、名称、可见性、类型、语音标记、前置权限、创建时间
- 创建/编辑房间:名称、简介、公开或私有、普通或聚落、前置条件判定与所需权限
- 房间头像:上传、更换、移除,支持 PNG/JPEG/WebP,5 MB 限制
- 删除房间带二次确认
- 新增 RoomAvatar 与 RoomAvatarEditor 组件,头像按附件 id 缓存 Blob URL
- 补充聊天室相关 API:房间列表、创建、更新、删除、头像更新、附件上传、头像获取
- 管理台导航接入聊天室入口
- 中英文各补 37 个文案
This commit is contained in:
AxTps committed 2026-10-18 12:13:34 +08:00
1 parent 87de306245
commit ef1435bd31
8 files changed
+968 -2

No files matched your search

@@ -2071,3 +2071,125 @@ export function skinSiteUpdateMultiModInfo(
body: mods,
})
}
// ---------------------------------------------------------------------------
// Chat
// ---------------------------------------------------------------------------
export type SkinSiteChatRoomVisibility = 'PUBLIC' | 'PRIVATE'
export type SkinSiteChatRoomKind = 'STANDARD' | 'SETTLEMENT'
export type SkinSiteChatPrerequisiteMode = 'ANY' | 'ALL'
export interface SkinSiteChatRoomInfo {
id: number
name: string
description: string
visibility: SkinSiteChatRoomVisibility
joined: boolean
invited: boolean
manager: boolean
createdAt: number
kind: SkinSiteChatRoomKind
avatarAttachmentId?: number | null
voiceEnabled: boolean
voiceAvailable: boolean
voiceParticipantCount: number
voicePublisherCount: number
voicePublisherLimit: number
voiceParticipantLimit: number
voiceListenerLimit: number
prerequisiteMode: SkinSiteChatPrerequisiteMode
prerequisitePermissions: string[]
prerequisiteEligible: boolean
}
export interface SkinSiteChatUploadInfo {
token: string
attachment: {
id: number
kind: 'IMAGE' | 'FILE'
name: string
size: number
}
}
export interface SkinSiteChatRoomCreateInput {
name: string
description: string
visibility: SkinSiteChatRoomVisibility
kind?: SkinSiteChatRoomKind
prerequisiteMode: SkinSiteChatPrerequisiteMode
prerequisitePermissions: string[]
}
export interface SkinSiteChatRoomUpdateInput {
name: string
description: string
visibility: SkinSiteChatRoomVisibility
prerequisiteMode: SkinSiteChatPrerequisiteMode
prerequisitePermissions: string[]
}
export function skinSiteChatRoomList(): Promise<SkinSiteResponse<SkinSiteChatRoomInfo[]>> {
return skinSiteRequest<SkinSiteChatRoomInfo[]>('chat/admin/rooms')
}
export function skinSiteChatRoomCreate(
data: SkinSiteChatRoomCreateInput,
): Promise<SkinSiteResponse<SkinSiteChatRoomInfo>> {
return skinSiteRequest<SkinSiteChatRoomInfo>('chat/admin/rooms', {
method: 'POST',
body: data,
})
}
export function skinSiteChatRoomUpdate(
id: number,
data: SkinSiteChatRoomUpdateInput,
): Promise<SkinSiteResponse<SkinSiteChatRoomInfo>> {
return skinSiteRequest<SkinSiteChatRoomInfo>('chat/rooms/' + id, {
method: 'PUT',
body: data,
})
}
export function skinSiteChatRoomDelete(id: number): Promise<SkinSiteResponse<string>> {
return skinSiteRequest<string>('chat/admin/rooms/' + id, { method: 'DELETE' })
}
export function skinSiteChatRoomAvatarUpdate(
roomId: number,
data: { imageToken?: string; removeImage?: boolean },
): Promise<SkinSiteResponse<SkinSiteChatRoomInfo>> {
return skinSiteRequest<SkinSiteChatRoomInfo>('chat/rooms/' + roomId + '/avatar', {
method: 'PUT',
body: data,
})
}
export function skinSiteChatAttachmentUpload(
file: File,
): Promise<SkinSiteResponse<SkinSiteChatUploadInfo>> {
const form = new FormData()
form.append('file', file, file.name)
return skinSiteRequest<SkinSiteChatUploadInfo>('chat/attachments', {
method: 'POST',
form,
})
}
export async function skinSiteChatRoomAvatarFetch(roomId: number): Promise<Blob | null> {
const url = `${SKIN_SITE_ORIGIN}${REQUEST_TYPE_PATH.starlight}/chat/rooms/${roomId}/avatar`
const headers: Record<string, string> = { Accept: 'image/*' }
const token = getSkinSiteToken()
if (token) headers['Authorization'] = `Bearer ${token}`
try {
const response = await tauriFetch(url, { method: 'GET', headers })
if (!response.ok) return null
const type = response.headers.get('content-type') ?? ''
if (type.includes('application/json') || type.includes('text/')) return null
return await response.blob()
} catch {
return null
}
}