8 Commits

Author SHA1 Message Date
2c981cb363 feat: 手机类别字段全面重构
- 删除原有简单字段(processor/ram_gb/storage_gb/screen_size/battery_mah)
- 新增52个详细字段,覆盖手机完整参数:
  - 基本信息:型号、品牌、发布日期、上市时间、产品级别、价格
  - 系统与处理器:操作系统、处理器(SoC)、GPU
  - 存储与内存:运行内存、存储空间、存储扩展
  - 屏幕:尺寸、类型、分辨率、刷新率、LTPO、亮度、屏幕特色
  - 摄像头:后置主摄、超广角、长焦、前置、视频录制、摄像头特色
  - 电池与充电:电池容量、有线快充、无线充电、电池特色
  - 连接与网络:运营商网络、Wi-Fi、蓝牙、NFC、红外遥控、定位、USB、SIM卡
  - 机身与外观:指纹识别、防水等级、面部识别、重量、厚度、材质、颜色、中框
  - 其他功能:扬声器、耳机接口、震动反馈
  - 特色与链接:特色、官网地址、数据参考、描述
- 字段名不带单位,数据提取时保留原始单位
- 更新子类别关键特性配置
- 智能添加prompt自动包含新字段配置
2026-04-30 13:00:48 +08:00
ae23ec42ad fix: 动态分类子类别筛选修复
- 移除showDynamicCategory中的dynamicSubcategoryFilter重置
- 改为在侧边栏点击时重置筛选值
- 点击子类别按钮时保持筛选值不被重置
- 手机、电脑、汽车、摄像等动态分类现在可以正确筛选
2026-04-29 23:03:33 +08:00
080e00b6c6 fix: 修复子类别筛选按钮点击无效问题
- 用containerId判断currentFilter,而非categoryId
- 用previousElementSibling找全部按钮,而非parent.querySelector
- 确保点击子类别后样式正确切换
- 全部按钮和子类别按钮互斥选中状态
2026-04-29 22:58:44 +08:00
6e69f09842 fix: 移除重复的"全部"按钮,只更新样式
- HTML中已有"全部"按钮,不需要重复添加
- renderSubcategoryFilters只渲染子类别按钮
- 同时更新"全部"按钮的选中/未选中样式
- 选中时蓝色背景白色文字,未选中时白色背景灰色文字
2026-04-29 22:51:16 +08:00
2dd45854c1 fix: 后台管理子类别筛选按钮显示选中状态
- 添加"全部"按钮,点击可清除筛选
- 选中状态:蓝色背景白色文字
- 未选中状态:白色背景灰色文字+边框
- 用户点击后能看到明显的视觉变化
- 根据当前筛选值动态更新按钮样式
2026-04-29 22:38:50 +08:00
72de68e1ff feat: 智能解析prompt改为保留原始单位
- 修改要求:从'不带单位'改为'保留原始单位'
- 字段标签中有单位标注时,提取数据带上对应单位
- 避免'提取数字不带单位'导致数据和单位不符
- 修改范围:
  - get_parse_prompt_template 函数
  - parse_with_llm 函数(图片解析和文本解析)
- 示例:显存(GB)字段提取'24GB'而不是'24'
2026-04-29 17:45:07 +08:00
6844d73c9d feat: 所有类别添加官网地址和数据参考字段
- 官网地址(official_urls): JSON数组,存储产品官网链接
- 数据参考(reference_urls): JSON数组,存储参数来源链接
- 格式示例: [{"url":"https://...","title":"..."}]
- 支持多个链接+标题
- 类型: JSON,长文本输入
- 覆盖: AI模型、GPU、CPU、手机、电脑、汽车、摄像全部7个类别
2026-04-29 17:30:32 +08:00
40b04ae9c1 feat: 所有类别添加特色(features)字段
- 字段名: features
- 显示名: 特色
- 类型: 文本
- 说明: 产品特色/亮点,简短描述
- 位置: 描述字段前面
- 覆盖: AI模型、GPU、CPU、手机、电脑、汽车、摄像全部7个类别
2026-04-29 17:20:26 +08:00
3 changed files with 694 additions and 162 deletions

6
app.py
View File

