feat(pc-client): 第二批登录、会话、聊天与添加同事界面按规范收口
Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
d1657ea29f
commit
a44537da4e
@@ -1,47 +1,85 @@
|
||||
import { useRequest } from "ahooks";
|
||||
import { FriendUserItem } from "@openim/wasm-client-sdk/lib/types/entity";
|
||||
import axios from "axios";
|
||||
import { Empty, Spin } from "antd";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { GroupedVirtuoso, GroupedVirtuosoHandle } from "react-virtuoso";
|
||||
import { GroupedVirtuoso } from "react-virtuoso";
|
||||
|
||||
import { useContactStore } from "@/store";
|
||||
import { formatContactsByWorker } from "@/utils/contactsFormat";
|
||||
import { emit } from "@/utils/events";
|
||||
import { getIMToken } from "@/utils/storage";
|
||||
|
||||
import AlphabetIndex from "./AlphabetIndex";
|
||||
import FriendListItem from "./FriendListItem";
|
||||
|
||||
type DirInfo = { title: string; department: string };
|
||||
|
||||
export const MyFriends = () => {
|
||||
const { t } = useTranslation();
|
||||
const friendList = useContactStore((state) => state.friendList);
|
||||
const virtuoso = useRef<GroupedVirtuosoHandle>(null);
|
||||
const alphabetRef = useRef<{ updateCurrentLetter: (letter: string) => void }>(null);
|
||||
|
||||
const { data: sectionData, cancel } = useRequest(
|
||||
() => formatContactsByWorker(friendList),
|
||||
{
|
||||
refreshDeps: [friendList],
|
||||
},
|
||||
);
|
||||
const [dirMap, setDirMap] = useState<Record<string, DirInfo>>({});
|
||||
const [dirReady, setDirReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const loadTitles = async () => {
|
||||
try {
|
||||
const token = await getIMToken();
|
||||
if (!token) {
|
||||
setDirReady(true);
|
||||
return;
|
||||
}
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_ACCOUNT_URL}/api/directory`,
|
||||
{
|
||||
params: { keyword: "", limit: 100 },
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
if (cancelled) return;
|
||||
if (data?.code === 0) {
|
||||
const map: Record<string, DirInfo> = {};
|
||||
for (const item of data.data?.items ?? []) {
|
||||
if (item.userID) {
|
||||
map[item.userID] = {
|
||||
title: item.title || "",
|
||||
department: item.department || "",
|
||||
};
|
||||
}
|
||||
}
|
||||
setDirMap(map);
|
||||
}
|
||||
} catch {
|
||||
// 目录不可用时职务行留空、部门归入未分组
|
||||
} finally {
|
||||
if (!cancelled) setDirReady(true);
|
||||
}
|
||||
};
|
||||
void loadTitles();
|
||||
return () => {
|
||||
cancel();
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const scrollToLetter = useCallback(
|
||||
(idx: number) => {
|
||||
const prevNum = sectionData?.groupCounts.slice(0, idx).reduce((a, b) => a + b, 0);
|
||||
console.log(prevNum);
|
||||
|
||||
virtuoso.current?.scrollToIndex({
|
||||
index: prevNum ?? 0,
|
||||
// behavior: "smooth",
|
||||
});
|
||||
},
|
||||
[sectionData?.groupCounts],
|
||||
);
|
||||
const sectionData = useMemo(() => {
|
||||
const ungrouped = t("placeholder.ungrouped");
|
||||
const map = new Map<string, FriendUserItem[]>();
|
||||
for (const friend of friendList) {
|
||||
const dept = dirMap[friend.userID]?.department?.trim() || ungrouped;
|
||||
const list = map.get(dept) ?? [];
|
||||
list.push(friend);
|
||||
map.set(dept, list);
|
||||
}
|
||||
const names = [...map.keys()].sort((a, b) => {
|
||||
if (a === ungrouped) return 1;
|
||||
if (b === ungrouped) return -1;
|
||||
return a.localeCompare(b, "zh");
|
||||
});
|
||||
return {
|
||||
groupCounts: names.map((name) => map.get(name)!.length),
|
||||
indexList: names,
|
||||
totalList: names.flatMap((name) => map.get(name)!),
|
||||
};
|
||||
}, [friendList, dirMap, t]);
|
||||
|
||||
const showUserCard = useCallback((userID: string) => {
|
||||
emit("OPEN_USER_CARD", {
|
||||
@@ -49,63 +87,36 @@ export const MyFriends = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const determineCurrentGroup = (startIndex: number) => {
|
||||
if (!sectionData) return;
|
||||
|
||||
let currentItemIndex = 0;
|
||||
|
||||
for (
|
||||
let groupIndex = 0;
|
||||
groupIndex < sectionData.groupCounts.length;
|
||||
groupIndex++
|
||||
) {
|
||||
const groupItemCount = sectionData.groupCounts[groupIndex];
|
||||
|
||||
if (startIndex < currentItemIndex + groupItemCount) {
|
||||
alphabetRef.current?.updateCurrentLetter(sectionData.indexList[groupIndex]);
|
||||
break;
|
||||
}
|
||||
|
||||
currentItemIndex += groupItemCount;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col overflow-hidden bg-white">
|
||||
<div className="m-5.5 text-base font-extrabold">{t("placeholder.myFriend")}</div>
|
||||
{!sectionData ? (
|
||||
{!dirReady && friendList.length === 0 ? (
|
||||
<Spin />
|
||||
) : !sectionData.groupCounts.length ? (
|
||||
) : !friendList.length ? (
|
||||
<Empty className="mt-[30%]" image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
) : (
|
||||
<div className="ml-4 mt-4 flex-1 overflow-auto pr-4">
|
||||
<AlphabetIndex
|
||||
ref={alphabetRef}
|
||||
indexList={sectionData.indexList}
|
||||
scrollToLetter={scrollToLetter}
|
||||
/>
|
||||
|
||||
<GroupedVirtuoso
|
||||
ref={virtuoso}
|
||||
groupCounts={sectionData.groupCounts}
|
||||
groupContent={(index) => (
|
||||
<div>
|
||||
<div className="bg-white px-3.5 pb-1 text-[13px] text-[var(--sub-text)]">
|
||||
{sectionData.indexList[index]}
|
||||
</div>
|
||||
<div className="mx-3.5 mb-3 h-px w-full bg-[var(--gap-text)] bg-white" />
|
||||
<div
|
||||
className="flex items-center bg-[var(--chat-bg)] px-3.5 text-xs text-[var(--sub-text)]"
|
||||
style={{ height: 28 }}
|
||||
>
|
||||
{sectionData.indexList[index]}
|
||||
</div>
|
||||
)}
|
||||
itemContent={(index) => {
|
||||
const friend = sectionData.totalList[index];
|
||||
return (
|
||||
<FriendListItem
|
||||
key={sectionData.totalList[index].userID}
|
||||
friend={sectionData.totalList[index]}
|
||||
key={friend.userID}
|
||||
friend={friend}
|
||||
title={dirMap[friend.userID]?.title}
|
||||
showUserCard={showUserCard}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
rangeChanged={({ startIndex }) => determineCurrentGroup(startIndex)}
|
||||
className="no-scrollbar h-full overflow-x-hidden"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user