2026-05-25 19:36:44 +08:00

99 lines
4.3 KiB
TypeScript

import { z } from "zod";
const requiredH5LinkURLSchema = z
.string()
.trim()
.min(1, "请填写 H5 链接")
.max(2048, "H5 链接不能超过 2048 个字符")
.refine((value) => !/\s/.test(value), "H5 链接不能包含空白字符");
const h5LinkKeySchema = z
.string()
.trim()
.min(1, "请填写 key")
.max(80, "key 不能超过 80 个字符")
.regex(/^[A-Za-z0-9_.:-]+$/, "key 只能包含字母、数字、下划线、中横线、点和冒号");
export const h5LinkUpdateSchema = z.object({
key: h5LinkKeySchema,
label: z.string().trim().min(1, "请填写配置项").max(80, "配置项不能超过 80 个字符"),
url: requiredH5LinkURLSchema,
});
export type H5LinkUpdateForm = z.infer<typeof h5LinkUpdateSchema>;
export const exploreTabSchema = z.object({
enabled: z.boolean(),
h5Url: requiredH5LinkURLSchema,
sortOrder: z.coerce.number().int("排序必须是整数").default(0),
tab: z.string().trim().min(1, "请填写 tab").max(40, "tab 不能超过 40 个字符"),
});
export type ExploreTabForm = z.infer<typeof exploreTabSchema>;
export const appBannerSchema = z
.object({
bannerType: z.enum(["h5", "app"]),
countryCode: z
.string()
.trim()
.transform((value) => value.toUpperCase())
.refine((value) => value === "" || /^[A-Z]{2,3}$/.test(value), "国家编码需为 2-3 位字母"),
coverUrl: z
.string()
.trim()
.min(1, "请上传封面图")
.max(1024, "封面图地址不能超过 1024 个字符")
.refine((value) => !/\s/.test(value), "封面图地址不能包含空白字符"),
description: z.string().trim().max(255, "描述不能超过 255 个字符"),
displayScope: z.enum(["home", "room", "recharge"]),
endsAtMs: z.coerce.number().int().min(0, "投放结束时间不正确").default(0),
param: z.string().trim().max(2048, "参数不能超过 2048 个字符"),
platform: z.enum(["android", "ios"]),
regionId: z.coerce.number().int().min(0, "区域不正确").default(0),
roomSmallImageUrl: z
.string()
.trim()
.max(1024, "房间内小屏图地址不能超过 1024 个字符")
.refine((value) => !/\s/.test(value), "房间内小屏图地址不能包含空白字符"),
sortOrder: z.coerce.number().int("排序必须是整数").default(0),
startsAtMs: z.coerce.number().int().min(0, "投放开始时间不正确").default(0),
status: z.enum(["active", "disabled", "expired"]),
})
.superRefine((value, ctx) => {
if (value.bannerType === "h5" && !value.param) {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 类型需要填写链接", path: ["param"] });
}
if (value.bannerType === "h5" && /\s/.test(value.param)) {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 链接不能包含空白字符", path: ["param"] });
}
if (value.displayScope === "room" && !value.roomSmallImageUrl) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "房间内显示需要上传房间内小屏图",
path: ["roomSmallImageUrl"],
});
}
if (value.startsAtMs > 0 && value.endsAtMs > 0 && value.startsAtMs >= value.endsAtMs) {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "投放结束时间必须晚于开始时间", path: ["endsAtMs"] });
}
});
export type AppBannerForm = z.infer<typeof appBannerSchema>;
export const appVersionSchema = z.object({
buildNumber: z.coerce.number().int("编译版本号必须是整数").positive("编译版本号必须大于 0"),
description: z.string().trim().max(1000, "更新描述不能超过 1000 个字符").default(""),
downloadUrl: z
.string()
.trim()
.min(1, "请填写下载链接")
.max(2048, "下载链接不能超过 2048 个字符")
.refine((value) => !/\s/.test(value), "下载链接不能包含空白字符"),
forceUpdate: z.boolean(),
platform: z.enum(["android", "ios"]),
version: z.string().trim().min(1, "请填写版本号").max(40, "版本号不能超过 40 个字符"),
});
export type AppVersionForm = z.infer<typeof appVersionSchema>;