@@ -163,7 +163,7 @@ def get_parse_prompt_template(category_type, category_id=None, subcategory_id=No
重要要求:
1. 图片中可能包含1个或多个产品请识别所有产品
2. 如果是多张图片,请综合分析所有图片内容
3. 数字字段只返回数字,不带单位
3. **提取数据时保留原始单位**:字段标签中如有单位标注(如($)、(GB)、(MHz)等),提取时请带上对应单位,保持数据完整性
4. 如果某字段没有提及返回null
5. 返回格式:如果识别到多个产品,返回数组 [对象列表]; 如果只有一个产品,返回单个对象
6. 只返回JSON数据不要其他内容"""
@@ -255,7 +255,7 @@ def parse_with_llm(text, category_type, images=None, category_id=None, subcatego
重要要求:
1. 图片中可能包含1个或多个产品请识别所有产品
2. 如果是多张图片,请综合分析所有图片内容
3. 数字字段只返回数字,不带单位
3. **提取数据时保留原始单位**:字段标签中如有单位标注(如($)、(GB)、(MHz)等),提取时请带上对应单位,保持数据完整性
4. 如果某字段没有提及返回null
5. 返回格式:如果识别到多个产品,返回数组 [对象列表]; 如果只有一个产品,返回单个对象
6. 只返回JSON数据不要其他内容"""
@@ -307,7 +307,7 @@ def parse_with_llm(text, category_type, images=None, category_id=None, subcatego
要求:
1. 根据文本内容智能提取各个字段的值
2. 数字字段只返回数字,不带单位
2. **提取数据时保留原始单位**:字段标签中如有单位标注(如($)、(GB)、(MHz)等),提取时请带上对应单位,保持数据完整性
3. 如果某字段在文本中没有提及返回null
4. 返回JSON格式不要包含任何其他内容

File diff suppressed because it is too large Load Diff

View File

@@ -534,11 +534,30 @@
container.innerHTML = '';
return;
}
// 获取当前筛选值 - 根据容器ID判断
let currentFilter = '';
if (containerId === 'model-subcategory-filters') currentFilter = modelSubcategoryFilter;
else if (containerId === 'gpu-subcategory-filters') currentFilter = gpuSubcategoryFilter;
else if (containerId === 'cpu-subcategory-filters') currentFilter = cpuSubcategoryFilter;
else if (containerId === 'dynamic-subcategory-filters') currentFilter = dynamicSubcategoryFilter;
// 渲染子类别按钮
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 onclick="${filterFunction}('${sub.id}')" class="px-3 py-1 rounded text-sm ${currentFilter === sub.id ? 'bg-indigo-600 text-white' : 'bg-white border text-gray-600 hover:bg-gray-100'}">
<i class="${sub.icon || 'ri-folder-line'} mr-1"></i>${sub.name}
</button>
`).join('');
// 更新"全部"按钮的样式 - 它是容器前面的那个按钮
const allBtn = container.previousElementSibling;
if (allBtn && allBtn.tagName === 'BUTTON') {
if (currentFilter === '') {
allBtn.className = 'px-3 py-1 rounded text-sm bg-indigo-600 text-white';
} else {
allBtn.className = 'px-3 py-1 rounded text-sm bg-white border text-gray-600 hover:bg-gray-100';
}
}
}
const colorMap = {
@@ -647,7 +666,7 @@
} else {
// 动态分类
html += `
<a href="#cat-${cat.id}" onclick="showDynamicCategory('${cat.id}')" class="sidebar-link flex items-center gap-2 px-3 py-2 rounded-lg text-gray-300" data-section="cat-${cat.id}">
<a href="#cat-${cat.id}" onclick="dynamicSubcategoryFilter='';showDynamicCategory('${cat.id}')" class="sidebar-link flex items-center gap-2 px-3 py-2 rounded-lg text-gray-300" data-section="cat-${cat.id}">
<i class="${cat.icon}"></i>
<span>${cat.name}管理</span>
</a>
@@ -669,7 +688,8 @@
// 显示动态分类数据
async function showDynamicCategory(categoryId) {
dynamicCategoryId = categoryId;
dynamicSubcategoryFilter = '';
// 不要每次都重置筛选值,除非是新切换的分类
// dynamicSubcategoryFilter = ''; // 移除这行
const cat = categories.find(c => c.id === categoryId);
const fields = cat ? (cat.fields || []) : [];
const fixedFields = ['id', 'created_at', 'updated_at', 'visible', 'raw_text', 'category_id', 'subcategory_id', 'is_pinned', 'images'];