主要功能: - 完整的Flask-Admin后台管理系统 - 网站/标签/新闻管理功能 - 用户登录认证系统 - 科技感/未来风UI设计 - 标签分类系统(取代传统分类) - 详情页面展示 - 数据库迁移脚本 - 书签导入解析工具 技术栈: - Flask + SQLAlchemy - Flask-Admin管理界面 - Bootstrap 4响应式设计 - 用户认证与权限管理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
204 lines
7.4 KiB
HTML
204 lines
7.4 KiB
HTML
{% extends 'admin/model/create.html' %}
|
||
|
||
{% block tail %}
|
||
{{ super() }}
|
||
<style>
|
||
.auto-fetch-btn {
|
||
margin-top: 10px;
|
||
margin-bottom: 15px;
|
||
}
|
||
.generate-tags-btn {
|
||
margin-top: 10px;
|
||
margin-bottom: 15px;
|
||
}
|
||
.fetch-status {
|
||
margin-top: 10px;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
display: none;
|
||
}
|
||
.tags-status {
|
||
margin-top: 10px;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
display: none;
|
||
}
|
||
.fetch-status.success, .tags-status.success {
|
||
background-color: rgba(34, 197, 94, 0.1);
|
||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||
color: #4ade80;
|
||
}
|
||
.fetch-status.error, .tags-status.error {
|
||
background-color: rgba(239, 68, 68, 0.1);
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
color: #f87171;
|
||
}
|
||
.auto-fetch-btn .loading-icon, .generate-tags-btn .loading-icon {
|
||
display: none;
|
||
}
|
||
.auto-fetch-btn.loading .loading-icon, .generate-tags-btn.loading .loading-icon {
|
||
display: inline-block;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
.auto-fetch-btn.loading .normal-icon, .generate-tags-btn.loading .normal-icon {
|
||
display: none;
|
||
}
|
||
@keyframes spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 在URL字段后添加"自动获取"按钮
|
||
const urlField = document.querySelector('input[name="url"]');
|
||
if (urlField) {
|
||
const fetchBtn = document.createElement('button');
|
||
fetchBtn.type = 'button';
|
||
fetchBtn.className = 'btn btn-info auto-fetch-btn';
|
||
fetchBtn.innerHTML = '<span class="normal-icon">↻</span><span class="loading-icon">↻</span> 自动获取网站信息';
|
||
|
||
const statusDiv = document.createElement('div');
|
||
statusDiv.className = 'fetch-status';
|
||
|
||
urlField.parentNode.appendChild(fetchBtn);
|
||
urlField.parentNode.appendChild(statusDiv);
|
||
|
||
fetchBtn.addEventListener('click', function() {
|
||
const url = urlField.value.trim();
|
||
|
||
if (!url) {
|
||
showStatus('请先输入网站URL', 'error');
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
fetchBtn.disabled = true;
|
||
fetchBtn.classList.add('loading');
|
||
statusDiv.style.display = 'none';
|
||
|
||
// 调用API获取网站信息
|
||
fetch('/api/fetch-website-info', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ url: url })
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
// 自动填充表单字段
|
||
const nameField = document.querySelector('input[name="name"]');
|
||
const shortDescField = document.querySelector('input[name="short_desc"]');
|
||
const descriptionField = document.querySelector('textarea[name="description"]');
|
||
const logoField = document.querySelector('input[name="logo"]');
|
||
|
||
if (nameField && data.data.name) {
|
||
nameField.value = data.data.name;
|
||
}
|
||
if (shortDescField && data.data.description) {
|
||
shortDescField.value = data.data.description.substring(0, 100);
|
||
}
|
||
if (descriptionField && data.data.description) {
|
||
descriptionField.value = data.data.description;
|
||
}
|
||
if (logoField && data.data.logo) {
|
||
logoField.value = data.data.logo;
|
||
}
|
||
|
||
showStatus('✓ 网站信息获取成功!已自动填充表单', 'success');
|
||
} else {
|
||
showStatus('✗ ' + (data.message || '获取失败,请手动填写'), 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showStatus('✗ 网络请求失败,请手动填写', 'error');
|
||
})
|
||
.finally(() => {
|
||
fetchBtn.disabled = false;
|
||
fetchBtn.classList.remove('loading');
|
||
});
|
||
});
|
||
|
||
function showStatus(message, type) {
|
||
statusDiv.textContent = message;
|
||
statusDiv.className = 'fetch-status ' + type;
|
||
statusDiv.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
// 在标签字段后添加"AI生成标签"按钮
|
||
const tagsField = document.querySelector('select[name="tags"]');
|
||
if (tagsField) {
|
||
const generateBtn = document.createElement('button');
|
||
generateBtn.type = 'button';
|
||
generateBtn.className = 'btn btn-success generate-tags-btn';
|
||
generateBtn.innerHTML = '<span class="normal-icon">✨</span><span class="loading-icon">↻</span> AI生成标签';
|
||
|
||
const tagsStatusDiv = document.createElement('div');
|
||
tagsStatusDiv.className = 'tags-status';
|
||
|
||
tagsField.parentNode.appendChild(generateBtn);
|
||
tagsField.parentNode.appendChild(tagsStatusDiv);
|
||
|
||
generateBtn.addEventListener('click', function() {
|
||
const nameField = document.querySelector('input[name="name"]');
|
||
const descriptionField = document.querySelector('textarea[name="description"]');
|
||
|
||
const name = nameField ? nameField.value.trim() : '';
|
||
const description = descriptionField ? descriptionField.value.trim() : '';
|
||
|
||
if (!name || !description) {
|
||
showTagsStatus('请先填写网站名称和描述', 'error');
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
generateBtn.disabled = true;
|
||
generateBtn.classList.add('loading');
|
||
tagsStatusDiv.style.display = 'none';
|
||
|
||
// 调用API生成标签
|
||
fetch('/api/generate-tags', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
name: name,
|
||
description: description
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success && data.tags && data.tags.length > 0) {
|
||
// 显示生成的标签
|
||
const tagsText = data.tags.join(', ');
|
||
showTagsStatus('✓ AI生成的标签建议: ' + tagsText + '\n(请在标签字段中手动选择或创建这些标签)', 'success');
|
||
} else {
|
||
showTagsStatus('✗ ' + (data.message || '标签生成失败'), 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showTagsStatus('✗ 网络请求失败,请重试', 'error');
|
||
})
|
||
.finally(() => {
|
||
generateBtn.disabled = false;
|
||
generateBtn.classList.remove('loading');
|
||
});
|
||
});
|
||
|
||
function showTagsStatus(message, type) {
|
||
tagsStatusDiv.textContent = message;
|
||
tagsStatusDiv.className = 'tags-status ' + type;
|
||
tagsStatusDiv.style.display = 'block';
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %}
|