Files
drawlogo/skills.html
T

374 lines
18 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
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>