feat(后台): 侧栏分组带图标、可收起,加顶栏;所有后台页面统一页头
Deploy / deploy (push) Canceled after 0s

- 侧栏按 概览 / 教学内容 / 学情分析 / 站点管理 分组,去掉超管菜单里的重复项;
  菜单项的角色与路由守卫一一对应,没有题目权限的教师不再看到点了就被踢回首页的「题目」
- 顶栏:面包屑、回到前台、明暗切换、用户菜单(退出登录);窄屏侧栏自动收起
- 新增 PageHeader:标题 + 右侧主操作 + 下方筛选栏,编辑页带返回按钮
- 卡点分析 / 年度趋势 / 标签管理从题目列表的按钮挪进侧栏
- 首页改成统计卡片 + 班级排名卡片;设置页按钮移到卡片右上角
- 题目、比赛、题单、用户列表的「选项」列固定在右侧,不再被挤出屏幕

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 19:54:42 +08:00
1 parent d25b5dbe6d
commit d3823430f9
25 files changed
+764 -587

No files matched your search

+6
View File
@@ -1,4 +1,5 @@
<script setup lang="ts">
import PageHeader from "admin/components/PageHeader.vue"
import { errorMessage } from "utils/api"
import {
CLASS_NAME_MAX_DIGITS,
@@ -81,6 +82,11 @@ async function submit() {
</script>
<template>
<PageHeader
title="批量导入用户"
description="每行一个名字,用户名为 ks + 班级号 + 名字,密码随机生成;导入后会下载账号清单"
:back="{ name: 'admin user list' }"
/>
<n-space>
<n-flex vertical>
<n-flex align="center">
+30 -42
View File
@@ -1,4 +1,5 @@
<script setup lang="ts">
import PageHeader from "admin/components/PageHeader.vue"
import { errorMessage } from "utils/api"
import { DataTableRowKey, NFlex, NTag, SelectOption } from "naive-ui"
import Pagination from "shared/components/Pagination.vue"
@@ -105,6 +106,7 @@ const columns: DataTableColumn<AdminUserForm>[] = [
key: "actions",
title: "选项",
width: 280,
fixed: "right",
render: (row) =>
h(Actions, {
user: row,
@@ -252,43 +254,39 @@ watch(() => [query.page, query.limit, query.type, query.orderBy], listUsers)
</script>
<template>
<n-flex class="titleWrapper" justify="space-between">
<n-flex>
<h2 class="title">用户列表</h2>
<n-button type="primary" @click="createNewUser">新建</n-button>
<n-button @click="$router.push({ name: 'admin user generate' })"> 导入 </n-button>
</n-flex>
<n-flex>
<PageHeader title="用户列表">
<template #actions>
<n-popconfirm v-if="userIDs.length" @positive-click="onDeleteUsers(userIDs)">
<template #trigger>
<n-button type="warning">删除</n-button>
<n-button type="error" secondary>删除选中({{ userIDs.length }})</n-button>
</template>
确定删除选中的用户吗?删除后无法恢复!
</n-popconfirm>
<n-flex align="center">
<n-select
v-model:value="query.orderBy"
:options="sortOptions"
placeholder="排序方式"
style="width: 120px"
/>
<n-select
v-model:value="query.type"
:options="adminOptions"
placeholder="选择用户类型"
style="width: 120px"
/>
<div>
<n-input
style="width: 200px"
v-model:value="query.keyword"
clearable
@clear="clearQuery"
/>
</div>
</n-flex>
</n-flex>
</n-flex>
<n-button @click="$router.push({ name: 'admin user generate' })">批量导入</n-button>
<n-button type="primary" @click="createNewUser">新建用户</n-button>
</template>
<template #filters>
<n-input
style="width: 220px"
v-model:value="query.keyword"
placeholder="用户名 / 真名 / 邮箱"
clearable
@clear="clearQuery"
/>
<n-select
v-model:value="query.type"
:options="adminOptions"
placeholder="选择用户类型"
style="width: 120px"
/>
<n-select
v-model:value="query.orderBy"
:options="sortOptions"
placeholder="排序方式"
style="width: 120px"
/>
</template>
</PageHeader>
<n-data-table
:data="users"
:columns="columns"
@@ -353,13 +351,3 @@ watch(() => [query.page, query.limit, query.type, query.orderBy], listUsers)
</n-form>
</n-modal>
</template>
<style scoped>
.titleWrapper {
margin-bottom: 16px;
}
.title {
margin: 0;
}
</style>