feat: 实现动态 DOCX 模板与样式映射
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const colorSchema = z
|
||||
.string()
|
||||
.regex(/^#[0-9a-f]{6}$/iu, "DOCX 颜色必须使用六位十六进制格式");
|
||||
const fontNameSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.regex(
|
||||
/^[\p{L}\p{N}\s._-]+$/u,
|
||||
"DOCX 字体名称包含不支持的字符"
|
||||
);
|
||||
const pointSizeSchema = z.number().min(6).max(72);
|
||||
const spacingSchema = z.number().min(0).max(144);
|
||||
|
||||
export const docxStylePresetSchema = z.enum([
|
||||
"general",
|
||||
"technical",
|
||||
"official",
|
||||
"formal",
|
||||
"tender"
|
||||
]);
|
||||
|
||||
export type DocxStylePreset = z.infer<
|
||||
typeof docxStylePresetSchema
|
||||
>;
|
||||
|
||||
export const docxFontFamilySchema = z.object({
|
||||
latin: fontNameSchema,
|
||||
eastAsia: fontNameSchema,
|
||||
complexScript: fontNameSchema.optional()
|
||||
});
|
||||
|
||||
export type DocxFontFamily = z.infer<
|
||||
typeof docxFontFamilySchema
|
||||
>;
|
||||
|
||||
const docxBodyStyleSchema = z.object({
|
||||
fonts: docxFontFamilySchema.optional(),
|
||||
sizePt: pointSizeSchema.optional(),
|
||||
color: colorSchema.optional(),
|
||||
lineSpacing: z.number().min(1).max(3).optional(),
|
||||
firstLineIndentChars: z.number().min(0).max(10).optional(),
|
||||
spacingBeforePt: spacingSchema.optional(),
|
||||
spacingAfterPt: spacingSchema.optional()
|
||||
});
|
||||
|
||||
const docxHeadingStyleSchema = z.object({
|
||||
fonts: docxFontFamilySchema.optional(),
|
||||
sizesPt: z
|
||||
.tuple([
|
||||
pointSizeSchema,
|
||||
pointSizeSchema,
|
||||
pointSizeSchema,
|
||||
pointSizeSchema,
|
||||
pointSizeSchema,
|
||||
pointSizeSchema
|
||||
])
|
||||
.optional(),
|
||||
color: colorSchema.optional(),
|
||||
bold: z.boolean().optional(),
|
||||
spacingBeforePt: spacingSchema.optional(),
|
||||
spacingAfterPt: spacingSchema.optional()
|
||||
});
|
||||
|
||||
const docxCodeStyleSchema = z.object({
|
||||
fonts: docxFontFamilySchema.optional(),
|
||||
sizePt: pointSizeSchema.optional(),
|
||||
color: colorSchema.optional(),
|
||||
backgroundColor: colorSchema.optional(),
|
||||
borderColor: colorSchema.optional(),
|
||||
lineSpacing: z.number().min(1).max(3).optional()
|
||||
});
|
||||
|
||||
const docxBlockStyleSchema = z.object({
|
||||
color: colorSchema.optional(),
|
||||
backgroundColor: colorSchema.optional(),
|
||||
borderColor: colorSchema.optional(),
|
||||
leftIndentChars: z.number().min(0).max(20).optional(),
|
||||
italic: z.boolean().optional()
|
||||
});
|
||||
|
||||
const docxTableStyleSchema = z.object({
|
||||
fonts: docxFontFamilySchema.optional(),
|
||||
sizePt: pointSizeSchema.optional(),
|
||||
color: colorSchema.optional(),
|
||||
headerColor: colorSchema.optional(),
|
||||
headerBackgroundColor: colorSchema.optional(),
|
||||
borderColor: colorSchema.optional(),
|
||||
cellMarginMm: z.number().min(0).max(20).optional()
|
||||
});
|
||||
|
||||
const docxCaptionStyleSchema = z.object({
|
||||
fonts: docxFontFamilySchema.optional(),
|
||||
sizePt: pointSizeSchema.optional(),
|
||||
color: colorSchema.optional(),
|
||||
italic: z.boolean().optional(),
|
||||
alignment: z.enum(["left", "center", "right"]).optional()
|
||||
});
|
||||
|
||||
export const docxThemeStyleSchema = z.object({
|
||||
preset: docxStylePresetSchema,
|
||||
body: docxBodyStyleSchema.optional(),
|
||||
headings: docxHeadingStyleSchema.optional(),
|
||||
code: docxCodeStyleSchema.optional(),
|
||||
blockQuote: docxBlockStyleSchema.optional(),
|
||||
table: docxTableStyleSchema.optional(),
|
||||
caption: docxCaptionStyleSchema.optional(),
|
||||
hyperlink: z
|
||||
.object({
|
||||
color: colorSchema.optional(),
|
||||
underline: z.boolean().optional()
|
||||
})
|
||||
.optional()
|
||||
});
|
||||
|
||||
export type DocxThemeStyle = z.infer<
|
||||
typeof docxThemeStyleSchema
|
||||
>;
|
||||
Reference in New Issue
Block a user