[Human] chore(project): 初始化导入 91dj H5 客户端项目代码

从本地工程首次入库;.npmrc(含私有 npm 源 token)与 tsbuildinfo 已加入 .gitignore,不随仓库提交。
This commit is contained in:
谢宇宁
2026-10-08 14:22:35 +07:00
commit eada71e211
1421 changed files with 144498 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
import { needsImageDecrypt, parseImageExtra, toBusinessImagePath } from '@/utils/im/message';
/** 消息中心私信列表的一行数据(纯 SDK 会话映射) */
export type PrivateSessionItem = {
id: string;
/** 业务 uid;列表由 SDK 驱动时为 0,进会话前用 /im/user-id 反查 */
userId: number;
/** 对端 IM userId(= conv.receiverId) */
friendImUserId: number;
userName: string;
userAvatar: string;
preContent: string;
createdAt: string | number;
noReadNum: number;
};
/** SDK conversation.subscribeList 推送的会话结构 */
export type SdkConversation = {
type?: string;
receiverId?: number | string;
unreadCount?: number;
extra?: string;
lastMessageTime?: number;
updatedAt?: number;
createdAt?: number;
content?: string;
chatMessageType?: string | number;
user?: {
id?: number;
nickName?: string;
nickname?: string;
name?: string;
avatar?: string;
portrait?: string;
imgUrl?: string;
};
remark?: string;
};
// —— 展示文案 ——
/** blob/data 本地地址、http(s) 公网图无需再走 imgDecypt */
export function isPublicImageUrl(url?: string): boolean {
return !needsImageDecrypt(url);
}
export function formatSessionPreview(content?: string): string {
const text = (content || '').trim();
if (!text) return '暂无消息';
if (/^\[图片\]|^图片$|image/i.test(text)) return '[图片]';
return text;
}
export function formatSessionListTime(date: string | number): string {
if (date == null || date === '') return '';
const d = typeof date === 'number' ? new Date(date) : new Date(date);
if (Number.isNaN(d.getTime()) || d.getFullYear() === 1) return '';
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
}
export function buildMessagePreview(msg: {
content?: string;
chatMessageType?: string | number;
extra?: unknown;
}): string {
const type = msg.chatMessageType;
if (type === 'IMAGE' || type === 2 || parseImageExtra(msg.extra)) return '[图片]';
return (msg.content || '').trim() || '暂无消息';
}
// —— SDK 会话 → 列表行 ——
function toTimestamp(value: string | number | undefined): number {
if (value == null || value === '') return 0;
const ts = typeof value === 'number' ? value : new Date(value).getTime();
return Number.isFinite(ts) ? ts : 0;
}
function isDirectConversation(conv: SdkConversation): boolean {
if (conv.type && conv.type !== 'direct') return false;
const imId = Number(conv.receiverId);
return Number.isFinite(imId) && imId > 0;
}
/** IM 快照偶发幽灵会话(user 资料整体缺失),无法渲染也无法跳转,不展示 */
function hasUserProfile(conv: SdkConversation): boolean {
const user = conv.user;
return !!(user?.nickName || user?.nickname || user?.name || user?.avatar || user?.portrait || user?.imgUrl);
}
function toSessionItem(conv: SdkConversation): PrivateSessionItem {
const imId = Number(conv.receiverId);
const user = conv.user;
return {
id: String(imId),
userId: 0,
friendImUserId: imId,
userName: user?.nickName || user?.nickname || user?.name || conv.remark || '',
userAvatar: toBusinessImagePath(user?.avatar || user?.portrait || user?.imgUrl || ''),
preContent: parseImageExtra(conv.extra)
? '[图片]'
: buildMessagePreview({
content: conv.content,
chatMessageType: conv.chatMessageType,
extra: conv.extra,
}),
createdAt: conv.lastMessageTime ?? conv.updatedAt ?? conv.createdAt ?? '',
noReadNum: Number(conv.unreadCount ?? 0),
};
}
/** SDK 会话快照 → 私信列表(仅单聊;按最后消息时间倒序) */
export function mapSdkConversations(conversations: SdkConversation[]): PrivateSessionItem[] {
const seen = new Set<number>();
const list: PrivateSessionItem[] = [];
for (const conv of conversations) {
if (!isDirectConversation(conv) || !hasUserProfile(conv)) continue;
const imId = Number(conv.receiverId);
if (seen.has(imId)) continue;
seen.add(imId);
list.push(toSessionItem(conv));
}
return list.sort((a, b) => toTimestamp(b.createdAt) - toTimestamp(a.createdAt));
}