feat: 初始化画个标仓库 - AI驱动设计系统(BYOK模式) - 224源/371504图标+25海报源+130技能
This commit is contained in:
+373
@@ -0,0 +1,373 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/svg+xml" href="logo/favicon.svg">
|
||||
<title>技能中心 · 画个标</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
|
||||
font-size: 14px; color: #1f2937; background: #f3f4f6; min-height: 100vh;
|
||||
}
|
||||
a { color: #4f46e5; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.hidden { display: none !important; }
|
||||
|
||||
/* ===== 顶栏 ===== */
|
||||
.topbar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 0 20px; height: 54px;
|
||||
background: linear-gradient(90deg, #1e1b4b, #312e81);
|
||||
color: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.15);
|
||||
position: sticky; top: 0; z-index: 20;
|
||||
}
|
||||
.topbar .brand { display: flex; align-items: center; gap: 10px; }
|
||||
.topbar .brand .icon { font-size: 20px; }
|
||||
.topbar .brand .name { font-size: 17px; font-weight: 700; letter-spacing: 1px; }
|
||||
.topbar .brand .tag {
|
||||
font-size: 11px; color: #c7d2fe; background: rgba(255,255,255,.12);
|
||||
padding: 2px 8px; border-radius: 20px;
|
||||
}
|
||||
.btn-back {
|
||||
border: 1px solid rgba(255,255,255,.35); border-radius: 6px;
|
||||
background: rgba(255,255,255,.1); color: #fff; padding: 6px 14px;
|
||||
font-size: 13px; cursor: pointer; transition: background .15s;
|
||||
}
|
||||
.btn-back:hover { background: rgba(255,255,255,.22); }
|
||||
|
||||
.wrap { max-width: 1080px; margin: 22px auto 60px; padding: 0 16px; }
|
||||
|
||||
/* ===== 概览统计 ===== */
|
||||
.stats { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
|
||||
.stat-card {
|
||||
flex: 1; min-width: 150px; background: #fff; border: 1px solid #e5e7eb;
|
||||
border-radius: 10px; padding: 14px 18px; box-shadow: 0 1px 3px rgba(0,0,0,.06);
|
||||
}
|
||||
.stat-card .num { font-size: 26px; font-weight: 800; color: #4f46e5; }
|
||||
.stat-card .lab { font-size: 12px; color: #6b7280; margin-top: 2px; }
|
||||
|
||||
/* ===== 指引 ===== */
|
||||
.guide {
|
||||
background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 10px;
|
||||
padding: 14px 18px; margin-bottom: 18px; font-size: 13px; line-height: 1.8;
|
||||
}
|
||||
.guide b { color: #4338ca; }
|
||||
.guide code {
|
||||
background: #e0e7ff; border-radius: 4px; padding: 1px 6px;
|
||||
font-size: 12px; color: #3730a3;
|
||||
}
|
||||
|
||||
/* ===== 工具栏(流程步骤 + 分类 Tab + 搜索) ===== */
|
||||
.toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.filters { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.step-filters { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.fchip {
|
||||
border: 1px solid #e5e7eb; background: #fff; color: #4b5563;
|
||||
padding: 6px 14px; border-radius: 20px; font-size: 13px; cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
.fchip:hover { border-color: #c7d2fe; color: #4338ca; }
|
||||
.fchip.active { background: #4f46e5; border-color: #4f46e5; color: #fff; }
|
||||
.fchip.stepped { border-style: dashed; }
|
||||
.fchip.stepped.active { background: #0e7490; border-color: #0e7490; }
|
||||
.search {
|
||||
flex: 1; min-width: 200px; border: 1px solid #e5e7eb; border-radius: 8px;
|
||||
padding: 8px 12px; font-size: 13px; color: #1f2937; outline: none;
|
||||
}
|
||||
.search:focus { border-color: #4f46e5; }
|
||||
.step-divider { font-size: 12px; color: #9ca3af; align-self: center; margin: 0 2px; }
|
||||
|
||||
/* ===== 技能卡片 ===== */
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 14px; }
|
||||
.card {
|
||||
background: #fff; border: 1px solid #e5e7eb; border-radius: 12px;
|
||||
padding: 16px; box-shadow: 0 1px 3px rgba(0,0,0,.06);
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
transition: box-shadow .15s, transform .15s;
|
||||
}
|
||||
.card:hover { box-shadow: 0 4px 14px rgba(79,70,229,.12); transform: translateY(-1px); }
|
||||
.card-head { display: flex; align-items: flex-start; gap: 10px; }
|
||||
.skill-emoji { font-size: 26px; line-height: 1; }
|
||||
.card-title { font-size: 15px; font-weight: 700; }
|
||||
.card-cat {
|
||||
display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 20px;
|
||||
background: #eef2ff; color: #4338ca; margin-left: 6px; vertical-align: 2px;
|
||||
}
|
||||
.card-step {
|
||||
display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 20px;
|
||||
background: #ecfeff; color: #0e7490; margin-left: 4px; vertical-align: 2px;
|
||||
}
|
||||
.card-scene { font-size: 12px; color: #6b7280; margin-top: 2px; }
|
||||
.card-summary { font-size: 13px; color: #4b5563; line-height: 1.7; }
|
||||
.card-source {
|
||||
font-size: 12px; color: #4338ca; background: #f5f3ff; border: 1px solid #ede9fe;
|
||||
border-radius: 8px; padding: 8px 10px; line-height: 1.6;
|
||||
}
|
||||
.card-source b { color: #3730a3; }
|
||||
details.card-detail { font-size: 12px; }
|
||||
details.card-detail summary { cursor: pointer; color: #6b7280; font-weight: 600; }
|
||||
.meta { margin-top: 8px; border-collapse: collapse; width: 100%; }
|
||||
.meta td { border-top: 1px solid #f3f4f6; padding: 5px 4px; vertical-align: top; }
|
||||
.meta td:first-child { color: #6b7280; width: 72px; white-space: nowrap; }
|
||||
.meta code { background: #f3f4f6; border-radius: 4px; padding: 1px 5px; font-size: 11px; color: #3730a3; }
|
||||
.card-foot {
|
||||
margin-top: auto; padding-top: 8px; border-top: 1px dashed #e5e7eb;
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.skill-status { font-size: 11px; color: #16a34a; font-weight: 600; }
|
||||
.btn-test {
|
||||
border: 1px solid #c7d2fe; background: #eef2ff; color: #4338ca;
|
||||
padding: 5px 12px; border-radius: 6px; font-size: 12px; cursor: pointer;
|
||||
}
|
||||
.btn-test:hover { background: #e0e7ff; }
|
||||
.test-out { margin-top: 8px; font-size: 12px; }
|
||||
.test-out pre {
|
||||
background: #1e1b4b; color: #c7d2fe; border-radius: 8px; padding: 10px;
|
||||
overflow: auto; max-height: 220px; font-size: 11px; line-height: 1.6;
|
||||
white-space: pre-wrap; word-break: break-all;
|
||||
}
|
||||
.test-ok { color: #16a34a; font-weight: 600; margin-bottom: 4px; }
|
||||
.test-err { color: #dc2626; font-weight: 600; }
|
||||
.test-pending { color: #6b7280; }
|
||||
|
||||
.empty { text-align: center; color: #6b7280; padding: 60px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="brand">
|
||||
<img class="icon" src="logo/logo-mark.svg" alt="画个标" width="20" height="20">
|
||||
<span class="name">画个标</span>
|
||||
<span class="tag">技能中心</span>
|
||||
</div>
|
||||
<button class="btn-back" onclick="location.href='index.html'">← 返回设计工作台</button>
|
||||
</div>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="stats">
|
||||
<div class="stat-card"><div class="num" id="st-total">-</div><div class="lab">设计技能总数(个)</div></div>
|
||||
<div class="stat-card"><div class="num" id="st-cats">-</div><div class="lab">技能分类(类)</div></div>
|
||||
<div class="stat-card"><div class="num" id="st-ok">-</div><div class="lab">可直接调用(内置可用)</div></div>
|
||||
</div>
|
||||
|
||||
<div class="guide">
|
||||
<b>🧩 技能中心说明</b>
|
||||
<div>全部技能按<b>设计流程 5 步</b>组织:<b>① 品牌洞察</b>(提炼关键词与方向)→ <b>② 方案生成</b>(色板/字体/布局建议)→ <b>③ 方案评审</b>(生成后对每套方案统一体检,出检查报告)→ <b>④ 迭代优化</b>(按评审结果改进)→ <b>⑤ 交付导出</b>(多格式导出与清单)。每个技能均为内置可用的设计能力,可在此「🔬 测试运行」单独验证。</div>
|
||||
<div style="margin-top:6px; color:#3730a3;">每个技能带性质徽章:<b>🎨 建议</b>(生成时给出设计建议)· <b>🔬 检查</b>(对方案体检出报告)· <b>📦 交付</b>(导出与清单)。技能来源含 GitHub 精选设计 skill 仓库(如 <code>op7418/logo-generator-skill</code>、<code>atypica-ai/logo-design-skill</code> 等)与自研设计规范,均已在代码中落地为可调用函数。</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="step-filters" id="step-filters"></div>
|
||||
<span class="step-divider">|</span>
|
||||
<div class="filters" id="filters"></div>
|
||||
<span class="step-divider">|</span>
|
||||
<div class="filters" id="ws-filters"></div>
|
||||
<input class="search" id="search" placeholder="🔍 搜索技能(中文名 / 场景 / 摘要 / 来源)">
|
||||
</div>
|
||||
|
||||
<div class="grid" id="grid"></div>
|
||||
<div class="empty hidden" id="empty">技能数据加载失败,请确认 js/skills/*.js 已加载。</div>
|
||||
</div>
|
||||
|
||||
<!-- 技能依赖的数据与核心库(供「测试运行」完整执行) -->
|
||||
<script src="js/data/icons.js"></script>
|
||||
<script src="js/data/industries.js"></script>
|
||||
<script src="js/data/styles.js?v=20260813"></script>
|
||||
<script src="js/data/palettes.js"></script>
|
||||
<script src="js/data/fonts.js"></script>
|
||||
<script src="js/data/decorations.js"></script>
|
||||
<script src="js/core/store.js"></script>
|
||||
<script src="js/core/exporter.js?v=20260812h"></script>
|
||||
<script src="js/core/generator.js?v=20260812h"></script>
|
||||
<script src="js/core/avatarGen.js?v=20260812h"></script>
|
||||
<script src="js/core/dicebear.js?v=20260822e"></script>
|
||||
<script src="js/core/ai.js?v=20260927a"></script>
|
||||
<script src="js/core/aiDesigner.js?v=20260812h"></script>
|
||||
<script src="js/data/builtin-icons.js?v=20260823g"></script>
|
||||
<script src="js/core/iconlib.js?v=20260823g"></script>
|
||||
<script src="js/skills/core.js?v=20260824a"></script>
|
||||
<script src="js/skills/layout.js?v=20260823d"></script>
|
||||
<script src="js/skills/color.js?v=20260823d"></script>
|
||||
<script src="js/skills/typography.js?v=20260823d"></script>
|
||||
<script src="js/skills/icon.js?v=20260823d"></script>
|
||||
<script src="js/skills/ai.js?v=20260815b"></script>
|
||||
<script src="js/skills/presentation.js?v=20260823d"></script>
|
||||
<script src="js/skills/export.js?v=20260823d"></script>
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
const CATS = {
|
||||
layout: '构图', color: '配色', typography: '字体',
|
||||
icon: '图标', ai: '智能', presentation: '展示', export: '导出'
|
||||
};
|
||||
const CATS_EMOJI = {
|
||||
layout: '📐', color: '🎨', typography: '🔤',
|
||||
icon: '🪄', ai: '🤖', presentation: '🖼️', export: '📦'
|
||||
};
|
||||
const STEP_NAMES = HGB_Skills.steps(); // 设计流程 5 步元数据
|
||||
const KIND_LABEL = { gen: '🎨 建议', check: '🔬 检查', deliver: '📦 交付' };
|
||||
let filter = 'all'; // 分类筛选
|
||||
let stepFilter = null; // 流程步骤筛选(1-5)
|
||||
let wsFilter = null; // 工作区筛选(logo/avatar/poster)
|
||||
let keyword = '';
|
||||
const WS = { logo: '🎨 LOGO', avatar: '🖼 头像', poster: '📰 海报' };
|
||||
|
||||
function esc(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<')
|
||||
.replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function renderStats() {
|
||||
document.getElementById('st-total').textContent = HGB_Skills.all().length;
|
||||
document.getElementById('st-cats').textContent = HGB_Skills.categories().length;
|
||||
document.getElementById('st-ok').textContent = HGB_Skills.all().length + ' 个';
|
||||
}
|
||||
|
||||
function renderStepFilters() {
|
||||
const cats = [{ step: null, name: '全部流程 ' + HGB_Skills.all().length }]
|
||||
.concat(STEP_NAMES.map(s => ({
|
||||
step: s.step,
|
||||
name: s.emoji + ' ' + s.name + ' ' + HGB_Skills.byStep(s.step).length
|
||||
})));
|
||||
document.getElementById('step-filters').innerHTML = cats.map(c =>
|
||||
`<button class="fchip stepped ${stepFilter === c.step ? 'active' : ''}" data-step="${c.step === null ? '__all__' : c.step}">${esc(c.name)}</button>`
|
||||
).join('');
|
||||
document.querySelectorAll('#step-filters [data-step]').forEach(b =>
|
||||
b.addEventListener('click', () => {
|
||||
const v = b.dataset.step;
|
||||
stepFilter = v === '__all__' ? null : parseInt(v, 10);
|
||||
if (stepFilter) filter = 'all'; // 切步骤时清除分类筛选
|
||||
renderStepFilters();
|
||||
renderFilters();
|
||||
render();
|
||||
}));
|
||||
}
|
||||
|
||||
function renderFilters() {
|
||||
const cats = [{ id: 'all', name: '全部 ' + HGB_Skills.all().length }]
|
||||
.concat(HGB_Skills.categories().map(c => ({
|
||||
id: c.id, name: (CATS_EMOJI[c.id] || '') + ' ' + (CATS[c.id] || c.id) + ' ' + c.count
|
||||
})));
|
||||
document.getElementById('filters').innerHTML = cats.map(c =>
|
||||
`<button class="fchip ${filter === c.id ? 'active' : ''}" data-f="${esc(c.id)}">${esc(c.name)}</button>`
|
||||
).join('');
|
||||
document.querySelectorAll('#filters [data-f]').forEach(b =>
|
||||
b.addEventListener('click', () => {
|
||||
filter = b.dataset.f;
|
||||
if (filter !== 'all') stepFilter = null; // 切分类时清除步骤筛选
|
||||
renderFilters();
|
||||
renderStepFilters();
|
||||
render();
|
||||
}));
|
||||
}
|
||||
|
||||
function renderWsFilters() {
|
||||
const opts = [{ id: '__all__', name: '全工作区 ' + HGB_Skills.all().length }]
|
||||
.concat([{ id: 'logo', name: WS.logo }, { id: 'avatar', name: WS.avatar }, { id: 'poster', name: WS.poster }]
|
||||
.map(w => ({ id: w.id, name: w.name + ' ' + HGB_Skills.forWorkspace(w.id).length })));
|
||||
document.getElementById('ws-filters').innerHTML = opts.map(c =>
|
||||
`<button class="fchip ${wsFilter === (c.id === '__all__' ? null : c.id) ? 'active' : ''}" data-ws="${esc(c.id)}">${esc(c.name)}</button>`
|
||||
).join('');
|
||||
document.querySelectorAll('#ws-filters [data-ws]').forEach(b =>
|
||||
b.addEventListener('click', () => {
|
||||
wsFilter = b.dataset.ws === '__all__' ? null : b.dataset.ws;
|
||||
renderWsFilters();
|
||||
render();
|
||||
}));
|
||||
}
|
||||
|
||||
function render() {
|
||||
const kw = keyword.trim().toLowerCase();
|
||||
const list = HGB_Skills.all().filter(s => {
|
||||
if (stepFilter && HGB_Skills.stepOf(s.id) !== stepFilter) return false;
|
||||
if (!stepFilter && filter !== 'all' && s.category !== filter) return false;
|
||||
if (wsFilter && !HGB_Skills.workspacesOf(s.id).includes(wsFilter)) return false;
|
||||
if (!kw) return true;
|
||||
const hay = [s.name, s.scene, s.summary, s.source, s.id, (CATS[s.category] || '')].join(' ').toLowerCase();
|
||||
return hay.includes(kw);
|
||||
});
|
||||
const grid = document.getElementById('grid');
|
||||
if (!list.length) {
|
||||
grid.innerHTML = '';
|
||||
document.getElementById('empty').classList.remove('hidden');
|
||||
document.getElementById('empty').textContent = '未找到匹配技能,请调整筛选或搜索词。';
|
||||
return;
|
||||
}
|
||||
document.getElementById('empty').classList.add('hidden');
|
||||
grid.innerHTML = list.map(s => {
|
||||
const st = STEP_NAMES.find(x => x.step === HGB_Skills.stepOf(s.id));
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="skill-emoji">${s.emoji || '🧩'}</span>
|
||||
<div>
|
||||
<div class="card-title">${esc(s.name)}
|
||||
<span class="card-cat">${CATS_EMOJI[s.category] || ''} ${esc(CATS[s.category] || s.category)}</span>
|
||||
<span class="card-step" title="${esc(st ? st.desc : '')}">${st ? st.emoji + ' ' + st.name : ''}</span>
|
||||
<span class="card-step" style="background:#f5f3ff;color:#6d28d9;">${KIND_LABEL[HGB_Skills.kind(s.id)] || ''}</span>
|
||||
</div>
|
||||
<div class="card-scene">📍 ${esc(s.scene)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-summary">${esc(s.summary)}</div>
|
||||
<div class="card-source"><b>学习来源:</b>${esc(s.source)}</div>
|
||||
<details class="card-detail">
|
||||
<summary>📋 调用参数与标识</summary>
|
||||
<table class="meta">
|
||||
<tr><td>技能 ID</td><td><code>${esc(s.id)}</code></td></tr>
|
||||
<tr><td>技能类别</td><td>${esc(s.category)}</td></tr>
|
||||
<tr><td>流程步骤</td><td>${st ? st.emoji + ' ' + st.name + '(' + esc(st.desc) + ')' : '方案生成'}</td></tr>
|
||||
<tr><td>调用参数</td><td>${s.params.map(p =>
|
||||
`<code>${esc(p.name)}</code> <span style="color:#9ca3af">${esc(p.type)}${p.required ? ' · 必填' : ''}</span> ${esc(p.desc)}`
|
||||
).join('<br>')}</td></tr>
|
||||
</table>
|
||||
</details>
|
||||
<div class="card-foot">
|
||||
<span class="skill-status">✅ 内置可用</span>
|
||||
<button class="btn-test" data-test="${esc(s.id)}">🔬 测试运行</button>
|
||||
</div>
|
||||
<div class="test-out"></div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
grid.querySelectorAll('[data-test]').forEach(btn =>
|
||||
btn.addEventListener('click', () => {
|
||||
const out = btn.closest('.card').querySelector('.test-out');
|
||||
const id = btn.dataset.test;
|
||||
out.innerHTML = '<div class="test-pending">⏳ 正在测试运行…</div>';
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const r = HGB_Skills.test(id);
|
||||
out.innerHTML = (r && r.error)
|
||||
? '<div class="test-err">❌ ' + esc(r.error) + '</div>'
|
||||
: '<div class="test-ok">✅ 测试通过</div><pre>' + esc(JSON.stringify(r, null, 2)) + '</pre>';
|
||||
} catch (e) {
|
||||
out.innerHTML = '<div class="test-err">❌ ' + esc(e.message) + '</div>';
|
||||
}
|
||||
}, 10);
|
||||
}));
|
||||
}
|
||||
|
||||
document.getElementById('search').addEventListener('input', (e) => {
|
||||
keyword = e.target.value;
|
||||
render();
|
||||
});
|
||||
|
||||
function init() {
|
||||
renderStats();
|
||||
renderStepFilters();
|
||||
renderFilters();
|
||||
renderWsFilters();
|
||||
render();
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user