Files
zjpb.net/templates/admin/skill/create.html
Jowe 3a63bc907e feat: 优化 Skills URL 自动识别
- 新增 /api/fetch-skill-info 接口识别 GitHub skills 仓库
- 解析 skill markdown 的简介、应用场景和包含内容
- Skills 创建页支持一键自动填充标题、简介、来源链接

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 23:11:34 +08:00

225 lines
8.5 KiB
HTML

{% extends 'admin/model/create.html' %}
{% block tail %}
{{ super() }}
<style>
.auto-fetch-btn {
margin-top: 10px;
margin-bottom: 15px;
}
.fetch-status {
margin-top: 10px;
padding: 10px;
border-radius: 8px;
display: none;
}
.fetch-status.success {
background-color: rgba(34, 197, 94, 0.1);
border: 1px solid rgba(34, 197, 94, 0.3);
color: #4ade80;
}
.fetch-status.error {
background-color: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
color: #f87171;
}
.logo-preview {
margin-top: 10px;
max-width: 100px;
max-height: 100px;
display: none;
}
.logo-preview img {
max-width: 100%;
max-height: 80px;
object-fit: contain;
border-radius: 8px;
border: 1px solid #ddd;
}
</style>
{% endblock %}
{% block header %}
<h2>添加 Skill</h2>
{% endblock %}
{% block form %}
<div class="form-group">
<label for="github_url">GitHub 仓库 URL <span style="color: #999; font-weight: normal;">(可选,填写后自动获取信息)</span></label>
<div style="display: flex; gap: 10px; align-items: flex-start;">
<input type="text" class="form-control" id="github_url" placeholder="例如: https://github.com/anthropics/claude-code" style="flex: 1;">
<button type="button" class="auto-fetch-btn btn btn-info" id="fetchSkillBtn">
<span class="material-symbols-outlined" style="vertical-align: middle;">auto_awesome</span>
自动识别
</button>
</div>
<div class="fetch-status" id="fetchStatus"></div>
</div>
<div class="form-group">
<label for="name">Skill 名称 <span style="color: #e74c3c;">*</span></label>
<input type="text" class="form-control" name="name" id="name" required placeholder="例如: PDF Helper">
</div>
<div class="form-group">
<label for="short_desc">简短描述</label>
<input type="text" class="form-control" name="short_desc" id="short_desc" placeholder="一句话描述这个 Skill 的用途">
</div>
<div class="form-group">
<label for="description">详细介绍</label>
<textarea class="form-control" name="description" id="description" rows="4" placeholder="详细介绍这个 Skill 的功能和使用场景"></textarea>
</div>
<div class="form-group">
<label for="logo">Logo 图片路径</label>
<input type="text" class="form-control" name="logo" id="logo" placeholder="/static/logos/xxx.png">
<div class="logo-preview" id="logoPreview">
<img id="logoImg" src="" alt="Logo预览">
</div>
</div>
<div class="form-group">
<label for="github_url_field">GitHub 链接</label>
<input type="text" class="form-control" name="github_url" id="github_url_field" placeholder="https://github.com/...">
</div>
<div class="form-group">
<label for="source_type">来源类型</label>
<select class="form-control" name="source_type" id="source_type">
<option value="manual" selected>手动添加</option>
<option value="github">GitHub</option>
</select>
</div>
<div class="form-group">
<label for="source_repo">来源仓库</label>
<input type="text" class="form-control" name="source_repo" id="source_repo" placeholder="owner/repo">
</div>
<div class="form-group">
<label for="usage">包含内容 / 应用场景</label>
<textarea class="form-control" name="usage" id="usage" rows="4" placeholder="这个 skills 仓库主要包含哪些内容、适合什么场景"></textarea>
</div>
<div class="form-group">
<label for="examples">来源链接 / 示例</label>
<textarea class="form-control" name="examples" id="examples" rows="4" placeholder="主要来源链接或示例链接"></textarea>
</div>
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="is_active" id="is_active" checked>
<label class="form-check-label" for="is_active">启用</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="is_featured" id="is_featured">
<label class="form-check-label" for="is_featured">推荐</label>
</div>
</div>
<div class="form-group">
<label for="sort_order">排序权重</label>
<input type="number" class="form-control" name="sort_order" id="sort_order" value="0" placeholder="数字越大越靠前">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">保存</button>
<a href="{{ url_for('skills.index_view') }}" class="btn btn-secondary">取消</a>
</div>
{% endblock %}
{% block tail2 %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const fetchBtn = document.getElementById('fetchSkillBtn');
const urlInput = document.getElementById('github_url');
const statusDiv = document.getElementById('fetchStatus');
const logoInput = document.getElementById('logo');
const logoPreview = document.getElementById('logoPreview');
const logoImg = document.getElementById('logoImg');
function showStatus(message, type) {
statusDiv.textContent = message;
statusDiv.className = 'fetch-status ' + type;
statusDiv.style.display = 'block';
}
if (logoInput) {
logoInput.addEventListener('input', function() {
if (this.value) {
logoImg.src = this.value;
logoPreview.style.display = 'block';
} else {
logoPreview.style.display = 'none';
}
});
}
if (fetchBtn && urlInput) {
fetchBtn.addEventListener('click', function() {
const url = urlInput.value.trim();
if (!url) {
showStatus('请先输入 GitHub 仓库 URL', 'error');
return;
}
if (!url.includes('github.com')) {
showStatus('请输入有效的 GitHub 仓库 URL', 'error');
return;
}
fetchBtn.disabled = true;
statusDiv.style.display = 'none';
fetch('/api/fetch-skill-info', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: url })
})
.then(response => response.json())
.then(data => {
if (!data.success) {
showStatus('✗ ' + (data.message || '获取失败,请手动填写'), 'error');
return;
}
const nameField = document.querySelector('input[name="name"]');
const shortDescField = document.querySelector('input[name="short_desc"]');
const descriptionField = document.querySelector('textarea[name="description"]');
const githubUrlField = document.querySelector('input[name="github_url"]');
const sourceRepoField = document.querySelector('input[name="source_repo"]');
const sourceTypeField = document.querySelector('select[name="source_type"]');
const usageField = document.querySelector('textarea[name="usage"]');
const examplesField = document.querySelector('textarea[name="examples"]');
if (nameField && data.data.name) nameField.value = data.data.name;
if (shortDescField && data.data.short_desc) shortDescField.value = data.data.short_desc.substring(0, 100);
if (descriptionField && data.data.description) descriptionField.value = data.data.description;
if (githubUrlField && data.data.github_url) githubUrlField.value = data.data.github_url;
if (sourceRepoField && data.data.source_repo) sourceRepoField.value = data.data.source_repo;
if (sourceTypeField && data.data.source_type) sourceTypeField.value = data.data.source_type;
if (usageField && data.data.usage) usageField.value = data.data.usage;
if (examplesField && data.data.examples) examplesField.value = data.data.examples;
showStatus('✓ Skill 信息获取成功!已自动填充标题、简介、应用场景和来源链接', 'success');
})
.catch(error => {
console.error('Error:', error);
showStatus('✗ 网络请求失败,请手动填写', 'error');
})
.finally(() => {
fetchBtn.disabled = false;
});
});
}
});
</script>
{% endblock %}