feat(ops): add tenant admin user management module

Adds /admin-users CRUD on the ops backend, covering listing, plan/role/KOL
toggles, subscription expiry, status flips and password resets, with a
configurable default plan code. The ops console exposes a new top-level
"用户管理" view and reorganises the sidebar so 操作员管理 and 审计日志
move under a "系统设置" group; AccountsView is renamed accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 00:02:46 +08:00
parent 62b824520c
commit 7be0de0614
15 changed files with 2574 additions and 27 deletions
+46 -5
View File
@@ -9,7 +9,9 @@
theme="light"
mode="inline"
:selected-keys="selectedKeys"
:open-keys="openKeys"
:items="menuItems"
@update:open-keys="onOpenKeysChange"
@click="onMenuClick"
/>
</a-layout-sider>
@@ -44,12 +46,19 @@
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import {
AuditOutlined,
SafetyCertificateOutlined,
SettingOutlined,
TeamOutlined,
} from "@ant-design/icons-vue";
import type { ItemType } from "ant-design-vue";
import { computed, h, onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useAuthStore } from "@/stores/auth";
interface MenuItem {
interface MenuLeaf {
key: string;
label: string;
path: string;
@@ -61,15 +70,47 @@ const auth = useAuthStore();
const collapsed = ref(false);
const menuItems = computed<MenuItem[]>(() => [
{ key: "/accounts", label: "账号管理", path: "/accounts" },
const menuLeaves: MenuLeaf[] = [
{ key: "/admin-users", label: "用户管理", path: "/admin-users" },
{ key: "/accounts", label: "操作员管理", path: "/accounts" },
{ key: "/audits", label: "审计日志", path: "/audits" },
];
const menuItems = computed<ItemType[]>(() => [
{
key: "/admin-users",
label: "用户管理",
icon: () => h(TeamOutlined),
},
{
key: "system",
label: "系统设置",
icon: () => h(SettingOutlined),
children: [
{
key: "/accounts",
label: "操作员管理",
icon: () => h(SafetyCertificateOutlined),
},
{
key: "/audits",
label: "审计日志",
icon: () => h(AuditOutlined),
},
],
},
]);
const openKeys = ref<string[]>(["system"]);
function onOpenKeysChange(keys: string[]) {
openKeys.value = keys;
}
const selectedKeys = computed(() => [route.path]);
function onMenuClick(info: { key: string | number }) {
const item = menuItems.value.find((m) => m.key === info.key);
const item = menuLeaves.find((m) => m.key === info.key);
if (item) void router.push(item.path);
}