feat(后台): 手机上也能用了——菜单收进左边抽屉,列表一行一张卡,不用再横着滑
- 侧栏手机上不再占一列图标,顶栏左边 ☰ 拉出整张菜单,按返回键先关菜单 - 题目、比赛、用户、公告、题单列表手机上一项一张卡:名字一行、小字一行、按钮一行, 「学生看得到」开关单独一列靠右 - 卡点分析、AI 报告手机上点开一项整屏看,返回键关掉;年度趋势、自学情况、首页改成上下排 - 出题页、教程编辑、题单编辑、标签、设置手机上排成一列;测试数据「删掉」手机上常驻 - 弹框一律收进屏幕宽度 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b757e2ac38
commit
02e2fa7d9b
30 files changed
+1328
-183
No files matched your search
@@ -7,6 +7,7 @@ import { parseTime } from "utils/functions"
|
||||
import type { AdminAnnouncementListItem } from "utils/types"
|
||||
import { editAnnouncement, getAnnouncement, getAnnouncementList } from "../api"
|
||||
import Actions from "./components/Actions.vue"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
|
||||
const message = useMessage()
|
||||
const total = ref(0)
|
||||
@@ -22,25 +23,66 @@ const announcements = ref<AdminAnnouncementListItem[]>([])
|
||||
* 精确到秒的两列时间拿掉
|
||||
*/
|
||||
const theme = useThemeVars()
|
||||
const { isMobile } = useBreakpoints()
|
||||
|
||||
const columns: DataTableColumn<AdminAnnouncementListItem>[] = [
|
||||
function renderTitle(row: AdminAnnouncementListItem) {
|
||||
return h("span", { class: "title-cell" }, [
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
class: "title-link",
|
||||
to: { name: "admin announcement edit", params: { announcementID: row.id } },
|
||||
},
|
||||
() => row.title,
|
||||
),
|
||||
row.top ? h("span", { class: "top" }, "置顶") : null,
|
||||
h("span", { class: "muted small" }, row.tag),
|
||||
])
|
||||
}
|
||||
|
||||
function renderVisible(row: AdminAnnouncementListItem) {
|
||||
return h(NSwitch, {
|
||||
value: row.visible,
|
||||
size: isMobile.value ? "medium" : "small",
|
||||
rubberBand: false,
|
||||
onUpdateValue: () => toggle(row, "visible"),
|
||||
})
|
||||
}
|
||||
|
||||
function renderActions(row: AdminAnnouncementListItem) {
|
||||
return h(Actions, {
|
||||
announcementID: row.id,
|
||||
top: row.top,
|
||||
onDeleted: listAnnouncements,
|
||||
onToggleTop: () => toggle(row, "top"),
|
||||
})
|
||||
}
|
||||
|
||||
/** 手机上一条一张卡:标题一行,发布 · 改过一行小字,操作一行;「学生看得到」单独一列靠右 */
|
||||
function renderMobileCard(row: AdminAnnouncementListItem) {
|
||||
const created = parseTime(row.createTime, "YYYY-MM-DD")
|
||||
const updated = parseTime(row.lastUpdateTime, "YYYY-MM-DD")
|
||||
return h("div", { class: "m-card" }, [
|
||||
renderTitle(row),
|
||||
h("div", { class: "m-meta muted num" }, [
|
||||
h("span", { class: "m-item" }, `${created} 发布`),
|
||||
updated === created ? null : h("span", { class: "m-item" }, ` · ${updated} 改过`),
|
||||
]),
|
||||
renderActions(row),
|
||||
])
|
||||
}
|
||||
|
||||
const mobileColumns: DataTableColumn<AdminAnnouncementListItem>[] = [
|
||||
{ title: "标题", key: "title", render: renderMobileCard },
|
||||
{ title: "看得到", key: "visible", width: 72, align: "center", render: renderVisible },
|
||||
]
|
||||
|
||||
const desktopColumns: DataTableColumn<AdminAnnouncementListItem>[] = [
|
||||
{
|
||||
title: "标题",
|
||||
key: "title",
|
||||
width: 360,
|
||||
render: (row) =>
|
||||
h("span", { class: "title-cell" }, [
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
class: "title-link",
|
||||
to: { name: "admin announcement edit", params: { announcementID: row.id } },
|
||||
},
|
||||
() => row.title,
|
||||
),
|
||||
row.top ? h("span", { class: "top" }, "置顶") : null,
|
||||
h("span", { class: "muted small" }, row.tag),
|
||||
]),
|
||||
render: renderTitle,
|
||||
},
|
||||
{
|
||||
title: "发布",
|
||||
@@ -62,28 +104,18 @@ const columns: DataTableColumn<AdminAnnouncementListItem>[] = [
|
||||
title: "学生看得到",
|
||||
key: "visible",
|
||||
width: 100,
|
||||
render: (row) =>
|
||||
h(NSwitch, {
|
||||
value: row.visible,
|
||||
size: "small",
|
||||
rubberBand: false,
|
||||
onUpdateValue: () => toggle(row, "visible"),
|
||||
}),
|
||||
render: renderVisible,
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
key: "actions",
|
||||
minWidth: 240,
|
||||
render: (row) =>
|
||||
h(Actions, {
|
||||
announcementID: row.id,
|
||||
top: row.top,
|
||||
onDeleted: listAnnouncements,
|
||||
onToggleTop: () => toggle(row, "top"),
|
||||
}),
|
||||
render: renderActions,
|
||||
},
|
||||
]
|
||||
|
||||
const columns = computed(() => (isMobile.value ? mobileColumns : desktopColumns))
|
||||
|
||||
// 列表响应不含 content(正文是 8MB 上限的富文本),而更新接口要求 content 必填 ——
|
||||
// 拿列表里那行直接回传会 400。所以先取回整条再改。
|
||||
async function toggle(announcement: AdminAnnouncementListItem, field: "visible" | "top") {
|
||||
@@ -134,6 +166,27 @@ watch(query, listAnnouncements, { deep: true })
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ann-table :deep(.m-card) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ann-table :deep(.m-card .title-cell) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ann-table :deep(.m-meta) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ann-table :deep(.m-item) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ann-table :deep(.title-link) {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
|
||||
Reference in new issue
Block a user