feat(分组): 成员改为方块网格,只显示中文名、全部铺开不滚动

反馈是「不要一行一个,只留中文名,做成方块,全展示不滚动」。

· 标签云 → 方块网格(grid + auto-fill,minmax(92px,1fr)):
  窗口越宽一行放得越多,每个成员一格,整齐且密度高
· 去掉账号,格子里只留中文名(账号移到 title 提示里,需要时悬停可看)
· 不设 max-height、不加 overflow —— 自然换行铺满,没有滚动条
· 组长:金色底 + 皇冠图标;移除按钮改成悬停才出现(避免误点)
· 无权限时方块不可点、也不出现移除按钮,纯展示

已通过 tsc 检查。
This commit is contained in:
2026-09-21 17:49:00 +08:00
parent 9598c3fb9f
commit 670436f68c

View File

@ -1,5 +1,5 @@
import { useState, useEffect, useCallback, useMemo } from "react"; import { useState, useEffect, useCallback, useMemo } from "react";
import { Table, Button, Modal, Input, Checkbox, Tag, App, Spin, Empty, Select, Tooltip } from "antd"; import { Table, Button, Modal, Input, Checkbox, Tag, App, Spin, Empty, Select } from "antd";
import { Plus, Pencil, Trash2, UserPlus, Crown, ShieldCheck } from "lucide-react"; import { Plus, Pencil, Trash2, UserPlus, Crown, ShieldCheck } from "lucide-react";
import { useAuth } from "../../contexts/AuthContext"; import { useAuth } from "../../contexts/AuthContext";
import { isSuperAdmin } from "../../constants/task"; import { isSuperAdmin } from "../../constants/task";
@ -385,36 +385,47 @@ export default function AdminGroupsPage() {
) : detail.members.length === 0 ? ( ) : detail.members.length === 0 ? (
<Empty description="该分组还没有成员" image={Empty.PRESENTED_IMAGE_SIMPLE} className="py-6" /> <Empty description="该分组还没有成员" image={Empty.PRESENTED_IMAGE_SIMPLE} className="py-6" />
) : ( ) : (
/* 标签云而非表格:成员多时一人一行要滚很久,标签能一行放好几个。 /* 方块网格:每个成员一格,只显示中文名,全部铺开不做滚动。
有权限时:点标签正文切换组长、点 × 移除;无权限时纯展示。 */ 自动按容器宽度铺排(auto-fill),窗口越宽一行放得越多。
<div className="flex flex-wrap gap-2 rounded-lg border border-gray-100 bg-gray-50/60 p-3"> 有权限时:点方块切换组长、悬停右上角出现移除;无权限时纯展示。 */
<div className="grid gap-2 [grid-template-columns:repeat(auto-fill,minmax(92px,1fr))]">
{detail.members.map((m) => ( {detail.members.map((m) => (
<Tag <div
key={m.user_id} key={m.user_id}
color={m.is_leader ? "gold" : undefined} title={
closable={detail.can_manage_members} detail.can_manage_members
onClose={(e) => { e.preventDefault(); handleRemoveMember(m.user_id); }} ? `${m.display_name}(${m.user_id})· ${m.is_leader ? "点击取消组长" : "点击设为组长"}`
className="!m-0 !px-2.5 !py-1 !text-sm" : `${m.display_name}(${m.user_id})`
}
onClick={() => {
if (detail.can_manage_members) handleToggleLeader(m.user_id, !m.is_leader);
}}
className={`group relative flex h-11 select-none items-center justify-center rounded-lg border px-2 text-sm transition-colors ${
m.is_leader
? "border-amber-300 bg-amber-50 font-medium text-amber-800"
: "border-gray-200 bg-white text-gray-700"
} ${
detail.can_manage_members
? "cursor-pointer hover:border-blue-300 hover:bg-blue-50"
: ""
}`}
> >
<Tooltip {m.is_leader && (
title={ <Crown size={12} className="mr-1 shrink-0 text-amber-500" />
detail.can_manage_members )}
? (m.is_leader ? "点击取消组长" : "点击设为组长") <span className="truncate">{m.display_name}</span>
: undefined
} {/* 移除:默认藏起来,悬停该方块才出现,避免误点 */}
> {detail.can_manage_members && (
<span <button
className={`select-none ${detail.can_manage_members ? "cursor-pointer" : ""}`} title="移出分组"
onClick={() => { onClick={(e) => { e.stopPropagation(); handleRemoveMember(m.user_id); }}
if (detail.can_manage_members) handleToggleLeader(m.user_id, !m.is_leader); className="absolute -right-1.5 -top-1.5 hidden h-4 w-4 items-center justify-center rounded-full bg-red-500 text-[10px] leading-none text-white hover:bg-red-600 group-hover:flex"
}}
> >
{m.is_leader && <Crown size={12} className="mr-1 inline align-[-1px]" />} ×
{m.display_name} </button>
<span className="ml-1.5 font-mono text-xs opacity-50">{m.user_id}</span> )}
</span> </div>
</Tooltip>
</Tag>
))} ))}
</div> </div>
)} )}