feat: 后台管理所有类别数据添加子类别字段和筛选功能
This commit is contained in:
@@ -159,6 +159,11 @@
|
||||
<button onclick="openAddModal('dynamic')" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700"><i class="ri-add-line mr-2"></i>手动添加</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 rounded-lg p-3 mb-4 flex items-center gap-4" id="dynamic-filter-area">
|
||||
<span class="text-sm text-gray-600">子类别筛选:</span>
|
||||
<button onclick="filterDynamicBySubcategory('')" class="px-3 py-1 rounded text-sm bg-white border text-gray-600 hover:bg-gray-100">全部</button>
|
||||
<div id="dynamic-subcategory-filters" class="flex gap-2"></div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden">
|
||||
<table class="w-full"><tbody id="admin-dynamic-table"><tr><td class="text-center text-gray-400 py-8">加载中...</td></tr></tbody></table>
|
||||
</div>
|
||||
@@ -173,6 +178,11 @@
|
||||
<button onclick="openAddModal('model')" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700"><i class="ri-add-line mr-2"></i>手动添加</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 rounded-lg p-3 mb-4 flex items-center gap-4">
|
||||
<span class="text-sm text-gray-600">子类别筛选:</span>
|
||||
<button onclick="filterModelsBySubcategory('')" class="px-3 py-1 rounded text-sm bg-white border text-gray-600 hover:bg-gray-100">全部</button>
|
||||
<div id="model-subcategory-filters" class="flex gap-2"></div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-x-auto">
|
||||
<table class="w-full min-w-[1200px]">
|
||||
<thead class="bg-gray-50 border-b">
|
||||
@@ -180,18 +190,17 @@
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">置顶</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">名称</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">厂商</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">子类别</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">参数量</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">上下文</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">类型</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">发布日期</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">热度</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">创建时间</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">更新时间</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium text-gray-600">显示</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium text-gray-600">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="admin-models-table"><tr><td colspan="12" class="text-center text-gray-400 py-8">加载中...</td></tr></tbody>
|
||||
<tbody id="admin-models-table"><tr><td colspan="11" class="text-center text-gray-400 py-8">加载中...</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@@ -205,6 +214,11 @@
|
||||
<button onclick="openAddModal('gpu')" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700"><i class="ri-add-line mr-2"></i>手动添加</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 rounded-lg p-3 mb-4 flex items-center gap-4">
|
||||
<span class="text-sm text-gray-600">子类别筛选:</span>
|
||||
<button onclick="filterGpusBySubcategory('')" class="px-3 py-1 rounded text-sm bg-white border text-gray-600 hover:bg-gray-100">全部</button>
|
||||
<div id="gpu-subcategory-filters" class="flex gap-2"></div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-x-auto">
|
||||
<table class="w-full min-w-[1200px]">
|
||||
<thead class="bg-gray-50 border-b">
|
||||
@@ -212,18 +226,17 @@
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">置顶</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">名称</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">厂商</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">子类别</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">显存</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">架构</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">价格</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">发布日期</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">热度</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">创建时间</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">更新时间</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium text-gray-600">显示</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium text-gray-600">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="admin-gpus-table"><tr><td colspan="12" class="text-center text-gray-400 py-8">加载中...</td></tr></tbody>
|
||||
<tbody id="admin-gpus-table"><tr><td colspan="11" class="text-center text-gray-400 py-8">加载中...</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@@ -237,6 +250,11 @@
|
||||
<button onclick="openAddModal('cpu')" class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"><i class="ri-add-line mr-2"></i>手动添加</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 rounded-lg p-3 mb-4 flex items-center gap-4">
|
||||
<span class="text-sm text-gray-600">子类别筛选:</span>
|
||||
<button onclick="filterCpusBySubcategory('')" class="px-3 py-1 rounded text-sm bg-white border text-gray-600 hover:bg-gray-100">全部</button>
|
||||
<div id="cpu-subcategory-filters" class="flex gap-2"></div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-x-auto">
|
||||
<table class="w-full min-w-[1200px]">
|
||||
<thead class="bg-gray-50 border-b">
|
||||
@@ -244,18 +262,17 @@
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">置顶</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">名称</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">厂商</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">子类别</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">核心/线程</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">主频</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">价格</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">发布日期</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">热度</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">创建时间</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium text-gray-600">更新时间</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium text-gray-600">显示</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium text-gray-600">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="admin-cpus-table"><tr><td colspan="12" class="text-center text-gray-400 py-8">加载中...</td></tr></tbody>
|
||||
<tbody id="admin-cpus-table"><tr><td colspan="11" class="text-center text-gray-400 py-8">加载中...</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@@ -448,11 +465,46 @@
|
||||
let categories = [];
|
||||
let currentFilter = '';
|
||||
let dynamicCategoryId = '';
|
||||
let modelSubcategoryFilter = '';
|
||||
let gpuSubcategoryFilter = '';
|
||||
let cpuSubcategoryFilter = '';
|
||||
let dynamicSubcategoryFilter = '';
|
||||
|
||||
// 生成随机ID(12位十六进制)
|
||||
function generateId() {
|
||||
return Math.random().toString(16).slice(2, 8) + Math.random().toString(16).slice(2, 8);
|
||||
}
|
||||
|
||||
// 获取子类别名称
|
||||
function getSubcategoryName(categoryId, subcategoryId) {
|
||||
const cat = categories.find(c => c.id === categoryId);
|
||||
if (!cat || !cat.subcategories) return '-';
|
||||
const sub = cat.subcategories.find(s => s.id === subcategoryId);
|
||||
return sub ? sub.name : '-';
|
||||
}
|
||||
|
||||
// 获取子类别图标
|
||||
function getSubcategoryIcon(categoryId, subcategoryId) {
|
||||
const cat = categories.find(c => c.id === categoryId);
|
||||
if (!cat || !cat.subcategories) return '';
|
||||
const sub = cat.subcategories.find(s => s.id === subcategoryId);
|
||||
return sub ? sub.icon : '';
|
||||
}
|
||||
|
||||
// 渲染子类别筛选按钮
|
||||
function renderSubcategoryFilters(categoryId, containerId, filterFunction) {
|
||||
const cat = categories.find(c => c.id === categoryId);
|
||||
const container = document.getElementById(containerId);
|
||||
if (!cat || !cat.subcategories || cat.subcategories.length === 0) {
|
||||
container.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = cat.subcategories.map(sub => `
|
||||
<button onclick="${filterFunction}('${sub.id}')" class="px-3 py-1 rounded text-sm bg-white border text-gray-600 hover:bg-gray-100">
|
||||
<i class="${sub.icon} mr-1"></i>${sub.name}
|
||||
</button>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
const colorMap = {
|
||||
blue: 'bg-blue-100 text-blue-600',
|
||||
@@ -582,6 +634,7 @@
|
||||
// 显示动态分类数据
|
||||
async function showDynamicCategory(categoryId) {
|
||||
dynamicCategoryId = categoryId;
|
||||
dynamicSubcategoryFilter = ''; // 重置筛选
|
||||
const cat = categories.find(c => c.id === categoryId);
|
||||
|
||||
document.querySelectorAll('section').forEach(s => s.classList.add('hidden'));
|
||||
@@ -599,21 +652,44 @@
|
||||
|
||||
document.getElementById('dynamic-title').textContent = cat.name + '管理';
|
||||
|
||||
// 渲染子类别筛选按钮
|
||||
if (cat.subcategories && cat.subcategories.length > 0) {
|
||||
document.getElementById('dynamic-filter-area').classList.remove('hidden');
|
||||
renderSubcategoryFilters(categoryId, 'dynamic-subcategory-filters', 'filterDynamicBySubcategory');
|
||||
} else {
|
||||
document.getElementById('dynamic-filter-area').classList.add('hidden');
|
||||
}
|
||||
|
||||
// 加载该分类的数据(后台显示全部,包括隐藏的)
|
||||
const res = await fetch(`/api/items/${categoryId}?all=1`);
|
||||
const items = await res.json();
|
||||
let items = await res.json();
|
||||
|
||||
// 子类别筛选
|
||||
if (dynamicSubcategoryFilter) {
|
||||
items = items.filter(i => i.subcategory_id === dynamicSubcategoryFilter);
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
document.getElementById('admin-dynamic-table').innerHTML = '<tr><td class="text-center text-gray-400 py-8">暂无数据,点击上方"添加数据"按钮添加</td></tr>';
|
||||
} else {
|
||||
const keys = Object.keys(items[0]).filter(k => !['id', 'created_at', 'updated_at', 'visible', 'raw_text'].includes(k));
|
||||
const keys = Object.keys(items[0]).filter(k => !['id', 'created_at', 'updated_at', 'visible', 'raw_text', 'subcategory_id'].includes(k));
|
||||
let html = `<thead class="bg-gray-50 border-b"><tr>`;
|
||||
// 添加子类别列
|
||||
if (cat.subcategories && cat.subcategories.length > 0) {
|
||||
html += `<th class="px-4 py-3 text-left text-sm font-medium text-gray-600">子类别</th>`;
|
||||
}
|
||||
keys.forEach(k => { html += `<th class="px-4 py-3 text-left text-sm font-medium text-gray-600">${k}</th>`; });
|
||||
html += `<th class="px-4 py-3 text-center text-sm font-medium text-gray-600">显示</th>`;
|
||||
html += `<th class="px-4 py-3 text-center text-sm font-medium text-gray-600">操作</th></tr></thead><tbody>`;
|
||||
|
||||
items.forEach(item => {
|
||||
html += `<tr class="border-b hover:bg-gray-50 ${item.visible === false ? 'bg-gray-100 opacity-60' : ''}">`;
|
||||
// 子类别显示
|
||||
if (cat.subcategories && cat.subcategories.length > 0) {
|
||||
const subName = getSubcategoryName(categoryId, item.subcategory_id);
|
||||
const subIcon = getSubcategoryIcon(categoryId, item.subcategory_id);
|
||||
html += `<td class="px-4 py-3">${item.subcategory_id ? `<span class="px-2 py-1 bg-indigo-100 text-indigo-600 rounded text-xs"><i class="${subIcon} mr-1"></i>${subName}</span>` : '<span class="text-gray-400">-</span>'}</td>`;
|
||||
}
|
||||
keys.forEach(k => { html += `<td class="px-4 py-3 text-gray-600">${item[k] || '-'}</td>`; });
|
||||
html += `<td class="px-4 py-3 text-center">
|
||||
<button onclick="toggleVisible('dynamic', '${item.id}')" class="${item.visible === false ? 'text-gray-400' : 'text-green-600'} hover:opacity-80" title="${item.visible === false ? '点击显示' : '点击隐藏'}">
|
||||
@@ -630,6 +706,11 @@
|
||||
document.getElementById('admin-dynamic-table').innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
function filterDynamicBySubcategory(subId) {
|
||||
dynamicSubcategoryFilter = subId;
|
||||
showDynamicCategory(dynamicCategoryId);
|
||||
}
|
||||
|
||||
// 切换显示区域
|
||||
function showSection(section) {
|
||||
@@ -800,9 +881,16 @@
|
||||
|
||||
// 加载模型列表
|
||||
async function loadAdminModels() {
|
||||
renderSubcategoryFilters('ai-models', 'model-subcategory-filters', 'filterModelsBySubcategory');
|
||||
const res = await fetch('/api/models?all=1');
|
||||
const models = await res.json();
|
||||
if (models.length === 0) { document.getElementById('admin-models-table').innerHTML = '<tr><td colspan="12" class="text-center text-gray-400 py-8">暂无数据</td></tr>'; return; }
|
||||
let models = await res.json();
|
||||
|
||||
// 子类别筛选
|
||||
if (modelSubcategoryFilter) {
|
||||
models = models.filter(m => m.subcategory_id === modelSubcategoryFilter);
|
||||
}
|
||||
|
||||
if (models.length === 0) { document.getElementById('admin-models-table').innerHTML = '<tr><td colspan="11" class="text-center text-gray-400 py-8">暂无数据</td></tr>'; return; }
|
||||
document.getElementById('admin-models-table').innerHTML = models.map(m => `
|
||||
<tr class="border-b hover:bg-gray-50 ${m.visible === false ? 'bg-gray-100 opacity-60' : ''} ${m.is_pinned ? 'bg-yellow-50' : ''}">
|
||||
<td class="px-3 py-3 text-center">
|
||||
@@ -812,13 +900,14 @@
|
||||
</td>
|
||||
<td class="px-3 py-3 font-medium text-gray-800">${m.name}</td>
|
||||
<td class="px-3 py-3 text-gray-600">${m.organization}</td>
|
||||
<td class="px-3 py-3">
|
||||
${m.subcategory_id ? `<span class="px-2 py-1 bg-blue-100 text-blue-600 rounded text-xs"><i class="${getSubcategoryIcon('ai-models', m.subcategory_id)} mr-1"></i>${getSubcategoryName('ai-models', m.subcategory_id)}</span>` : '<span class="text-gray-400">-</span>'}
|
||||
</td>
|
||||
<td class="px-3 py-3">${m.parameters}B</td>
|
||||
<td class="px-3 py-3 text-gray-600">${m.context_length || '-'}</td>
|
||||
<td class="px-3 py-3">${m.is_open_source ? '<span class="text-green-600">开源</span>' : '<span class="text-gray-600">商业</span>'}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${m.publish_date || '-'}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${m.views || 0}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${formatDateShort(m.created_at)}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${formatDateShort(m.updated_at)}</td>
|
||||
<td class="px-3 py-3 text-center">
|
||||
<button onclick="toggleVisible('model', '${m.id}')" class="${m.visible === false ? 'text-gray-400' : 'text-green-600'} hover:opacity-80" title="${m.visible === false ? '点击显示' : '点击隐藏'}">
|
||||
<i class="${m.visible === false ? 'ri-eye-off-line' : 'ri-eye-line'}"></i>
|
||||
@@ -832,12 +921,24 @@
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function filterModelsBySubcategory(subId) {
|
||||
modelSubcategoryFilter = subId;
|
||||
loadAdminModels();
|
||||
}
|
||||
|
||||
// 加载GPU列表
|
||||
async function loadAdminGpus() {
|
||||
renderSubcategoryFilters('gpus', 'gpu-subcategory-filters', 'filterGpusBySubcategory');
|
||||
const res = await fetch('/api/gpus?all=1');
|
||||
const gpus = await res.json();
|
||||
if (gpus.length === 0) { document.getElementById('admin-gpus-table').innerHTML = '<tr><td colspan="12" class="text-center text-gray-400 py-8">暂无数据</td></tr>'; return; }
|
||||
let gpus = await res.json();
|
||||
|
||||
// 子类别筛选
|
||||
if (gpuSubcategoryFilter) {
|
||||
gpus = gpus.filter(g => g.subcategory_id === gpuSubcategoryFilter);
|
||||
}
|
||||
|
||||
if (gpus.length === 0) { document.getElementById('admin-gpus-table').innerHTML = '<tr><td colspan="11" class="text-center text-gray-400 py-8">暂无数据</td></tr>'; return; }
|
||||
document.getElementById('admin-gpus-table').innerHTML = gpus.map(g => `
|
||||
<tr class="border-b hover:bg-gray-50 ${g.visible === false ? 'bg-gray-100 opacity-60' : ''} ${g.is_pinned ? 'bg-yellow-50' : ''}">
|
||||
<td class="px-3 py-3 text-center">
|
||||
@@ -847,13 +948,14 @@
|
||||
</td>
|
||||
<td class="px-3 py-3 font-medium text-gray-800">${g.name}</td>
|
||||
<td class="px-3 py-3 text-gray-600">${g.manufacturer}</td>
|
||||
<td class="px-3 py-3">
|
||||
${g.subcategory_id ? `<span class="px-2 py-1 bg-green-100 text-green-600 rounded text-xs"><i class="${getSubcategoryIcon('gpus', g.subcategory_id)} mr-1"></i>${getSubcategoryName('gpus', g.subcategory_id)}</span>` : '<span class="text-gray-400">-</span>'}
|
||||
</td>
|
||||
<td class="px-3 py-3">${g.memory_gb}GB</td>
|
||||
<td class="px-3 py-3 text-gray-600">${g.architecture || '-'}</td>
|
||||
<td class="px-3 py-3 text-gray-600">${formatPrice(g)}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${g.publish_date || '-'}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${g.views || 0}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${formatDateShort(g.created_at)}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${formatDateShort(g.updated_at)}</td>
|
||||
<td class="px-3 py-3 text-center">
|
||||
<button onclick="toggleVisible('gpu', '${g.id}')" class="${g.visible === false ? 'text-gray-400' : 'text-green-600'} hover:opacity-80" title="${g.visible === false ? '点击显示' : '点击隐藏'}">
|
||||
<i class="${g.visible === false ? 'ri-eye-off-line' : 'ri-eye-line'}"></i>
|
||||
@@ -867,12 +969,24 @@
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function filterGpusBySubcategory(subId) {
|
||||
gpuSubcategoryFilter = subId;
|
||||
loadAdminGpus();
|
||||
}
|
||||
|
||||
// 加载CPU列表
|
||||
async function loadAdminCpus() {
|
||||
renderSubcategoryFilters('cpus', 'cpu-subcategory-filters', 'filterCpusBySubcategory');
|
||||
const res = await fetch('/api/cpus?all=1');
|
||||
const cpus = await res.json();
|
||||
if (cpus.length === 0) { document.getElementById('admin-cpus-table').innerHTML = '<tr><td colspan="12" class="text-center text-gray-400 py-8">暂无数据</td></tr>'; return; }
|
||||
let cpus = await res.json();
|
||||
|
||||
// 子类别筛选
|
||||
if (cpuSubcategoryFilter) {
|
||||
cpus = cpus.filter(c => c.subcategory_id === cpuSubcategoryFilter);
|
||||
}
|
||||
|
||||
if (cpus.length === 0) { document.getElementById('admin-cpus-table').innerHTML = '<tr><td colspan="11" class="text-center text-gray-400 py-8">暂无数据</td></tr>'; return; }
|
||||
document.getElementById('admin-cpus-table').innerHTML = cpus.map(c => `
|
||||
<tr class="border-b hover:bg-gray-50 ${c.visible === false ? 'bg-gray-100 opacity-60' : ''} ${c.is_pinned ? 'bg-yellow-50' : ''}">
|
||||
<td class="px-3 py-3 text-center">
|
||||
@@ -882,13 +996,14 @@
|
||||
</td>
|
||||
<td class="px-3 py-3 font-medium text-gray-800">${c.name}</td>
|
||||
<td class="px-3 py-3 text-gray-600">${c.manufacturer}</td>
|
||||
<td class="px-3 py-3">
|
||||
${c.subcategory_id ? `<span class="px-2 py-1 bg-purple-100 text-purple-600 rounded text-xs"><i class="${getSubcategoryIcon('cpus', c.subcategory_id)} mr-1"></i>${getSubcategoryName('cpus', c.subcategory_id)}</span>` : '<span class="text-gray-400">-</span>'}
|
||||
</td>
|
||||
<td class="px-3 py-3">${c.cores}/${c.threads}</td>
|
||||
<td class="px-3 py-3 text-gray-600">${c.base_clock_ghz || '-'}-${c.boost_clock_ghz || '-'}GHz</td>
|
||||
<td class="px-3 py-3 text-gray-600">${formatPrice(c)}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${c.publish_date || '-'}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${c.views || 0}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${formatDateShort(c.created_at)}</td>
|
||||
<td class="px-3 py-3 text-gray-500 text-sm">${formatDateShort(c.updated_at)}</td>
|
||||
<td class="px-3 py-3 text-center">
|
||||
<button onclick="toggleVisible('cpu', '${c.id}')" class="${c.visible === false ? 'text-gray-400' : 'text-green-600'} hover:opacity-80" title="${c.visible === false ? '点击显示' : '点击隐藏'}">
|
||||
<i class="${c.visible === false ? 'ri-eye-off-line' : 'ri-eye-line'}"></i>
|
||||
@@ -902,6 +1017,11 @@
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function filterCpusBySubcategory(subId) {
|
||||
cpuSubcategoryFilter = subId;
|
||||
loadAdminCpus();
|
||||
}
|
||||
|
||||
// 加载知识列表
|
||||
async function loadAdminKnowledge() {
|
||||
@@ -1421,11 +1541,28 @@
|
||||
</form>`;
|
||||
}
|
||||
|
||||
// 生成子类别选择器
|
||||
function getSubcategorySelect(categoryId, currentValue = '') {
|
||||
const cat = categories.find(c => c.id === categoryId);
|
||||
if (!cat || !cat.subcategories || cat.subcategories.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const options = cat.subcategories.map(sub =>
|
||||
`<option value="${sub.id}" ${currentValue === sub.id ? 'selected' : ''}><i class="${sub.icon} mr-1"></i>${sub.name}</option>`
|
||||
).join('');
|
||||
|
||||
return `<div><label class="text-sm text-gray-600 mb-1 block">子类别</label><select name="subcategory_id" class="w-full px-3 py-2 border rounded-lg"><option value="">请选择</option>${options}</select></div>`;
|
||||
}
|
||||
|
||||
function getDynamicForm(data = {}) {
|
||||
const cat = categories.find(c => c.id === dynamicCategoryId);
|
||||
currentImages = data.images || [];
|
||||
const subcategorySelect = getSubcategorySelect(dynamicCategoryId, data.subcategory_id);
|
||||
|
||||
return `<form id="itemForm" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
${subcategorySelect}
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">名称 *</label><input type="text" name="name" value="${data.name || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">品牌</label><input type="text" name="brand" value="${data.brand || ''}" class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">价格</label><input type="number" name="price" value="${data.price || ''}" step="0.01" class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
@@ -1441,8 +1578,11 @@
|
||||
|
||||
function getModelForm(data = {}) {
|
||||
currentImages = data.images || [];
|
||||
const subcategorySelect = getSubcategorySelect('ai-models', data.subcategory_id);
|
||||
|
||||
return `<form id="itemForm" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
${subcategorySelect}
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">名称 *</label><input type="text" name="name" value="${data.name || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">厂商 *</label><input type="text" name="organization" value="${data.organization || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">参数量(B) *</label><input type="number" name="parameters" value="${data.parameters || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
@@ -1464,8 +1604,11 @@
|
||||
|
||||
function getGpuForm(data = {}) {
|
||||
currentImages = data.images || [];
|
||||
const subcategorySelect = getSubcategorySelect('gpus', data.subcategory_id);
|
||||
|
||||
return `<form id="itemForm" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
${subcategorySelect}
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">名称 *</label><input type="text" name="name" value="${data.name || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">厂商 *</label><input type="text" name="manufacturer" value="${data.manufacturer || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">架构</label><input type="text" name="architecture" value="${data.architecture || ''}" class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
@@ -1493,8 +1636,11 @@
|
||||
|
||||
function getCpuForm(data = {}) {
|
||||
currentImages = data.images || [];
|
||||
const subcategorySelect = getSubcategorySelect('cpus', data.subcategory_id);
|
||||
|
||||
return `<form id="itemForm" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
${subcategorySelect}
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">名称 *</label><input type="text" name="name" value="${data.name || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">厂商 *</label><input type="text" name="manufacturer" value="${data.manufacturer || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">架构</label><input type="text" name="architecture" value="${data.architecture || ''}" class="w-full px-3 py-2 border rounded-lg"></div>
|
||||
|
||||
Reference in New Issue
Block a user