fix(分组): 把超管排除在分组之外 + 添加成员改用带搜索的下拉

1) 超管不进分组
   超管的数据范围是硬编码全厂(resolve_data_scope 规则 1),业务分组对他
   根本不生效。把他放进成员名单会造成两处矛盾:
     · 界面上「他在这个组里」暗示他受该组约束,但实际不受;
     · 若再给他打组长标记,会出现「组长却不受组范围限制」的怪状态。
   所以:
     · member-candidates 的 SQL 加 `COALESCE(role,'') <> 'SUPER_ADMIN'`
       (LICA 19 人 → 17 人)
     · add_member 再挡一道并给出明确原因,防止绕过界面直接调接口
     · _is_super_admin_account 在 MOM 查询失败时**保守当作超管拦下** ——
       误拦只是加不进去,误放会留下脏数据

2) 添加成员的下拉改用 antd Select
   原先是原生 <select>:LICA 近 20 人,展开会整屏铺开、且不能搜索,又长又难选。
   改为带 showSearch 的 Select(按姓名或账号过滤,optionFilterProp=label),
   并在未选人时禁用「添加」按钮,避免无意义报错。

实测:
  候选人数 19 → 17,超管已不在列表中
  直接 POST 加 sunxia / xingyouwu → 400 并给出原因
  普通成员 duwensheng 加入 → 201,移出 → 204(对照组正常)
This commit is contained in:
2026-09-21 17:41:17 +08:00
parent 15097baa2b
commit 5095756571
2 changed files with 67 additions and 17 deletions

View File

@ -1,5 +1,5 @@
import { useState, useEffect, useCallback, useMemo } from "react";
import { Table, Button, Modal, Input, Checkbox, Tag, App, Spin, Empty } 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 { useAuth } from "../../contexts/AuthContext";
import { isSuperAdmin } from "../../constants/task";
@ -412,22 +412,33 @@ export default function AdminGroupsPage() {
/>
)}
{/* 添加成员面板 */}
{/* 添加成员面板 —— 用 antd Select 而非原生 select
LICA 有近 20 人,原生下拉会整屏铺开且不能搜,又长又难选。 */}
{candidates.length > 0 && (
<div className="mt-3 flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 p-3">
<select
className="flex-1 rounded-lg border border-gray-200 px-3 py-2 text-sm focus:border-blue-400 focus:outline-none"
value={picked ?? ""}
onChange={(e) => setPicked(e.target.value || undefined)}
>
<option value=""></option>
{candidates.map((c) => (
<option key={c.username} value={c.username}>{c.full_name}{c.username}</option>
))}
</select>
<Checkbox checked={pickedLeader} onChange={(e) => setPickedLeader(e.target.checked)}></Checkbox>
<Button type="primary" size="small" loading={adding} onClick={handleAddMember}></Button>
<Button size="small" onClick={() => setCandidates([])}></Button>
<div className="mt-3 flex flex-wrap items-center gap-3 rounded-lg border border-blue-200 bg-blue-50 p-3">
<Select
className="min-w-[260px] flex-1"
placeholder="搜索姓名或账号…"
showSearch
allowClear
autoFocus
value={picked}
onChange={(v) => setPicked(v)}
optionFilterProp="label"
options={candidates.map((c) => ({
value: c.username,
label: `${c.full_name}${c.username}`,
}))}
/>
<Checkbox checked={pickedLeader} onChange={(e) => setPickedLeader(e.target.checked)}>
</Checkbox>
<div className="flex gap-2">
<Button type="primary" size="small" loading={adding} disabled={!picked} onClick={handleAddMember}>
</Button>
<Button size="small" onClick={() => setCandidates([])}></Button>
</div>
</div>
)}
</div>