v0.16.1: 修复后台整体不可交互(node:fs 被客户端外置化报错,image-variant 改惰性获取)、字号再放大一号(15→16)、修复深色主题被烘焙 CSS 反盖(:where 降级变量块特异性)、antd v6 弃用 API 清理(Space direction→orientation/Drawer width→size)、仪表盘菜单标签改中文源串

This commit is contained in:
bigcode
2026-11-05 02:23:28 +08:00
parent a018019504
commit 1cefdecfe0
12 changed files with 30 additions and 21 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ export const en: AdminDict = {
"内容": "Content",
"商务": "Commerce",
"系统": "System",
"Dashboard": "Dashboard",
"仪表盘": "Dashboard",
"页面 / 文章": "Pages / Posts",
"菜单": "Menus",
"媒体库": "Media",
+1 -1
View File
@@ -19,7 +19,7 @@ export type AdminCapabilityKey = "content" | "commerce" | "system";
export const ADMIN_MENU_GROUPS: { label: string; children: AdminMenuDef[] }[] = [
{
label: "概览",
children: [{ key: "/admin/", label: "Dashboard" }],
children: [{ key: "/admin/", label: "仪表盘" }],
},
{
label: "内容",
+2 -2
View File
@@ -21,8 +21,8 @@ type ThemeCtx = {
/** # 成员变量 主题上下文(默认浅色) **/
const Ctx = createContext<ThemeCtx>({ theme: "light", setTheme: () => {} });
/** # 成员变量 后台统一主题 token(字号放大一号:14 → 15;主题变更后需重跑 scripts/gen-antd-css.tsx) **/
export const ADMIN_THEME_TOKEN = { colorPrimary: "#1677ff", fontSize: 15 };
/** # 成员变量 后台统一主题 token(字号放大两号:14 → 16;主题变更后需重跑 scripts/gen-antd-css.tsx) **/
export const ADMIN_THEME_TOKEN = { colorPrimary: "#1677ff", fontSize: 16 };
/** # 组件 后台主题 + AntD 全局包装(语言上下文之外的所有页面都应在其中) */
export function AdminThemeProvider(props: { children: ReactNode }) {
+1 -1
View File
@@ -147,7 +147,7 @@ function DashboardPage() {
</Col>
<Col xs={24} xl={8}>
<Card title={t("询盘漏斗")} size="small" style={{ height: "100%" }}>
<Space direction="vertical" size={12} style={{ display: "flex" }}>
<Space orientation="vertical" size={12} style={{ display: "flex" }}>
{(["new", "read", "replied", "spam", "archived"] as const).map((st) => {
const n = inquiryStatus.find((s) => s.status === st)?.count ?? 0;
return (
+1 -1
View File
@@ -183,7 +183,7 @@ function InquiriesPage() {
<Drawer
title={viewing ? `${t("询盘")} #${viewing.id} · ${viewing.name || viewing.email}` : ""}
width="min(1120px, 92vw)"
size="min(1120px, 92vw)"
open={viewing !== null}
onClose={() => setViewing(null)}
destroyOnHidden
+1 -1
View File
@@ -134,7 +134,7 @@ function PermissionsPage() {
style={{ marginBottom: 16 }}
message={t("按角色配置菜单功能权限与 MCP / API 访问。admin 角色恒为全量,不可编辑;未勾选任何菜单的角色无法访问对应后台页面。")}
/>
<Space direction="vertical" size={16} style={{ display: "flex" }}>
<Space orientation="vertical" size={16} style={{ display: "flex" }}>
{EDITABLE_ROLES.map((role) =>
draft[role] ? (
<Card
+2 -2
View File
@@ -27,7 +27,7 @@ function ProfilePage() {
return (
<Card size="small">
<PageHeader title={t("个人设置")} />
<Space direction="vertical" size={16} style={{ display: "flex", maxWidth: 820 }}>
<Space orientation="vertical" size={16} style={{ display: "flex", maxWidth: 820 }}>
<ProfileForm profile={data} />
<PasswordForm />
<McpTokenCard profile={data} />
@@ -78,7 +78,7 @@ function ProfileForm(props: { profile: ProfileData }) {
>
<Space size={20} align="start" style={{ display: "flex" }}>
<Form.Item label={t("头像")} style={{ textAlign: "center" }}>
<Space direction="vertical" size={8}>
<Space orientation="vertical" size={8}>
{avatar ? (
<Avatar size={72} src={avatar} />
) : (
+1 -1
View File
@@ -209,7 +209,7 @@ function SettingsPage() {
key: "__other__",
label: t("其他"),
children: (
<Space direction="vertical" size={16} style={{ display: "flex" }}>
<Space orientation="vertical" size={16} style={{ display: "flex" }}>
<Typography.Text type="secondary">{t("以下键无表单定义(由脚本或系统内部写入),此处只读展示。")}</Typography.Text>
{otherRows.map((r) => (
<Card key={r.key} type="inner" size="small" title={r.key} extra={<Typography.Text type="secondary" style={{ fontSize: 12 }}>{t("最后更新")}:{fmtDateTime(r.updatedAt)}</Typography.Text>}>
+7 -4
View File
@@ -4,8 +4,11 @@
* 变体文件;dump 的 image.url 只存原图。这里在服务端按 uploads 目录里实际存在的
* `<base>-<w>x<h>.<ext>` 变体择优改写 image.url(选宽度 ≤ 目标里最大的;无变体保持原图)。
*/
import { readdirSync } from "node:fs";
import { join, dirname } from "node:path";
/** # 函数 惰性获取服务端 node:fs(不能静态 import:客户端 bundle 求值时 vite 会将其外置化并在导入期抛错) */
function getFs(): typeof import("node:fs") | undefined {
return (globalThis as { process?: { getBuiltinModule?: (m: string) => unknown } })
.process?.getBuiltinModule?.("node:fs") as typeof import("node:fs") | undefined;
}
/** # 常量 WP 尺寸名 → 目标宽度(Elementor 常用档位) **/
const SIZE_TARGET: Record<string, number> = {
@@ -40,11 +43,11 @@ function findVariant(url: string, targetWidth: number): string | null {
const m = localPath.match(/^(.*\/)([^/]+)\.(\w+)$/);
if (!m) return null;
const [, dir, base, ext] = m;
const fsDir = join("public", dir);
const fsDir = `public${dir}`; // dir 恒以 / 开头(/uploads/...),等价于 path.join("public", dir)
let files = dirCache.get(fsDir);
if (!files) {
try {
files = readdirSync(fsDir);
files = getFs()?.readdirSync(fsDir) ?? [];
} catch {
files = [];
}