From 5fe7f5d47655ed1910a5e75af7fe6b8f0770767a Mon Sep 17 00:00:00 2001 From: tianmo Date: Wed, 30 Sep 2026 17:24:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yelaymaths/pages/index/index.wxml | 1 - yelaymaths/pages/index/index.wxss | 14 ++----- .../modules/admin/resource-registry.test.ts | 41 +++++++++++++++++++ .../src/modules/admin/resource-registry.ts | 25 ++++++++++- 4 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 yelaymaths_api/src/modules/admin/resource-registry.test.ts diff --git a/yelaymaths/pages/index/index.wxml b/yelaymaths/pages/index/index.wxml index b832040..2a94c78 100644 --- a/yelaymaths/pages/index/index.wxml +++ b/yelaymaths/pages/index/index.wxml @@ -4,7 +4,6 @@ - 📢 {{broadcast}} diff --git a/yelaymaths/pages/index/index.wxss b/yelaymaths/pages/index/index.wxss index b1c0aeb..eb194d4 100644 --- a/yelaymaths/pages/index/index.wxss +++ b/yelaymaths/pages/index/index.wxss @@ -163,13 +163,6 @@ page { border-radius: 12rpx; } -.broadcast-icon { - flex: none; - margin-right: 12rpx; - font-size: 28rpx; - line-height: 1; -} - .broadcast-body { flex: 1; min-width: 0; @@ -194,9 +187,10 @@ page { animation: broadcast-marquee 18s linear infinite; } -/* 两段文字各带一个等宽右边距,整条宽度 = 2 ×(文字+间距), - 所以左移 50% 正好是一段,循环无缝 */ -.broadcast-text { +/* 滚动时两段文字各带一个等宽右边距,整条宽度 = 2 ×(文字+间距), + 左移 50% 正好是一段,循环无缝。 + 这个边距**只能在滚动时加**:静态居中时它会算进居中范围,把文字整体推偏。 */ +.broadcast-scroll .broadcast-text { margin-right: 80rpx; } diff --git a/yelaymaths_api/src/modules/admin/resource-registry.test.ts b/yelaymaths_api/src/modules/admin/resource-registry.test.ts new file mode 100644 index 0000000..622bea3 --- /dev/null +++ b/yelaymaths_api/src/modules/admin/resource-registry.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest'; +import { RESOURCES } from './resource-registry'; + +/** + * 日期字段的转换护栏。 + * + * 起因:后台加「首页新课」填发布日期,报 PrismaClientValidationError—— + * 表单传的是 'YYYY-MM-DD',而 Prisma 的 DateTime 要完整时间,原样透传就被拒。 + */ +describe('资源注册表的日期字段', () => { + const schema = RESOURCES['home-courses']!.create; + + it('纯日期串会转成 Date(UTC 零点),不再原样透传给 Prisma', () => { + const parsed = schema.parse({ course_name: 'x', published_at: '2026-09-30' }); + expect(parsed.published_at).toBeInstanceOf(Date); + expect((parsed.published_at as Date).toISOString()).toBe('2026-09-30T00:00:00.000Z'); + }); + + it('转出来的日期能原样回填,不会串一天', () => { + const parsed = schema.parse({ course_name: 'x', published_at: '2026-09-30' }); + // 后台显示/回填用的是 ISO 串前 10 位 + expect((parsed.published_at as Date).toISOString().slice(0, 10)).toBe('2026-09-30'); + }); + + it('完整时间串照样接受', () => { + const parsed = schema.parse({ course_name: 'x', published_at: '2026-09-30T12:34:56.000Z' }); + expect((parsed.published_at as Date).toISOString()).toBe('2026-09-30T12:34:56.000Z'); + }); + + it('空值按「不填」处理,不报错', () => { + expect(schema.parse({ course_name: 'x', published_at: '' }).published_at).toBeNull(); + expect(schema.parse({ course_name: 'x', published_at: null }).published_at).toBeNull(); + expect(schema.parse({ course_name: 'x' }).published_at).toBeUndefined(); + }); + + it('乱填会被挡下,并给出可读的原因', () => { + const result = schema.safeParse({ course_name: 'x', published_at: '九月三十号' }); + expect(result.success).toBe(false); + expect(JSON.stringify(result.error?.issues)).toContain('日期格式不正确'); + }); +}); diff --git a/yelaymaths_api/src/modules/admin/resource-registry.ts b/yelaymaths_api/src/modules/admin/resource-registry.ts index 159dade..e97b546 100644 --- a/yelaymaths_api/src/modules/admin/resource-registry.ts +++ b/yelaymaths_api/src/modules/admin/resource-registry.ts @@ -9,7 +9,29 @@ import { z } from 'zod'; * 一个通用控制器 + 这张表覆盖了管理后台的绝大部分内容管理功能。 */ -const dateish = z.union([z.string(), z.date(), z.null()]); +/** + * 日期字段。后台表单传的是 'YYYY-MM-DD' 纯日期串,而 Prisma 的 DateTime 需要完整时间, + * 原样透传会抛 PrismaClientValidationError(首页新课加日期就报过这个)。 + * + * 补成 **UTC 零点**,是为了跟后台的显示/回填对齐:那边是拿 ISO 串切前 10 位, + * 若按北京时间补,就会出现「显示 09-30 → 保存 → 回填成 09-29」串一天。 + */ +const dateish = z + .union([z.string(), z.date(), z.null()]) + .transform((value, ctx) => { + if (value === null) return null; + if (value instanceof Date) return value; + + const text = value.trim(); + if (!text) return null; + + const parsed = new Date(/^\d{4}-\d{2}-\d{2}$/.test(text) ? `${text}T00:00:00.000Z` : text); + if (Number.isNaN(parsed.getTime())) { + ctx.addIssue({ code: z.ZodIssueCode.custom, message: `日期格式不正确:${value}` }); + return z.NEVER; + } + return parsed; + }); const optionalDate = dateish.optional(); export interface ResourceDef { @@ -136,4 +158,3 @@ export const RESOURCES: Record = { export const READONLY_RESOURCES: Record[] }> = { 'free-lessons': { delegate: 'freeLesson', orderBy: [{ series_no: 'asc' }, { sort_order: 'asc' }] }, }; -