fix: 群详情成员分页并与后端能力对齐

This commit is contained in:
Nixevol
2026-09-30 16:22:49 +08:00
parent 05ecb43766
commit 965060dc0c
8 changed files with 204 additions and 22 deletions
+86 -21
View File
@@ -10,10 +10,12 @@ import {
NModal,
NScrollbar,
NSpace,
NText,
useDialog,
} from "naive-ui";
import PageHeader from "@/components/PageHeader.vue";
import LoadFailed from "@/components/LoadFailed.vue";
import HelpTip from "@/components/HelpTip.vue";
import {
addGroupMembers,
createGroup,
@@ -40,11 +42,14 @@ const pageSize = ref(50);
const query = ref("");
const createOpen = ref(false);
const createForm = reactive({ id: "", name: "", owner_id: "", member_ids: "" });
const createForm = reactive({ name: "", owner_id: "", member_ids: "" });
const createLoading = ref(false);
const detailOpen = ref(false);
const detailId = ref("");
const detail = ref<GroupDetail | null>(null);
const memberPage = ref(1);
const memberPageSize = ref(50);
const renameName = ref("");
const addMembers = ref("");
@@ -137,13 +142,38 @@ const memberColumns = computed<DataTableColumns<GroupMember>>(() => [
},
]);
async function openDetail(id: string) {
detail.value = await getGroup(id);
async function loadDetail(id: string) {
const cursor = memberPage.value > 1 ? String((memberPage.value - 1) * memberPageSize.value) : "";
detail.value = await getGroup(id, cursor, memberPageSize.value);
renameName.value = detail.value.name;
}
async function openDetail(id: string) {
detailId.value = id;
memberPage.value = 1;
addMembers.value = "";
await loadDetail(id);
detailOpen.value = true;
}
function failLabel(code: string): string {
switch (code) {
case "not_found":
case "invalid_target":
return "端不存在";
case "endpoint_disabled":
return "端已停用";
case "group_full":
return "群已满";
default:
return code;
}
}
function formatFailed(failed: { id: string; code: string }[]): string {
return failed.map((f) => `${f.id}(${failLabel(f.code)})`).join("、");
}
async function submitCreate() {
if (!createForm.name.trim() || !createForm.owner_id.trim()) {
message.error("名称与群主必填");
@@ -156,14 +186,13 @@ async function submitCreate() {
.map((s) => s.trim())
.filter(Boolean);
const res = await createGroup({
id: createForm.id.trim() || undefined,
name: createForm.name.trim(),
owner_id: createForm.owner_id.trim(),
member_ids,
});
createOpen.value = false;
if (res.failed.length) {
message.warning(`已创建,部分成员失败:${res.failed.map((f) => f.id).join(", ")}`);
message.warning(`已创建,部分成员失败:${formatFailed(res.failed)}`);
} else {
message.success("已创建");
}
@@ -191,7 +220,7 @@ async function onRename() {
if (!detail.value) return;
await renameGroup(detail.value.id, renameName.value.trim());
message.success("已改名");
await openDetail(detail.value.id);
await loadDetail(detail.value.id);
await load();
}
@@ -204,28 +233,44 @@ async function onAddMembers() {
if (!ids.length) return;
const res = await addGroupMembers(detail.value.id, ids);
if (res.failed.length) {
message.warning(`部分失败:${res.failed.map((f) => f.id).join(", ")}`);
message.warning(`部分失败:${formatFailed(res.failed)}`);
} else {
message.success("已加人");
}
await openDetail(detail.value.id);
await loadDetail(detail.value.id);
await load();
}
async function onRemove(endpointId: string) {
if (!detail.value) return;
await removeGroupMember(detail.value.id, endpointId);
message.success("已移除");
await openDetail(detail.value.id);
await load();
dialog.warning({
title: "移除成员",
content: `确定将 ${endpointId} 移出本群?`,
positiveText: "移除",
negativeText: "取消",
onPositiveClick: async () => {
await removeGroupMember(detail.value!.id, endpointId);
message.success("已移除");
await loadDetail(detail.value!.id);
await load();
},
});
}
async function onTransfer(endpointId: string) {
if (!detail.value) return;
await transferGroup(detail.value.id, endpointId);
message.success("已转让群主");
await openDetail(detail.value.id);
await load();
dialog.warning({
title: "转让群主",
content: `确定将群主转让给 ${endpointId}?`,
positiveText: "转让",
negativeText: "取消",
onPositiveClick: async () => {
await transferGroup(detail.value!.id, endpointId);
message.success("已转让群主");
await loadDetail(detail.value!.id);
await load();
},
});
}
</script>
@@ -237,7 +282,7 @@ async function onTransfer(endpointId: string) {
type="primary"
data-testid="group-create-open"
@click="
Object.assign(createForm, { id: '', name: '', owner_id: '', member_ids: '' });
Object.assign(createForm, { name: '', owner_id: '', member_ids: '' });
createOpen = true;
"
>
@@ -249,7 +294,7 @@ async function onTransfer(endpointId: string) {
<LoadFailed v-if="loadError" :description="loadError" @retry="load" />
<template v-else>
<n-space style="margin-bottom: 12px">
<n-input v-model:value="query" placeholder="名称/编号" style="width: 200px" @keyup.enter="page = 1; load()" />
<n-input v-model:value="query" placeholder="群名称" style="width: 200px" data-testid="group-query" @keyup.enter="page = 1; load()" />
<n-button @click="page = 1; load()">查询</n-button>
</n-space>
<n-data-table
@@ -275,8 +320,12 @@ async function onTransfer(endpointId: string) {
<n-modal v-model:show="createOpen" preset="card" title="新建群" style="width: 480px">
<n-scrollbar style="max-height: 320px">
<n-form label-placement="left" label-width="90" :show-feedback="false">
<n-form-item label="编号">
<n-input v-model:value="createForm.id" placeholder="留空则生成" data-testid="group-id" />
<n-form-item>
<template #label>
编号
<HelpTip>由服务器生成,后台不能自定。</HelpTip>
</template>
<n-text depth="3">创建后自动分配</n-text>
</n-form-item>
<n-form-item label="名称">
<n-input v-model:value="createForm.name" data-testid="group-name" />
@@ -323,7 +372,23 @@ async function onTransfer(endpointId: string) {
</n-space>
</n-form-item>
</n-form>
<n-data-table :columns="memberColumns" :data="detail.members" :row-key="(r: GroupMember) => r.id" size="small" />
<n-data-table
remote
:columns="memberColumns"
:data="detail.members"
:row-key="(r: GroupMember) => r.id"
size="small"
:max-height="280"
:pagination="{
page: memberPage,
pageSize: memberPageSize,
itemCount: detail.member_total ?? detail.members.length,
onChange: (p: number) => {
memberPage = p;
loadDetail(detailId);
},
}"
/>
</n-scrollbar>
<template #footer>
<n-space justify="end">