Files
deerflow-code/web-code/components/qa/config/templateStore.ts
T
2026-05-27 19:43:15 +08:00

153 lines
4.5 KiB
TypeScript

/**
* templateStore.ts - 模板存储管理模块
*
* 本模块负责从后端加载模板配置,并支持本地覆盖功能
* 模板存储在 /public/templates 目录下,通过 fetch API 加载
*
* 主要功能:
* - 加载模板索引列表
* - 根据 ID 或元数据加载单个模板
* - 从本地存储读取/保存模板覆盖
* - 检查模板是否有本地覆盖
*/
import type { Template, TemplateMeta } from '../types'
/**
* 本地存储中覆盖模板的前缀
* 完整 key 格式:mope_template_override:{template_id}
*/
const OVERRIDE_PREFIX = 'mope_template_override:'
/**
* 深度克隆对象
* 使用 JSON 序列化/反序列化实现
* 注意:这会丢失 undefined、正则、函数等特殊类型
*
* @param value - 需要克隆的值
* @returns 克隆后的新值
*/
export function clone<T>(value: T): T {
return JSON.parse(JSON.stringify(value))
}
/**
* 加载模板索引列表
* 从 http://ch1.b.uat.4.cn/zncmweb/templates/index.json 获取所有可用模板的元数据
*
* @returns 模板元数据数组
* @throws 如果请求失败或响应非 OK,抛出错误
*/
export async function loadTemplateIndex(): Promise<TemplateMeta[]> {
const response = await fetch('https://ch1.b.uat.4.cn/zncmweb/templates/index.json')
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const data = await response.json()
return (data.templates || []) as TemplateMeta[]
}
/**
* 根据元数据或 ID 加载模板
* 如果本地有覆盖,返回覆盖版本
*
* @param metaOrId - 模板元数据对象或模板 ID 字符串
* @returns 模板对象
* @throws 如果请求失败或响应非 OK,抛出错误
*/
export async function loadTemplateByMeta(metaOrId: TemplateMeta | string): Promise<Template> {
// 提取模板 ID
const templateId = typeof metaOrId === 'string'
? metaOrId
: metaOrId?.template_id
// 确定请求的 URL
const file = typeof metaOrId === 'string'
? `/templates/${templateId}.json`
: (metaOrId as TemplateMeta)?.file
const response = await fetch(file || `/templates/${templateId}.json`)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const template = await response.json() as Template
// 检查是否有本地覆盖
const override = getTemplateOverride(template.template_id || templateId || '')
// 如果有覆盖,返回覆盖版本
return override ? clone(override) : template
}
/**
* 加载原始模板(忽略本地覆盖)
* 用于在用户重置模板时恢复到原始状态
*
* @param metaOrId - 模板元数据对象或模板 ID 字符串
* @returns 原始模板对象
* @throws 如果请求失败或响应非 OK,抛出错误
*/
export async function loadOriginalTemplate(metaOrId: TemplateMeta | string): Promise<Template> {
// 提取模板 ID
const templateId = typeof metaOrId === 'string'
? metaOrId
: metaOrId?.template_id
// 确定请求的 URL
const file = typeof metaOrId === 'string'
? `/templates/${templateId}.json`
: (metaOrId as TemplateMeta)?.file
const response = await fetch(file || `/templates/${templateId}.json`)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
return response.json() as Promise<Template>
}
/**
* 从本地存储获取模板覆盖
*
* @param templateId - 模板 ID
* @returns 覆盖的模板对象,如果没有覆盖则返回 null
*/
export function getTemplateOverride(templateId: string): Template | null {
if (!templateId) return null
try {
const raw = localStorage.getItem(`${OVERRIDE_PREFIX}${templateId}`)
return raw ? JSON.parse(raw) : null
} catch {
return null
}
}
/**
* 检查指定模板是否有本地覆盖
*
* @param templateId - 模板 ID
* @returns 如果有覆盖返回 true,否则返回 false
*/
export function hasTemplateOverride(templateId: string): boolean {
return Boolean(getTemplateOverride(templateId))
}
/**
* 保存模板覆盖到本地存储
*
* @param templateId - 模板 ID
* @param template - 要保存的模板对象
* @throws 如果 templateId 为空,抛出错误
*/
export function saveTemplateOverride(templateId: string, template: Template): void {
if (!templateId) throw new Error('缺少 template_id')
localStorage.setItem(
`${OVERRIDE_PREFIX}${templateId}`,
JSON.stringify(template)
)
}
/**
* 清除指定模板的本地覆盖
*
* @param templateId - 模板 ID
*/
export function clearTemplateOverride(templateId: string): void {
if (!templateId) return
localStorage.removeItem(`${OVERRIDE_PREFIX}${templateId}`)
}