feat: worksheet发布功能开发

This commit is contained in:
R524809
2026-04-24 17:49:11 +08:00
parent 5106dbbd7c
commit bb69d5c49d
24 changed files with 2540 additions and 1014 deletions
+156
View File
@@ -0,0 +1,156 @@
import type {
WorksheetCloudCategory,
WorksheetStatus,
} from '../core/models/worksheet';
/**
* 调试发布时的裁剪模式。
* - header-footer: 同 node-tools 入口图脚本,默认裁掉页眉和页脚
* - header-only: 仅裁掉页眉,保留底部品牌区
* - none: 不裁剪,直接压缩整张 A4 预览图
*/
export type DebugCropMode = 'header-footer' | 'header-only' | 'none';
/**
* 发布到 worksheets 集合的核心元数据。
* 这里对齐《小程序云开发方案》中的题型字段设计,供页面侧生成 payload 使用。
*/
export interface DebugPublishMeta {
id: string;
title: string;
subtitle: string;
category: WorksheetCloudCategory;
subcategory?: string;
path: string;
ageMin: number;
ageMax: number;
grade: number;
difficulty: 1 | 2 | 3 | 4;
previewImg?: string;
tags: string[];
isNew: boolean;
isHot: boolean;
sortOrder?: number;
status: WorksheetStatus;
}
/** 发布弹窗里可调的图片处理参数。 */
export interface DebugPublishSettings {
cropMode: DebugCropMode;
quality: number;
width: number;
maxSizeKB: number;
}
/** 弹窗确认后回传给页面的表单结果。 */
export interface DebugPublishConfirmDetail {
meta: Pick<DebugPublishMeta, 'title' | 'subtitle' | 'tags' | 'status'>;
settings: DebugPublishSettings;
}
/** 图片处理组件收到的输入参数。 */
export interface DebugProcessImageParams {
sourcePath: string;
settings: DebugPublishSettings;
}
/** 图片处理完成后返回给页面的信息。 */
export interface DebugProcessImageResult {
tempFilePath: string;
size: number;
width: number;
height: number;
quality: number;
}
/** 默认压缩质量,与文档里的入口图建议值保持一致。 */
export const DEBUG_PUBLISH_DEFAULT_QUALITY = 90;
/** 默认入口图宽度,沿用 node-tools 脚本的 600px。 */
export const DEBUG_PUBLISH_DEFAULT_WIDTH = 600;
/** 默认体积上限,超过后会继续降质压缩。 */
export const DEBUG_PUBLISH_MAX_SIZE_KB = 200;
/** 页眉裁剪比例:110 / 842,来源于现有入口图脚本。 */
export const DEBUG_CROP_TOP_RATIO = 110 / 842;
/** 页脚裁剪比例:52 / 842,来源于现有入口图脚本。 */
export const DEBUG_CROP_BOTTOM_RATIO = 52 / 842;
/** 仅开发版开放 Debug 发布能力。 */
export function isDebugPublishEnabled(): boolean {
const accountInfo = wx.getAccountInfoSync();
return accountInfo.miniProgram.envVersion === 'develop';
}
/** 限制压缩质量范围,避免过高或过低。 */
export function clampPublishQuality(value: number): number {
return Math.min(95, Math.max(40, Math.round(value)));
}
/** 限制输出宽度范围,防止导出过小或过大。 */
export function clampPublishWidth(value: number): number {
return Math.min(1000, Math.max(400, Math.round(value)));
}
/** 根据裁剪模式计算顶部 / 底部裁剪比例。 */
export function getCropRatios(mode: DebugCropMode): {
topRatio: number;
bottomRatio: number;
} {
if (mode === 'header-only') {
return {
topRatio: DEBUG_CROP_TOP_RATIO,
bottomRatio: 0,
};
}
if (mode === 'none') {
return {
topRatio: 0,
bottomRatio: 0,
};
}
return {
topRatio: DEBUG_CROP_TOP_RATIO,
bottomRatio: DEBUG_CROP_BOTTOM_RATIO,
};
}
/** 统一构造预览图在云存储中的路径。 */
export function buildWorksheetPreviewCloudPath(
category: WorksheetCloudCategory,
id: string,
): string {
return `assets/previews/${category}/${id}.jpg`;
}
/** 将输入框中的标签文本拆成标签数组,兼容英文逗号、中文逗号、顿号和空白。 */
export function normalizeTagsInput(raw: string): string[] {
return raw
.split(/[,,、\s]+/)
.map((item) => item.trim())
.filter(Boolean);
}
/**
* 由年龄段推断年级值。
* 当前先用“年龄中心点 -> 年级”的简单映射,后续如有更细分规则可独立替换。
*/
export function inferGradeFromAge(ageMin: number, ageMax: number): number {
const centerAge = Math.round((ageMin + ageMax) / 2);
const ageGradeMap: Record<number, number> = {
2: -4,
3: -3,
4: -2,
5: -1,
6: 0,
7: 1,
8: 2,
9: 3,
10: 4,
11: 5,
12: 6,
};
return ageGradeMap[centerAge] ?? 0;
}