fix(后台): 用户、题目、比赛、公告、标签几张列表的操作不再收进 ⋯,直接摆在行尾;名字和题目那列不再吃掉整行的空,创建日期不再折成两行

1366 宽的屏上题目列表、比赛列表的按钮都放得下;题目的知识点多于两个时只摆一个加「+N」。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-13 14:29:56 +08:00
1 parent bd3a57468f
commit 6470f56f87
9 files changed
+91 -130

No files matched your search

@@ -1,9 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue"
import { deleteAnnouncement } from "admin/api" import { deleteAnnouncement } from "admin/api"
/** /**
* 公告一行的操作(设计稿「后台公告」AnnAdmin):「编辑」常驻,置顶 / 删除收进 ⋯ * 公告一行的操作:编辑、置顶、删除直接摆出来(用户说的:不要收进 ⋯)
*/ */
interface Props { interface Props {
announcementID: number announcementID: number
@@ -23,15 +22,7 @@ function goEdit() {
}) })
} }
const menuOptions = computed<DropdownOption[]>(() => [ function confirmDelete() {
{ label: props.top ? "取消置顶" : "置顶", key: "top" },
{ type: "divider", key: "divider" },
{ label: "删除", key: "delete" },
])
function onMenu(key: string) {
if (key === "top") emit("toggleTop")
else if (key === "delete") {
dialog.warning({ dialog.warning({
title: "删除这条公告?", title: "删除这条公告?",
content: "删掉就找不回来了。只是不想让学生看到的话,关掉「学生看得到」就行。", content: "删掉就找不回来了。只是不想让学生看到的话,关掉「学生看得到」就行。",
@@ -43,16 +34,14 @@ function onMenu(key: string) {
emit("deleted") emit("deleted")
}, },
}) })
}
} }
</script> </script>
<template> <template>
<n-flex :size="4" justify="end" :wrap="false"> <n-flex :size="6" :wrap="false">
<n-button size="small" @click="goEdit">编辑</n-button> <n-button size="small" @click="goEdit">编辑</n-button>
<n-dropdown trigger="click" placement="bottom-end" :options="menuOptions" @select="onMenu"> <n-button size="small" @click="emit('toggleTop')">
<n-button size="small" quaternary aria-label="更多"> {{ props.top ? "取消置顶" : "置顶" }}
<template #icon><Icon icon="ph:dots-three-bold" /></template>
</n-button> </n-button>
</n-dropdown> <n-button size="small" quaternary type="error" @click="confirmDelete">删除</n-button>
</n-flex> </n-flex>
</template> </template>
+3 -3
View File
@@ -18,7 +18,7 @@ const announcements = ref<AdminAnnouncementListItem[]>([])
/** /**
* 后台公告列表(设计稿「后台公告」AnnAdmin):一行一条,标题(置顶挂标,公告 / 更新淡淡写在后面)、 * 后台公告列表(设计稿「后台公告」AnnAdmin):一行一条,标题(置顶挂标,公告 / 更新淡淡写在后面)、
* 发布、改过、学生看得到;「编辑」常驻,置顶 / 删除收进 ⋯。ID、作者(都是同一个人)、 * 发布、改过、学生看得到;编辑、置顶、删除直接摆出来。ID、作者(都是同一个人)、
* 精确到秒的两列时间拿掉 * 精确到秒的两列时间拿掉
*/ */
const theme = useThemeVars() const theme = useThemeVars()
@@ -27,7 +27,7 @@ const columns: DataTableColumn<AdminAnnouncementListItem>[] = [
{ {
title: "标题", title: "标题",
key: "title", key: "title",
minWidth: 300, width: 360,
render: (row) => render: (row) =>
h("span", { class: "title-cell" }, [ h("span", { class: "title-cell" }, [
h( h(
@@ -73,7 +73,7 @@ const columns: DataTableColumn<AdminAnnouncementListItem>[] = [
{ {
title: "", title: "",
key: "actions", key: "actions",
width: 110, minWidth: 240,
render: (row) => render: (row) =>
h(Actions, { h(Actions, {
announcementID: row.id, announcementID: row.id,
@@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue"
import type { AdminContest } from "utils/types" import type { AdminContest } from "utils/types"
import { cloneContest } from "../../api" import { cloneContest } from "../../api"
@@ -22,14 +21,8 @@ function goClass() {
window.open(`/contest/${props.contest.id}/class`, "_blank") window.open(`/contest/${props.contest.id}/class`, "_blank")
} }
const menuOptions: DropdownOption[] = [ function openFront() {
{ label: "复制一份", key: "clone" }, window.open(`/contest/${props.contest.id}`, "_blank")
{ label: "在前台看", key: "view" },
]
function onMenu(key: string) {
if (key === "clone") clone()
else if (key === "view") window.open(`/contest/${props.contest.id}`, "_blank")
} }
async function clone() { async function clone() {
@@ -46,16 +39,14 @@ async function clone() {
} }
</script> </script>
<template> <template>
<n-flex :size="4" justify="end" :wrap="false"> <!-- 操作直接摆出来,不收进 ⋯(用户说的) -->
<n-flex :size="4" :wrap="false">
<n-button size="small" @click="goEdit">编辑</n-button> <n-button size="small" @click="goEdit">编辑</n-button>
<n-button size="small" @click="goClass"> <n-button size="small" @click="goClass">
{{ contest.status === "-1" ? "成绩" : "全班情况" }} {{ contest.status === "-1" ? "成绩" : "全班情况" }}
</n-button> </n-button>
<n-dropdown trigger="click" placement="bottom-end" :options="menuOptions" @select="onMenu"> <n-button size="small" @click="clone">复制一份</n-button>
<n-button size="small" quaternary aria-label="更多"> <n-button size="small" title="在前台打开这场比赛" @click="openFront">前台看</n-button>
<template #icon><Icon icon="ph:dots-three-bold" /></template>
</n-button>
</n-dropdown>
</n-flex> </n-flex>
</template> </template>
<style scoped></style> <style scoped></style>
+6 -6
View File
@@ -12,7 +12,7 @@ import Actions from "./components/Actions.vue"
/** /**
* 后台比赛列表(设计稿「比赛重设计」CtAdminList)。一行一场:比赛名(期中 / 期末挂标, * 后台比赛列表(设计稿「比赛重设计」CtAdminList)。一行一场:比赛名(期中 / 期末挂标,
* 要密码的带锁)、开始时间和多长、状态、几道题、几人参加、出卷人、学生看得到; * 要密码的带锁)、开始时间和多长、状态、几道题、几人参加、出卷人、学生看得到;
* 操作「编辑」「成绩 / 全班情况」+ ⋯。按开始时间排 —— 原来按创建时间,复制出来的旧比赛 * 操作「编辑」「成绩 / 全班情况」「复制一份」「在前台看」直接摆出来。按开始时间排 —— 原来按创建时间,复制出来的旧比赛
* 会跑到最前面。ID、标签、类型三列拿掉(标签只在期中 / 期末时挂出来,类型就是那把锁) * 会跑到最前面。ID、标签、类型三列拿掉(标签只在期中 / 期末时挂出来,类型就是那把锁)
*/ */
const theme = useThemeVars() const theme = useThemeVars()
@@ -47,7 +47,7 @@ const columns: DataTableColumn<AdminContestListItem>[] = [
{ {
title: "比赛", title: "比赛",
key: "title", key: "title",
minWidth: 220, width: 260,
render: (row) => render: (row) =>
h("span", { class: "title-cell" }, [ h("span", { class: "title-cell" }, [
h(ContestTitle, { contest: row }), h(ContestTitle, { contest: row }),
@@ -57,7 +57,7 @@ const columns: DataTableColumn<AdminContestListItem>[] = [
{ {
title: "开始 · 多长", title: "开始 · 多长",
key: "start_time", key: "start_time",
width: 220, width: 200,
render: (row) => render: (row) =>
h("span", { class: "num" }, [ h("span", { class: "num" }, [
parseTime(row.startTime, "YYYY-MM-DD HH:mm"), parseTime(row.startTime, "YYYY-MM-DD HH:mm"),
@@ -67,7 +67,7 @@ const columns: DataTableColumn<AdminContestListItem>[] = [
{ {
title: "状态", title: "状态",
key: "status", key: "status",
width: 80, width: 72,
render: (row) => render: (row) =>
h( h(
"span", "span",
@@ -89,7 +89,7 @@ const columns: DataTableColumn<AdminContestListItem>[] = [
{ {
title: "出卷", title: "出卷",
key: "created_by", key: "created_by",
width: 96, width: 84,
ellipsis: { tooltip: true }, ellipsis: { tooltip: true },
render: (row) => row.createdBy.username, render: (row) => row.createdBy.username,
}, },
@@ -108,7 +108,7 @@ const columns: DataTableColumn<AdminContestListItem>[] = [
{ {
title: "", title: "",
key: "actions", key: "actions",
width: 190, minWidth: 270,
render: (row) => h(Actions, { contest: row }), render: (row) => h(Actions, { contest: row }),
}, },
] ]
@@ -1,13 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue"
import { errorCode, errorMessage } from "utils/api" import { errorCode, errorMessage } from "utils/api"
import { deleteContestProblem, deleteProblem, makeProblemPublic } from "admin/api" import { deleteContestProblem, deleteProblem, makeProblemPublic } from "admin/api"
import download from "utils/download" import download from "utils/download"
/** /**
* 题目列表一行的操作(设计稿「后台题目列表」PlA):常用的「编辑」常驻,其余收进 ⋯ —— * 题目列表一行的操作:编辑、在前台看、下载测试数据、删除直接摆出来(用户说的:不要收进 ⋯)。
* 原来是四颗彩色按钮(编辑 / 查看 / 删除 / 下载)并排,1366 宽的屏上把「可见」开关挤出了屏幕。 * 原来是四颗彩色按钮,现在一律素色,删除用浅色的危险按钮。
* 有人交过的题删不掉(服务端也拦),「删除」直接灰掉并写明为什么,不用点了才知道 * 有人交过的题删不掉(服务端也拦),「删除」直接灰掉、鼠标放上去写明为什么
*/ */
interface Props { interface Props {
problemID: number problemID: number
@@ -69,21 +68,7 @@ function goCheck() {
window.open(data.href, "_blank") window.open(data.href, "_blank")
} }
const menuOptions = computed<DropdownOption[]>(() => [ function confirmDelete() {
{ label: "在前台看", key: "view" },
{ label: "下载测试数据", key: "download" },
...(isContestProblem.value ? [{ label: "转成公开题", key: "public" }] : []),
{ type: "divider", key: "divider" },
props.triedUsers
? { label: `删除(有 ${props.triedUsers} 人交过)`, key: "delete", disabled: true }
: { label: "删除", key: "delete" },
])
function onMenu(key: string) {
if (key === "view") goCheck()
else if (key === "download") downloads()
else if (key === "public") openMakePublicModal()
else if (key === "delete") {
dialog.warning({ dialog.warning({
title: `删除 ${props.problemDisplayID}?`, title: `删除 ${props.problemDisplayID}?`,
content: "删掉就找不回来了。", content: "删掉就找不回来了。",
@@ -91,7 +76,6 @@ function onMenu(key: string) {
negativeText: "不删", negativeText: "不删",
onPositiveClick: handleDeleteProblem, onPositiveClick: handleDeleteProblem,
}) })
}
} }
function openMakePublicModal() { function openMakePublicModal() {
@@ -122,13 +106,24 @@ async function handleMakePublic() {
} }
</script> </script>
<template> <template>
<n-flex :size="4" justify="end" :wrap="false"> <n-flex :size="6" :wrap="false">
<n-button size="small" @click="goEdit">编辑</n-button> <n-button size="small" @click="goEdit">编辑</n-button>
<n-dropdown trigger="click" placement="bottom-end" :options="menuOptions" @select="onMenu"> <n-button size="small" title="在前台打开这道题" @click="goCheck">前台看</n-button>
<n-button size="small" quaternary aria-label="更多"> <n-button size="small" title="下载测试数据" @click="downloads">测试数据</n-button>
<template #icon><Icon icon="ph:dots-three-bold" /></template> <n-button v-if="isContestProblem" size="small" @click="openMakePublicModal">
转成公开题
</n-button> </n-button>
</n-dropdown> <span :title="props.triedUsers ? `有 ${props.triedUsers} 人交过,删不掉` : undefined">
<n-button
size="small"
quaternary
type="error"
:disabled="!!props.triedUsers"
@click="confirmDelete"
>
删除
</n-button>
</span>
</n-flex> </n-flex>
<n-modal <n-modal
+26 -11
View File
@@ -102,12 +102,18 @@ const visibleTabs = computed(() => [
{ value: "0" as const, label: "藏着的", count: counts.value?.hidden }, { value: "0" as const, label: "藏着的", count: counts.value?.hidden },
]) ])
/** 知识点只摆前两个,多的写 +N,鼠标放上去看全部(P073 这类题挂了六个) */ /**
* 知识点:两个以内全摆,多了只摆第一个 + 「+N」,鼠标放上去看全部(P073 这类题挂了六个)。
* 列宽只放得下两个,摆两个再加 +N 的话 +N 会被截掉
*/
function renderTags(tags: string[]) { function renderTags(tags: string[]) {
if (!tags.length) return h("span", { class: "muted" }, "—") if (!tags.length) return h("span", { class: "muted" }, "—")
const shown = tags.length > 2 ? tags.slice(0, 1) : tags
return h("span", { class: "tags", title: tags.join("、") }, [ return h("span", { class: "tags", title: tags.join("、") }, [
...tags.slice(0, 2).map((tag) => h("span", { class: "tag", key: tag }, tag)), ...shown.map((tag) => h("span", { class: "tag", key: tag }, tag)),
tags.length > 2 ? h("span", { class: "muted more" }, `+${tags.length - 2}`) : null, tags.length > shown.length
? h("span", { class: "muted more" }, `+${tags.length - shown.length}`)
: null,
]) ])
} }
@@ -146,15 +152,17 @@ const baseColumns: DataTableColumn<AdminProblemRow>[] = [
{ {
title: "题号", title: "题号",
key: "_id", key: "_id",
width: 84, width: 70,
render: (row) => h("span", { class: "did", title: `内部编号 ${row.id}` }, row._id), render: (row) => h("span", { class: "did", title: `内部编号 ${row.id}` }, row._id),
}, },
{ title: "题目", key: "title", minWidth: 220, ellipsis: false, render: renderTitle }, // 题目定宽:p90 也就 12 个字加一两个类型标签。原来 minWidth 让它吃掉整行剩余空间(用户嫌空太多),
{ title: "知识点", key: "tags", width: 190, render: (row) => renderTags(row.tags) }, // 剩下的宽度给最后的操作列
{ title: "题目", key: "title", width: 196, ellipsis: false, render: renderTitle },
{ title: "知识点", key: "tags", width: 158, render: (row) => renderTags(row.tags) },
{ {
title: "做对 / 做过", title: "做对 / 做过",
key: "users", key: "users",
width: 100, width: 96,
render: (row) => render: (row) =>
row.triedUsers row.triedUsers
? h("span", { class: "num" }, [ ? h("span", { class: "num" }, [
@@ -163,11 +171,11 @@ const baseColumns: DataTableColumn<AdminProblemRow>[] = [
]) ])
: h("span", { class: "muted" }, "没人做过"), : h("span", { class: "muted" }, "没人做过"),
}, },
{ title: "出题人", key: "username", width: 100, ellipsis: { tooltip: true } }, { title: "出题人", key: "username", width: 86, ellipsis: { tooltip: true } },
{ {
title: "创建", title: "创建",
key: "create_time", key: "create_time",
width: 96, width: 100,
render: (row) => h("span", { class: "num muted" }, parseTime(row.createTime, "YYYY-MM-DD")), render: (row) => h("span", { class: "num muted" }, parseTime(row.createTime, "YYYY-MM-DD")),
}, },
{ {
@@ -185,7 +193,7 @@ const baseColumns: DataTableColumn<AdminProblemRow>[] = [
{ {
title: "", title: "",
key: "actions", key: "actions",
width: 104, minWidth: 240,
render: (row) => render: (row) =>
h(Actions, { h(Actions, {
problemID: row.id, problemID: row.id,
@@ -385,6 +393,11 @@ watch(() => [query.page, query.limit, query.author, query.kind, query.visible],
opacity: 0.7; opacity: 0.7;
} }
/* 表头不折行:「做对 / 做过」「学生看得到」折成两行时整张表头变高 */
.problem-table :deep(.n-data-table-th) {
white-space: nowrap;
}
.problem-table :deep(.did) { .problem-table :deep(.did) {
font-weight: 600; font-weight: 600;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
@@ -409,10 +422,11 @@ watch(() => [query.page, query.limit, query.author, query.kind, query.visible],
} }
.problem-table :deep(.tags) { .problem-table :deep(.tags) {
display: inline-flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
white-space: nowrap; white-space: nowrap;
overflow: hidden;
} }
.problem-table :deep(.tag) { .problem-table :deep(.tag) {
@@ -435,5 +449,6 @@ watch(() => [query.page, query.limit, query.author, query.kind, query.visible],
.problem-table :deep(.num) { .problem-table :deep(.num) {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
white-space: nowrap;
} }
</style> </style>
+12 -30
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { Icon } from "@iconify/vue"
import PageHeader from "admin/components/PageHeader.vue" import PageHeader from "admin/components/PageHeader.vue"
import { useThemeVars } from "naive-ui" import { useThemeVars } from "naive-ui"
import type { AdminTag, TagCategory } from "utils/types" import type { AdminTag, TagCategory } from "utils/types"
@@ -30,7 +29,8 @@ function openTagProblems(tag: AdminTag) {
/** /**
* 两栏:知识点 / 主题(设计稿「后台标签」TagA)。原来是一张宽表,每行一组单选按钮切分类、 * 两栏:知识点 / 主题(设计稿「后台标签」TagA)。原来是一张宽表,每行一组单选按钮切分类、
* 两颗按钮,ID 一列、页头一段说明。现在一行一个标签 + 挂了几道题(条的长短看出哪个用得多), * 两颗按钮,ID 一列、页头一段说明。现在一行一个标签 + 挂了几道题(条的长短看出哪个用得多),
* 点名字看这个标签下的题,改名 / 换分类 / 删除收进 ⋯。还没归类的单独一条放最上面 * 点名字看这个标签下的题,改名 / 换分类 / 删除直接摆在行尾(用户说的:不要收进 ⋯)。
* 还没归类的单独一条放最上面
*/ */
const columnsOf = computed(() => { const columnsOf = computed(() => {
const confirmed = visibleTags.value.filter((t) => t.categoryConfirmed) const confirmed = visibleTags.value.filter((t) => t.categoryConfirmed)
@@ -49,25 +49,6 @@ const columnsOf = computed(() => {
}) })
const pendingTags = computed(() => visibleTags.value.filter((t) => !t.categoryConfirmed)) const pendingTags = computed(() => visibleTags.value.filter((t) => !t.categoryConfirmed))
function menuOf(tag: AdminTag): DropdownOption[] {
return [
{ label: "改名(同名就合并)", key: "rename" },
{ label: tag.category === "theme" ? "改成知识点" : "改成主题", key: "move" },
{ type: "divider", key: "divider" },
{
label: tag.problemCount ? `删除(${tag.problemCount} 道题会摘掉它)` : "删除",
key: "delete",
props: { style: `color: ${theme.value.errorColor}` },
},
]
}
function onMenu(tag: AdminTag, key: string) {
if (key === "rename") startEdit(tag)
else if (key === "move") changeCategory(tag, tag.category === "theme" ? "knowledge" : "theme")
else if (key === "delete") confirmDelete(tag)
}
async function listTags() { async function listTags() {
const res = await getTagAdminList(keyword.value) const res = await getTagAdminList(keyword.value)
tags.value = res tags.value = res
@@ -177,16 +158,17 @@ watchDebounced(keyword, listTags, { debounce: 500, maxWait: 1000 })
><i :style="{ width: `${(tag.problemCount / col.max) * 100}%` }"></i ><i :style="{ width: `${(tag.problemCount / col.max) * 100}%` }"></i
></span> ></span>
<span class="count">{{ tag.problemCount }} 道</span> <span class="count">{{ tag.problemCount }} 道</span>
<n-dropdown <n-button size="tiny" quaternary @click="startEdit(tag)">改名</n-button>
trigger="click" <n-button
placement="bottom-end" size="tiny"
:options="menuOf(tag)" quaternary
@select="(key: string) => onMenu(tag, key)" @click="changeCategory(tag, tag.category === 'theme' ? 'knowledge' : 'theme')"
> >
<n-button size="small" quaternary aria-label="更多"> {{ tag.category === "theme" ? "改成知识点" : "改成主题" }}
<template #icon><Icon icon="ph:dots-three-bold" /></template>
</n-button> </n-button>
</n-dropdown> <n-button size="tiny" quaternary type="error" @click="confirmDelete(tag)"
>删除</n-button
>
</template> </template>
</div> </div>
</div> </div>
@@ -259,7 +241,7 @@ watchDebounced(keyword, listTags, { debounce: 500, maxWait: 1000 })
} }
.name { .name {
flex: 0 0 140px; flex: 0 0 120px;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
+8 -21
View File
@@ -1,12 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue"
import { editUser } from "admin/api" import { editUser } from "admin/api"
import type { AdminUserForm } from "utils/types" import type { AdminUserForm } from "utils/types"
/** /**
* 用户列表一行的操作(设计稿「后台用户与批量建号」UsList):最常用的「重置密码」常驻 * 用户列表一行的操作:重置密码、编辑、禁用、删除直接摆出来(用户说的:不要收进 ⋯)。
* (学生忘了密码是后台进得最多的一件事),其余收进 ⋯。原来四颗彩色按钮并排, * 删除前确认,交过题的账号删不掉,提示改为禁用
* 1366 的屏上把「上次登录」挤成了半截
*/ */
interface Props { interface Props {
user: AdminUserForm user: AdminUserForm
@@ -27,17 +25,7 @@ async function banUser() {
emit("userBanned", props.user) emit("userBanned", props.user)
} }
const menuOptions = computed<DropdownOption[]>(() => [ function confirmDelete() {
{ label: "编辑", key: "edit" },
{ label: props.user.isDisabled ? "解除禁用" : "禁用", key: "ban" },
{ type: "divider", key: "divider" },
{ label: "删除", key: "delete" },
])
function onMenu(key: string) {
if (key === "edit") emit("openEditModal", props.user)
else if (key === "ban") banUser()
else if (key === "delete") {
dialog.warning({ dialog.warning({
title: `删除 ${props.user.username}?`, title: `删除 ${props.user.username}?`,
content: "删掉就找不回来了。交过题的账号删不掉,那种请改为禁用。", content: "删掉就找不回来了。交过题的账号删不掉,那种请改为禁用。",
@@ -45,16 +33,15 @@ function onMenu(key: string) {
negativeText: "不删", negativeText: "不删",
onPositiveClick: () => emit("deleteUser", [props.user.id]), onPositiveClick: () => emit("deleteUser", [props.user.id]),
}) })
}
} }
</script> </script>
<template> <template>
<n-flex :size="4" justify="end" :wrap="false"> <n-flex :size="6" :wrap="false">
<n-button size="small" @click="emit('resetPassword', props.user)">重置密码</n-button> <n-button size="small" @click="emit('resetPassword', props.user)">重置密码</n-button>
<n-dropdown trigger="click" placement="bottom-end" :options="menuOptions" @select="onMenu"> <n-button size="small" @click="emit('openEditModal', props.user)">编辑</n-button>
<n-button size="small" quaternary aria-label="更多"> <n-button size="small" @click="banUser">
<template #icon><Icon icon="ph:dots-three-bold" /></template> {{ props.user.isDisabled ? "解除禁用" : "禁用" }}
</n-button> </n-button>
</n-dropdown> <n-button size="small" quaternary type="error" @click="confirmDelete">删除</n-button>
</n-flex> </n-flex>
</template> </template>
+4 -2
View File
@@ -96,7 +96,9 @@ const columns: DataTableColumn<AdminUserForm>[] = [
{ {
title: "用户名", title: "用户名",
key: "username", key: "username",
minWidth: 200, // 定宽:用户名一般就 ks + 班号 + 两三个字,原来 minWidth 让它把整行剩下的空都吃掉了(用户嫌空太多)。
// 剩下的宽度给最后的操作列,操作直接摆出来
width: 230,
render: (row) => h(UserNameCell, { user: row }), render: (row) => h(UserNameCell, { user: row }),
}, },
{ {
@@ -133,7 +135,7 @@ const columns: DataTableColumn<AdminUserForm>[] = [
{ {
key: "actions", key: "actions",
title: "", title: "",
width: 130, minWidth: 300,
render: (row) => render: (row) =>
h(Actions, { h(Actions, {
user: row, user: row,