Compare commits

...

1 Commits

Author SHA1 Message Date
chroe 9ef56a18f2 refactor(showcase): 成员管理卡片优化
- 组织ID查找改为 org +info 输入名字返回ID(不再列出全站805个组织)
- 新增仓库选择器(默认 chroe/gitlink-cli),成员操作可选目标仓库
- 移除重复的 org +list(与 member +list 重叠)
- 修复跨模块命令查找逻辑(用 uid 精确匹配)
2026-06-04 11:39:17 +08:00
1 changed files with 25 additions and 9 deletions

View File

@ -157,18 +157,19 @@ const MODULES = [
},
{
name: "member", title: "成员管理",
desc: "管理项目成员和组织成员,支持添加、移除、修改角色、批量组织邀请。用户ID 可用搜索查找",
desc: "管理项目成员和组织成员,支持添加、移除、修改角色、批量组织邀请",
color: "#f5222d",
repoSelector: true,
commands: [
{ name: "list", desc: "列出项目成员", detail: "获取项目中所有协作者的列表,返回结果中包含每位成员的 id数字和 login用户名,可按关键词搜索", params: [{k:"keyword",l:"搜索关键词",v:""}] },
{ name: "list", desc: "列出项目成员", detail: "获取项目中所有协作者的列表,返回每位成员的 id数字和 login用户名", params: [{k:"keyword",l:"搜索关键词",v:""}] },
{ name: "users", desc: "🔍 按用户名搜索ID", module: "search", detail: "输入 GitLink 用户名搜索,返回的 user_id 就是添加/移除成员时需要的数字 ID", params: [{k:"keyword",l:"用户名",v:""}] },
{ name: "add", desc: "添加成员", detail: "邀请用户加入项目成为协作者。user-id 是数字 ID先点「按用户名搜索ID」查找", params: [{k:"user-id",l:"用户数字ID",v:""}] },
{ name: "remove", desc: "移除成员", detail: "将指定用户移出项目。先点 +list 查看成员的数字 ID", params: [{k:"user-id",l:"用户数字ID",v:""}] },
{ name: "update", desc: "修改成员角色", detail: "更改成员的项目角色Manager管理员、Developer开发者、Reporter报告者。先点 +list 查看成员 ID", params: [{k:"user-id",l:"用户数字ID",v:""},{k:"role",l:"角色",v:"Developer"}] },
{ name: "add", desc: "添加成员", detail: "邀请用户加入项目成为协作者user-id 为数字 ID先点「搜索ID」查找", params: [{k:"user-id",l:"用户数字ID",v:""}] },
{ name: "remove", desc: "移除成员", detail: "将指定用户移出项目", params: [{k:"user-id",l:"用户数字ID",v:""}] },
{ name: "update", desc: "修改成员角色", detail: "更改成员角色Manager / Developer / Reporter", params: [{k:"user-id",l:"用户数字ID",v:""},{k:"role",l:"角色",v:"Developer"}] },
{ section: "────────── 组织成员 ──────────" },
{ name: "list", desc: "📋 查看组织列表/ID", module: "org", detail: "列出所有组织,返回的 id 就是批量邀请时需要的组织 ID", params: [{k:"limit",l:"每页数量",v:"20"}] },
{ name: "members", desc: "查看组织成员", module: "org", detail: "查看指定组织的成员列表,需要组织 ID", params: [{k:"id",l:"组织ID",v:"151783"}] },
{ name: "batch-invite", desc: "批量邀请成员加入组织", module: "org", detail: "批量邀请用户加入组织支持用户名或数字ID支持设置角色member/admin建议先开预览模式确认", params: [{k:"id",l:"组织ID先用 查看组织列表 获取)",v:"151783"},{k:"users",l:"用户名/ID列表",v:"chroe"},{k:"role",l:"角色",v:"member"},{k:"dry-run",l:"预览模式",v:"true",bool:true}] },
{ name: "info", desc: "🔍 输入组织名查ID", module: "org", detail: "输入组织的 name如 e2e-test-org-1780389982返回结果中的 id 字段就是组织 ID", params: [{k:"id",l:"组织名称name",v:"e2e-test-org-1780389982"}] },
{ name: "members", desc: "查看组织成员", module: "org", detail: "查看指定组织的成员列表", params: [{k:"id",l:"组织ID",v:"151783"}] },
{ name: "batch-invite", desc: "批量邀请加入组织", module: "org", detail: "批量邀请用户加入组织支持用户名或数字ID建议先开预览模式确认", params: [{k:"id",l:"组织ID先用「查ID」获取)",v:"151783"},{k:"users",l:"用户名/ID列表",v:"chroe"},{k:"role",l:"角色",v:"member"},{k:"dry-run",l:"预览模式",v:"true",bool:true}] },
]
},
{
@ -255,6 +256,7 @@ MODULES.forEach(mod => {
</div>
<div class="module-body collapsed">
<p style="padding:12px 20px;color:#666;font-size:0.9em;border-bottom:1px solid #f0f0f0;background:#fafbfc;">${mod.desc}</p>
${mod.repoSelector ? `<div style="padding:8px 20px;border-bottom:1px solid #f0f0f0;background:#f6ffed;display:flex;gap:10px;align-items:center;font-size:0.9em;"><label style="white-space:nowrap;color:#52c41a;">📦 仓库:</label><input id="repo-owner-${mod.name}" value="chroe" placeholder="owner" style="width:90px;padding:4px 8px;border:1px solid #d9d9d9;border-radius:4px;">/<input id="repo-name-${mod.name}" value="gitlink-cli" placeholder="repo" style="width:150px;padding:4px 8px;border:1px solid #d9d9d9;border-radius:4px;"></div>` : ''}
${cmdHTML}
</div>`;
container.appendChild(card);
@ -297,7 +299,12 @@ async function runCommand(module, command, uid) {
const btn = document.getElementById(`btn-${uid}`);
const panel = document.getElementById(`result-${uid}`);
const mod = MODULES.find(m => m.name === module);
const cmd = mod.commands.find(c => c.name === command);
const cmd = mod.commands.find(c => {
// match by uid suffix to handle cross-module commands correctly
const expectedUid = c.module ? `${module}-${c.module}-${c.name}` : `${module}-${c.name}`;
return expectedUid === uid;
});
if (!cmd) return;
const args = getArgs(uid, cmd.params);
const apiModule = cmd.module || module;
@ -305,6 +312,15 @@ async function runCommand(module, command, uid) {
btn.textContent = '运行中...';
let url = `/api/run?module=${apiModule}&command=${command}`;
// 如果卡片有仓库选择器,传入 owner/repo
if (mod.repoSelector) {
const ownerEl = document.getElementById(`repo-owner-${module}`);
const repoEl = document.getElementById(`repo-name-${module}`);
if (ownerEl && repoEl) {
url += `&owner=${encodeURIComponent(ownerEl.value.trim())}`;
url += `&repo=${encodeURIComponent(repoEl.value.trim())}`;
}
}
if (args) url += `&args=${encodeURIComponent(args)}`;
try {