- 侧栏按 概览 / 教学内容 / 学情分析 / 站点管理 分组,去掉超管菜单里的重复项; 菜单项的角色与路由守卫一一对应,没有题目权限的教师不再看到点了就被踢回首页的「题目」 - 顶栏:面包屑、回到前台、明暗切换、用户菜单(退出登录);窄屏侧栏自动收起 - 新增 PageHeader:标题 + 右侧主操作 + 下方筛选栏,编辑页带返回按钮 - 卡点分析 / 年度趋势 / 标签管理从题目列表的按钮挪进侧栏 - 首页改成统计卡片 + 班级排名卡片;设置页按钮移到卡片右上角 - 题目、比赛、题单、用户列表的「选项」列固定在右侧,不再被挤出屏幕 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d25b5dbe6d
commit
d3823430f9
25 files changed
+764
-587
No files matched your search
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue"
|
||||
import type { RouteLocationRaw } from "vue-router"
|
||||
|
||||
/**
|
||||
* 后台每一页顶上的那一块:标题行(返回 · 标题 · 标签 ········ 操作按钮),
|
||||
* 下面可选一行筛选栏。以前每页各写一份 `.titleWrapper`,筛选控件和「新建」
|
||||
* 挤在同一行、左右位置每页不一样,这里统一成一种排法。
|
||||
*
|
||||
* - `back`:给了就在标题前出一个返回按钮,跳到这个位置(不用 router.back,
|
||||
* 从外链直接打开编辑页时没有「上一页」可回)
|
||||
* - `#extra`:紧跟标题的小东西(计数 tag 之类)
|
||||
* - `#actions`:标题行最右边的按钮,主操作放最后
|
||||
* - `#filters`:筛选栏,靠左排
|
||||
*/
|
||||
defineProps<{
|
||||
title: string
|
||||
description?: string
|
||||
back?: RouteLocationRaw
|
||||
}>()
|
||||
|
||||
const slots = defineSlots<{
|
||||
extra?: () => any
|
||||
actions?: () => any
|
||||
filters?: () => any
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pageHeader">
|
||||
<div class="row">
|
||||
<div class="heading">
|
||||
<n-button v-if="back" quaternary circle size="small" @click="$router.push(back)">
|
||||
<template #icon><Icon icon="ph:arrow-left" /></template>
|
||||
</n-button>
|
||||
<h2 class="title">{{ title }}</h2>
|
||||
<slot name="extra" />
|
||||
</div>
|
||||
<div v-if="slots.actions" class="actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
<n-text v-if="description" depth="3" class="description">{{ description }}</n-text>
|
||||
<div v-if="slots.filters" class="filters">
|
||||
<slot name="filters" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pageHeader {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.description {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user