feat: 初始化画个标仓库 - AI驱动设计系统(BYOK模式) - 224源/371504图标+25海报源+130技能
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* 画个标 · 海报素材库加载器(懒加载)
|
||||
* 素材库由 scripts/build-posters.js 构建拆分为:
|
||||
* - js/data/poster-builtin.js 轻量索引(sources 元数据 + 分类索引,无全量内容)
|
||||
* - js/data/posters/{id}.js 每源一个数据文件(HGB_PosterSource_{id} = {items:[...]})
|
||||
* 本模块负责:按源懒加载 + 缓存 + 同步读已加载源 + 预加载核心源
|
||||
*
|
||||
* 与图标素材库(iconlib.js)的差异:
|
||||
* - 海报素材为全彩/位图/模板,保留原始宽高比,渲染不再归一化到 24 视口
|
||||
* - items 字段而非 icons;每条含 type/category/categoryCn/workspaces 归类标签
|
||||
*/
|
||||
const HGB_PosterLib = (function () {
|
||||
'use strict';
|
||||
|
||||
const VERSION = '20260823e'; // 与 poster.html 中 poster-builtin.js?v= 版本联动
|
||||
|
||||
const cache = {}; // sid -> {items:[...]}
|
||||
const pending = {}; // sid -> Promise
|
||||
|
||||
function fileURL(sid) {
|
||||
return 'js/data/posters/' + sid + '.js?v=' + VERSION;
|
||||
}
|
||||
|
||||
function loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = url;
|
||||
s.onload = resolve;
|
||||
s.onerror = function () { reject(new Error('海报素材源加载失败: ' + url)); };
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
/** 加载单个源(缓存 + 并发去重) */
|
||||
function loadSource(sid) {
|
||||
if (cache[sid]) return Promise.resolve(cache[sid]);
|
||||
if (pending[sid]) return pending[sid];
|
||||
pending[sid] = loadScript(fileURL(sid)).then(() => {
|
||||
const data = window['HGB_PosterSource_' + sid];
|
||||
if (!data || !data.items) throw new Error('海报素材源不存在: ' + sid);
|
||||
cache[sid] = data;
|
||||
delete pending[sid];
|
||||
return data;
|
||||
}).catch((e) => {
|
||||
delete pending[sid];
|
||||
throw e;
|
||||
});
|
||||
return pending[sid];
|
||||
}
|
||||
|
||||
/** 并行加载多个源(全部成功才 resolve) */
|
||||
function loadSources(ids) {
|
||||
return Promise.all((ids || []).map(loadSource));
|
||||
}
|
||||
|
||||
/** 并行加载多个源(容忍单个失败,返回已成功加载的) */
|
||||
function preload(ids) {
|
||||
return Promise.allSettled((ids || []).map(loadSource)).then((rs) =>
|
||||
rs.filter(r => r.status === 'fulfilled').map(r => r.value));
|
||||
}
|
||||
|
||||
/** 加载全部源 */
|
||||
function loadAll() {
|
||||
const ids = Object.keys((typeof HGB_PosterBuiltin !== 'undefined' ? HGB_PosterBuiltin.sources : {}) || {});
|
||||
return preload(ids);
|
||||
}
|
||||
|
||||
/** 同步读取已加载源(未加载返回 null) */
|
||||
function getSource(sid) {
|
||||
return cache[sid] || null;
|
||||
}
|
||||
|
||||
/** 是否已加载 */
|
||||
function loaded(sid) {
|
||||
return !!cache[sid];
|
||||
}
|
||||
|
||||
/** 已加载的全部源 id */
|
||||
function loadedIds() {
|
||||
return Object.keys(cache);
|
||||
}
|
||||
|
||||
/** 从已加载源按 名称/id 找素材(返回 {id,name,svg,type,...} 或 null) */
|
||||
function findPoster(ref) {
|
||||
if (!ref || typeof ref !== 'string') return null;
|
||||
const name = ref.trim();
|
||||
const parts = name.split(':');
|
||||
if (parts.length === 2) {
|
||||
const src = cache[parts[0]];
|
||||
const hit = src && (src.items || []).find(i => i.name === parts[1] || i.id === name);
|
||||
if (hit) return hit;
|
||||
}
|
||||
const lower = name.toLowerCase();
|
||||
for (const sid of Object.keys(cache)) {
|
||||
const hit = (cache[sid].items || []).find(i =>
|
||||
i.id === name || i.name === name ||
|
||||
String(i.name).toLowerCase() === lower ||
|
||||
String(i.name).toLowerCase().includes(lower));
|
||||
if (hit) return hit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 搜索已加载源(词根包含匹配),返回完整素材对象 */
|
||||
function searchLoaded(keyword, limit) {
|
||||
const kw = String(keyword || '').toLowerCase();
|
||||
const out = [];
|
||||
for (const sid of Object.keys(cache)) {
|
||||
for (const it of cache[sid].items || []) {
|
||||
if (it.name && it.name.toLowerCase().includes(kw)) {
|
||||
out.push({ id: sid + ':' + it.name, name: it.name, svg: it.svg, source: sid, type: it.type });
|
||||
if (out.length >= (limit || 60)) return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 搜索索引(全量 id/name,无内容,用于筛选展示) */
|
||||
function searchIndex(keyword, limit) {
|
||||
const kw = String(keyword || '').toLowerCase();
|
||||
const out = [];
|
||||
const idx = (typeof HGB_PosterBuiltin !== 'undefined' && HGB_PosterBuiltin.itemIndex) || [];
|
||||
for (const it of idx) {
|
||||
if (it.name && it.name.toLowerCase().includes(kw)) {
|
||||
out.push({ id: it.id, name: it.name, source: String(it.id).split(':')[0], category: it.category });
|
||||
if (out.length >= (limit || 20)) return out;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 按 id 反查来源 id */
|
||||
function sourceOf(fullId) {
|
||||
return String(fullId || '').split(':')[0];
|
||||
}
|
||||
|
||||
return {
|
||||
VERSION, loadSource, loadSources, preload, loadAll,
|
||||
getSource, loaded, loadedIds, findPoster, searchLoaded, searchIndex, sourceOf
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user