learn_results.html 7.29 KB
{% extends "base.html" %}
{% block title %}学习结果 - WeChat Viewer{% endblock %}
{% block body %}
<style>
body{overflow-y:auto!important;height:auto!important;}
.results-container{max-width:960px;margin:0 auto;padding:20px;}
.results-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.results-header h1{font-size:20px;color:#2c3e50;}
.result-card{background:white;border-radius:8px;padding:16px 18px;margin-bottom:10px;box-shadow:0 1px 2px rgba(0,0,0,0.05);border-left:3px solid #8e44ad;}
.result-card.failed{border-left-color:#e74c3c;}
.result-card .top{display:flex;align-items:center;gap:10px;margin-bottom:10px;}
.result-card .title{font-size:14px;font-weight:600;flex:1;}
.result-type{font-size:10px;padding:2px 8px;border-radius:10px;font-weight:600;}
.result-type.skill{background:#d5f5e3;color:#1e8449}
.result-type.memory{background:#d6eaf8;color:#1a5276}
.result-type.methodology{background:#fdebd0;color:#b9770e}
.result-type.reference{background:#e8daef;color:#6c3483}
.result-type.architecture{background:#fadbd8;color:#922b21}
.auto-badge{font-size:9px;padding:1px 6px;border-radius:8px;background:#e8daef;color:#6c3483;}
.result-card .summary{font-size:12px;color:#666;line-height:1.5;margin-bottom:8px;}
.result-card .details{font-size:11px;color:#888;}
.result-card .details span{display:inline-block;margin-right:12px;margin-bottom:4px;}
.feedback-section{border-top:1px solid #eee;padding-top:8px;margin-top:8px;}
.feedback-input{width:100%;padding:6px;border:1px solid #ddd;border-radius:4px;font-size:11px;box-sizing:border-box;}
.btn-feedback{background:#3498db;color:white;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:11px;margin-top:4px;}
.btn-feedback:hover{background:#2980b9}
.existing-feedback{font-size:11px;color:#8e44ad;padding:4px 8px;background:#f4ecf7;border-radius:4px;}
.empty-state{text-align:center;padding:60px 20px;color:#999;}
.loading{text-align:center;padding:40px;color:#999;}
</style>
<div class="results-container">
<div class="results-header">
<h1>📊 学习结果</h1>
<div style="display:flex;gap:8px;">
<a href="/learn" style="font-size:12px;color:#3498db;text-decoration:none;padding:6px 10px;border:1px solid #3498db;border-radius:6px;">← 审核队列</a>
<button onclick="refresh()" style="background:#8e44ad;color:white;border:none;padding:6px 14px;border-radius:6px;cursor:pointer;font-size:13px;">🔄 刷新</button>
</div>
</div>
<div class="rbar" style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap;align-items:center;">
  <input type="text" id="searchInput" placeholder="搜索文章标题/摘要..." oninput="render()" style="flex:1;min-width:120px;padding:10px;border:1px solid #ddd;border-radius:8px;font-size:14px;">
  <select id="typeFilter" onchange="render()" style="padding:10px;border:1px solid #ddd;border-radius:8px;font-size:13px;background:white;cursor:pointer;">
    <option value="">全部类型</option>
    <option value="skill">Skill</option>
    <option value="memory">记忆</option>
    <option value="todo">待办</option>
    <option value="reference">参考</option>
    <option value="methodology">方法论</option>
    <option value="architecture">架构</option>
  </select>
  <select id="sortBy" onchange="render()" style="padding:10px;border:1px solid #ddd;border-radius:8px;font-size:13px;background:white;cursor:pointer;">
    <option value="time">按时间↓</option>
    <option value="title">按标题</option>
  </select>
</div>
<div id="stats" style="margin-bottom:16px;font-size:13px;color:#888;"></div>
<div id="list"></div>
</div>
<script>
var allResults = [];
var TYPE_NAMES = {
  skill:'🛠 Skill', memory:'🧠 记忆', todo:'📋 待办',
  reference:'📖 参考', methodology:'💡 方法论', architecture:'🔧 架构'
};

function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}

function refresh(){
  var el=document.getElementById('list');
  el.innerHTML='<div class="loading">加载中...</div>';
  fetch('/api/learn-results').then(function(r){return r.json()}).then(function(j){
    if(!j.ok||!j.data.length){el.innerHTML='<div class="empty-state"><p>暂无学习结果<br><small>吸收文章后会自动出现在这里</small></p></div>';return;}
    allResults = j.data;
    document.getElementById('stats').textContent='共 '+allResults.length+' 条学习记录';
    render();
  }).catch(function(e){el.innerHTML='<div class="empty-state"><p>加载失败: '+e.message+'</p></div>';});
}

function render(){
  var el=document.getElementById('list');
  var q=(document.getElementById('searchInput').value||'').trim().toLowerCase();
  var tf=document.getElementById('typeFilter').value;
  var sb=document.getElementById('sortBy').value;
  var filtered=[];
  for(var i=0;i<allResults.length;i++){
    var t=allResults[i];
    if(q){
      var hay=((t.article_title||'')+' '+(t.result_summary||'')+' '+(t.created_skill||'')+' '+(t.updated_skill||'')).toLowerCase();
      if(hay.indexOf(q)<0)continue;
    }
    if(tf&&t.result_type!==tf)continue;
    filtered.push(t);
  }
  if(sb==='title')filtered.sort(function(a,b){return (a.article_title||'').localeCompare(b.article_title||'')});
  else filtered.sort(function(a,b){return (b.created_at||'').localeCompare(a.created_at||'')});

  if(!filtered.length){el.innerHTML='<div class="empty-state"><p>无匹配</p></div>';return;}
  var h='';
  for(var i=0;i<filtered.length;i++){
    var t=filtered[i];
    var tn=TYPE_NAMES[t.result_type]||t.result_type;
    var failed=t.status==='failed';
    h+='<div class="result-card'+(failed?' failed':'')+'">';
    h+='<div class="top"><span class="result-type '+t.result_type+'">'+tn+'</span>';
    if(t.auto_applied)h+='<span class="auto-badge">🤖 自动</span>';
    h+='<span class="title">'+esc(t.article_title||'未命名')+'</span>';
    h+='<span style="font-size:10px;color:#999;">'+(t.created_at||'').substring(0,16)+'</span></div>';
    if(t.result_summary)h+='<div class="summary">'+esc(t.result_summary)+'</div>';
    h+='<div class="details">';
    if(t.created_skill)h+='<span>🛠 创建: <b>'+esc(t.created_skill)+'</b></span>';
    if(t.updated_skill)h+='<span>📝 更新: <b>'+esc(t.updated_skill)+'</b></span>';
    if(t.learned_methodology)h+='<span>💡 方法论: '+esc(t.learned_methodology).substring(0,80)+'</span>';
    if(t.verification_result)h+='<span>✅ 验证: '+esc(t.verification_result).substring(0,80)+'</span>';
    if(t.architecture_change)h+='<span>🔧 架构: '+esc(t.architecture_change).substring(0,80)+'</span>';
    h+='</div>';
    h+='<div class="feedback-section">';
    if(t.human_feedback){
      h+='<div class="existing-feedback">💬 反馈: '+esc(t.human_feedback)+'</div>';
    }else{
      h+='<input class="feedback-input" id="fb_'+t.id+'" placeholder="输入反馈...">';
      h+='<button class="btn-feedback" onclick="sendFeedback('+t.id+')">发送反馈</button>';
    }
    h+='</div>';
    h+='</div>';
  }
  el.innerHTML=h;
}

function sendFeedback(id){
  var inp=document.getElementById('fb_'+id);
  if(!inp||!inp.value.trim())return;
  fetch('/api/learn-result/'+id+'/feedback',{
    method:'POST',
    headers:{'Content-Type':'application/json'},
    body:JSON.stringify({feedback:inp.value.trim()})
  }).then(function(r){return r.json()}).then(function(j){
    if(j.ok){refresh();}else{alert('失败: '+(j.error||''));}
  });
}

refresh();
</script>
{% endblock %}