mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-23 00:21:28 +00:00
feat(admin-ui): members panel, invite links, audit tab and role-aware console
- New MembersPanel: role switching, add/remove members, invite-link generation with copy/revoke, pending invite list - New Audit tab (AuditLog.vue) with group filter - Role-aware UI: viewer hides route/group edit actions (RouteCard readonly), owner-only controls, role badge + member count on group cards - GroupEditor: members managed via panel, owners field super-admin only - en/zh i18n and CSS for the new panels
This commit is contained in:
parent
252f6f181a
commit
11b74aa82a
11 changed files with 805 additions and 55 deletions
|
|
@ -34,17 +34,14 @@
|
|||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.admins") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.adminsNote") }}</span></label
|
||||
>{{ t("groupEditor.membersNote") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.membersHint") }}</span></label
|
||||
>
|
||||
<input
|
||||
v-model="form.adminIds"
|
||||
type="text"
|
||||
:placeholder="t('groupEditor.adminsPlaceholder')"
|
||||
/>
|
||||
<div class="hint">{{ t("groupEditor.adminsHint") }}</div>
|
||||
<p class="hint">
|
||||
{{ t("groupEditor.membersGoPanel") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div v-if="superAdmin" class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.owners") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.ownersNote") }}</span></label
|
||||
|
|
@ -56,6 +53,13 @@
|
|||
/>
|
||||
<div class="hint">{{ t("groupEditor.ownersHint") }}</div>
|
||||
</div>
|
||||
<div v-else class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.owners") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.ownersSuperOnly") }}</span></label
|
||||
>
|
||||
<input v-model="ownersReadonly" type="text" disabled />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.providers") }}
|
||||
|
|
@ -106,7 +110,7 @@ import type { Group } from "~/types";
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{ open: boolean; group: Group | null; saving: boolean }>();
|
||||
const props = defineProps<{ open: boolean; group: Group | null; saving: boolean; superAdmin?: boolean }>();
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
(e: "save", group: Group): void;
|
||||
|
|
@ -114,11 +118,11 @@ const emit = defineEmits<{
|
|||
|
||||
const isEdit = computed(() => props.group != null);
|
||||
const formError = ref("");
|
||||
const ownersReadonly = computed(() => (props.group?.owners ?? []).join(", "));
|
||||
|
||||
const form = reactive({
|
||||
id: "",
|
||||
name: "",
|
||||
adminIds: "",
|
||||
owners: "",
|
||||
providers: [] as ("github" | "gitea")[],
|
||||
emoji: true,
|
||||
|
|
@ -145,7 +149,6 @@ watch(
|
|||
const g = props.group;
|
||||
form.id = g?.id ?? "";
|
||||
form.name = g?.name ?? "";
|
||||
form.adminIds = (g?.adminIds ?? []).join(", ");
|
||||
form.owners = (g?.owners ?? []).join(", ");
|
||||
form.providers = (g?.providers ?? []).filter(
|
||||
(p): p is "github" | "gitea" => p === "github" || p === "gitea",
|
||||
|
|
@ -172,11 +175,17 @@ function save(): void {
|
|||
return;
|
||||
}
|
||||
const owners = splitList(form.owners);
|
||||
const members = props.group?.members
|
||||
? props.group.members.map((m) => ({ ...m }))
|
||||
: props.group?.adminIds?.length
|
||||
? props.group.adminIds.map((login) => ({ login, role: "owner" as const }))
|
||||
: [];
|
||||
emit("save", {
|
||||
id,
|
||||
name,
|
||||
adminIds: splitList(form.adminIds),
|
||||
owners: owners.length ? owners : undefined,
|
||||
members,
|
||||
adminIds: members.filter((m) => m.role === "owner").map((m) => m.login),
|
||||
owners: props.superAdmin ? (owners.length ? owners : undefined) : props.group?.owners,
|
||||
providers: form.providers.length ? form.providers : undefined,
|
||||
emoji: form.emoji,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue