后台用户列表把所有账号的明文密码直接铺在表格里。学生密码是有意保留的运营需求 (老师要能查),但管理员账号的密码跟着一起明文显示,投屏或者旁人路过就看到了。 现在学生管理员/教师管理员/超管三类账号的密码默认渲染成 •••••• 加一个「显示」 按钮,点一下该行展开成明文(仍然是 TextCopy,点击复制照旧)。普通用户不变。 展开状态按 user id 记在 list.vue 里,listUsers() 里清空,所以翻页、搜索、换筛选、 换排序之后一律回到打码;不做定时自动隐藏,也不记进 localStorage。 只改前端。rawPassword 照旧随列表下发——/users* 每条路由本来就是 requireSuperAdmin, 非超管连列表都拿不到,要防的是屏幕被看到,不是拿到响应的人。改成「按需取单个密码」 反而要多开一个下发明文的端点,把面扩大。 rawPassword 为空时不打码:给一个点了什么也不显示的按钮没意义,直接走原来的 TextCopy。 顺带把角色标签缩短(学生管理员→生管、教师管理员→师管)。getUserRole 只有同一张表的 用户名列在用,密码列变宽之后那一列需要让出横向空间。 实跑验证(本机 dev 栈 + 超管登录):管理员行打码、学生行明文照旧、点「显示」后展开 且仍是可复制的按钮、搜索触发重新拉列表后回到打码。vue-tsc 与 build 均通过。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
46
apps/web/src/admin/user/components/Password.vue
Normal file
46
apps/web/src/admin/user/components/Password.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script lang="ts" setup>
|
||||
import TextCopy from "shared/components/TextCopy.vue"
|
||||
import { USER_TYPE } from "utils/constants"
|
||||
import type { User } from "utils/types"
|
||||
|
||||
interface Props {
|
||||
user: User
|
||||
revealed: boolean
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
defineEmits<{
|
||||
(e: "reveal", value: number): void
|
||||
}>()
|
||||
|
||||
/**
|
||||
* 只有**管理员账号**的密码默认打码。
|
||||
*
|
||||
* 学生的密码照旧直接显示 —— 老师查学生密码是日常动作(明文列 `raw_password` 就是为它
|
||||
* 保留的),挡一道只是添乱。要防的是管理员密码在投屏、或者旁人路过时被看到。
|
||||
*
|
||||
* `rawPassword` 为空时不打码:给一个点了什么也不显示的按钮没有意义,直接走原来的
|
||||
* TextCopy(渲染成空)。
|
||||
*/
|
||||
const masked = computed(
|
||||
() =>
|
||||
!props.revealed &&
|
||||
props.user.adminType !== USER_TYPE.REGULAR_USER &&
|
||||
!!props.user.rawPassword,
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
<n-flex v-if="masked" align="center" :size="6" :wrap="false">
|
||||
<span class="dots">••••••</span>
|
||||
<n-button size="tiny" secondary @click="$emit('reveal', props.user.id)">
|
||||
显示
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<TextCopy v-else>{{ props.user.rawPassword }}</TextCopy>
|
||||
</template>
|
||||
<style scoped>
|
||||
/* 不加 nowrap 的话六个点会在窄列里折行,把整行撑高 */
|
||||
.dots {
|
||||
white-space: nowrap;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "../api"
|
||||
import Actions from "./components/Actions.vue"
|
||||
import Name from "./components/Name.vue"
|
||||
import Password from "./components/Password.vue"
|
||||
import { PROBLEM_PERMISSION, USER_TYPE } from "utils/constants"
|
||||
import { useRouteQuery } from "@vueuse/router"
|
||||
import TextCopy from "shared/components/TextCopy.vue"
|
||||
@@ -49,6 +50,9 @@ const sortOptions = [
|
||||
]
|
||||
const [create, toggleCreate] = useToggle(false)
|
||||
const password = ref("")
|
||||
// 已经点开的管理员密码,按 user id 记。listUsers() 里清空,所以翻页/搜索/换筛选之后
|
||||
// 一律回到打码状态 —— 不做定时自动隐藏,也不记进 localStorage。
|
||||
const revealedPasswords = ref(new Set<number>())
|
||||
const userIDs = ref<DataTableRowKey[]>([])
|
||||
|
||||
const rowKey = (row: User) => row.id
|
||||
@@ -65,8 +69,13 @@ const columns: DataTableColumn<User>[] = [
|
||||
{
|
||||
title: "密码",
|
||||
key: "raw_password",
|
||||
width: 100,
|
||||
render: (row) => h(TextCopy, () => row.rawPassword),
|
||||
width: 150,
|
||||
render: (row) =>
|
||||
h(Password, {
|
||||
user: row,
|
||||
revealed: revealedPasswords.value.has(row.id),
|
||||
onReveal: (id: number) => revealedPasswords.value.add(id),
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: "创建时间",
|
||||
@@ -131,6 +140,7 @@ async function listUsers() {
|
||||
)
|
||||
total.value = res.total
|
||||
users.value = res.results
|
||||
revealedPasswords.value.clear()
|
||||
}
|
||||
|
||||
function chooseUsers(rowKeys: DataTableRowKey[]) {
|
||||
|
||||
@@ -141,7 +141,7 @@ export function debounce<T extends (...args: any[]) => any>(
|
||||
|
||||
export function getUserRole(role: User["adminType"]): {
|
||||
type: "default" | "info" | "warning" | "error"
|
||||
label: "普通" | "学生管理员" | "教师管理员" | "超管"
|
||||
label: "普通" | "生管" | "师管" | "超管"
|
||||
} {
|
||||
const roleMap = {
|
||||
[USER_TYPE.REGULAR_USER]: {
|
||||
@@ -150,11 +150,11 @@ export function getUserRole(role: User["adminType"]): {
|
||||
},
|
||||
[USER_TYPE.STUDENT_ADMIN]: {
|
||||
type: "info" as const,
|
||||
label: "学生管理员" as const,
|
||||
label: "生管" as const,
|
||||
},
|
||||
[USER_TYPE.TEACHER_ADMIN]: {
|
||||
type: "warning" as const,
|
||||
label: "教师管理员" as const,
|
||||
label: "师管" as const,
|
||||
},
|
||||
[USER_TYPE.SUPER_ADMIN]: {
|
||||
type: "error" as const,
|
||||
|
||||
Reference in New Issue
Block a user