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:
@@ -88,7 +88,7 @@ export const en: AdminDict = {
|
||||
"内容": "Content",
|
||||
"商务": "Commerce",
|
||||
"系统": "System",
|
||||
"Dashboard": "Dashboard",
|
||||
"仪表盘": "Dashboard",
|
||||
"页面 / 文章": "Pages / Posts",
|
||||
"菜单": "Menus",
|
||||
"媒体库": "Media",
|
||||
|
||||
+1
-1
@@ -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
@@ -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 }) {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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} />
|
||||
) : (
|
||||
|
||||
@@ -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>}>
|
||||
|
||||
@@ -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 = [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user