From 03745e19c315de74dd5a95d7db1f0698fafa40ab Mon Sep 17 00:00:00 2001 From: mac Date: Thu, 4 Jun 2026 15:58:56 +0800 Subject: [PATCH] =?UTF-8?q?v1.4.2=20=E2=80=94=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=BD=92=E7=B1=BB=E5=8F=96=E4=BB=A3=E6=89=8B=E5=8A=A8=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E6=9D=BF=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端关键词自动归类立志/责善/勤学到六板块 - 移除DOM板块选择器,简化UI - 蓝图面板展示各板块立志·责善·勤学统计+详情列表 - 旧数据自动兼容、新数据无需手动选分类 --- app.py | 88 ++++++++++++++++++++++++++++++++++++++++---- static/app.js | 53 +++++++++++++------------- static/style.css | 26 +++++++------ templates/index.html | 6 +++ 4 files changed, 128 insertions(+), 45 deletions(-) diff --git a/app.py b/app.py index 1f3807f..5b21940 100644 --- a/app.py +++ b/app.py @@ -77,7 +77,30 @@ def compute_stats(): total_evening = 0 total_study = 0 calendar = {} - all_morning_items = [] # 收集所有立志项用于蓝图统计 + all_morning_items = [] + all_evening_items = [] + all_study_items = [] + + # 关键词 → 板块 自动归类规则 + PILLAR_KEYWORDS = { + '医药营销': ['科普','科研','学术会议','学术','会议','患者管理','临床推广','临床','药企','处方','KOL','代表','拜访','荣昌','恒瑞','信达','鲁银','OPC','项目','标杆','客户'], + '医疗服务': ['诊疗','治疗','医生','医院','科室','手术','门诊','患者','病','护理','诊断','影像','检验'], + '医疗支付': ['医保','商保','理赔','支付','保险','费用','报销','商业保险','保单'], + 'AI 智能': ['AI','数字人','智能','算法','自动化','平台','系统','网站','开发','代码','程序','模型'], + '公司治理': ['团队','组织','管理','招聘','HR','制度','流程','财务','考核','KPI','规划','人才','架构','预算'], + '个人修养': ['早起','睡觉','俯卧撑','运动','冥想','阅读','复盘','写作','习惯','修身','立志','责善','改过','勤学'], + } + + def classify_auto(text): + """根据文本内容自动归类""" + if not text or not text.strip(): + return '' + text_lower = text.strip() + for pillar, keywords in PILLAR_KEYWORDS.items(): + for kw in keywords: + if kw in text_lower: + return pillar + return '' # 未匹配 for row in rows: d = row['date'] @@ -86,12 +109,36 @@ def compute_stats(): evening = data.get('evening', []) study = data.get('study', []) - # 收集 morning items + # 收集并自动归类 for mi in morning: - if isinstance(mi, dict) and (mi.get('text', '') or '').strip(): - all_morning_items.append(mi) - elif isinstance(mi, str) and mi.strip(): - all_morning_items.append({'text': mi, 'pillar': ''}) + text = mi if isinstance(mi, str) else mi.get('text', '') + if isinstance(text, str) and text.strip(): + auto_pillar = mi.get('pillar', '') if isinstance(mi, dict) else '' + if not auto_pillar: + auto_pillar = classify_auto(text) + all_morning_items.append({'text': text.strip(), 'pillar': auto_pillar}) + + for ei in evening: + if isinstance(ei, dict): + m = (ei.get('mistake') or '').strip() + imp = (ei.get('improvement') or '').strip() + if m or imp: + pillar = ei.get('pillar', '') or classify_auto(m + ' ' + imp) + all_evening_items.append({'mistake': m, 'improvement': imp, 'pillar': pillar}) + elif isinstance(ei, str) and ei.strip(): + all_evening_items.append({'mistake': ei.strip(), 'improvement': '', 'pillar': classify_auto(ei)}) + + for si in study: + name = si.get('name', '') if isinstance(si, dict) else str(si) + if name.strip(): + pillar = si.get('pillar', '') if isinstance(si, dict) else '' + if not pillar: + pillar = classify_auto(name) + all_study_items.append({ + 'name': name.strip(), + 'done': si.get('done', False) if isinstance(si, dict) else False, + 'pillar': pillar + }) morning_count = sum(1 for x in morning if ( isinstance(x, str) and x.strip() or @@ -114,11 +161,38 @@ def compute_stats(): # day_score = 60 + extra * 5 (不在此处使用,仅用于日历状态) calendar[d] = 'pass' if all_three else 'fail' + # 构建板块统计 + pillars = ['医疗服务','医药营销','医疗支付','AI 智能','公司治理','个人修养'] + pillar_breakdown = {} + for p in pillars: + pillar_breakdown[p] = { + 'morning': 0, 'evening': 0, 'study': 0, + 'morning_items': [], 'evening_items': [], 'study_items': [] + } + for mi in all_morning_items: + p = mi['pillar'] + if p in pillar_breakdown: + pillar_breakdown[p]['morning'] += 1 + pillar_breakdown[p]['morning_items'].append(mi) + for ei in all_evening_items: + p = ei['pillar'] + if p in pillar_breakdown: + pillar_breakdown[p]['evening'] += 1 + pillar_breakdown[p]['evening_items'].append(ei) + for si in all_study_items: + p = si['pillar'] + if p in pillar_breakdown: + pillar_breakdown[p]['study'] += 1 + pillar_breakdown[p]['study_items'].append(si) + return dict( total_days=total_days, total_morning=total_morning, total_evening=total_evening, total_study=total_study, calendar=calendar, - morning_items=all_morning_items + morning_items=all_morning_items, + evening_items=all_evening_items, + study_items=all_study_items, + pillar_breakdown=pillar_breakdown ) diff --git a/static/app.js b/static/app.js index 1e7a509..a2b322b 100644 --- a/static/app.js +++ b/static/app.js @@ -242,11 +242,7 @@ var mEls = document.querySelectorAll('#morning-list .item-row input'); for (var i=0; i' + p.emoji + ' ' + p.name + ''; - }); var div = document.createElement('div'); div.className = 'item-row'; div.innerHTML = '' + (idx+1) + '.' + '' + - '' + ''; container.appendChild(div); renumberMorning(); @@ -1023,17 +1011,28 @@ function loadBlueprint() { apiGetStats(function(err, data) { - if (err || !data || !data.morning_items) return; - var counts = {}; - PILLARS.forEach(function(p){ counts[p.name] = 0; }); - var total = 0; - (data.morning_items || []).forEach(function(item){ - var name = (typeof item === 'string') ? '' : (item.pillar || ''); - if (counts[name] !== undefined) { counts[name]++; total++; } - }); + if (err || !data || !data.pillar_breakdown) return; + var pb = data.pillar_breakdown; PILLARS.forEach(function(p){ var el = document.getElementById('bp-count-' + p.name); - if (el) el.textContent = counts[p.name] + ' 条'; + var info = pb[p.name] || {}; + var m = info.morning || 0, e = info.evening || 0, s = info.study || 0; + if (el) el.textContent = '立志' + m + ' · 责善' + e + ' · 勤学' + s; + // 显示详情列表 + var detailEl = document.getElementById('bp-detail-' + p.name); + if (detailEl) { + var html = ''; + (info.morning_items || []).forEach(function(mi){ + html += '
🥼 ' + esc(mi.text) + '
'; + }); + (info.evening_items || []).forEach(function(ei){ + html += '
🔍 ' + esc(ei.mistake || ei.improvement) + '
'; + }); + (info.study_items || []).forEach(function(si){ + html += '
📚 ' + esc(si.name) + '
'; + }); + detailEl.innerHTML = html || '
暂无记录
'; + } }); }); } diff --git a/static/style.css b/static/style.css index 8a14f0e..0b3fbf7 100644 --- a/static/style.css +++ b/static/style.css @@ -1463,19 +1463,23 @@ body { .bp-desc { font-size: 11px; color: var(--text-muted); margin-bottom: 10px; line-height: 1.5; } .bp-count { font-size: 13px; font-weight: 600; color: var(--primary); } -/* 立志板块选择器 */ -.morning-pillar { - padding: 4px 8px; - border: 1.5px solid var(--border); - border-radius: var(--radius-sm); +.bp-detail { + margin-top: 10px; + text-align: left; font-size: 11px; - font-family: inherit; - color: var(--text-dim); - background: var(--card); - min-width: 100px; - cursor: pointer; + max-height: 120px; + overflow-y: auto; + border-top: 1px solid var(--border); + padding-top: 8px; } -.morning-pillar:hover { border-color: var(--primary); } +.bp-item { + padding: 2px 0; + color: var(--text-dim); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.bp-empty { color: var(--text-muted); text-align: center; padding: 6px 0; } @media (max-width: 900px) { .blueprint-pillars { grid-template-columns: repeat(2, 1fr); } diff --git a/templates/index.html b/templates/index.html index cc207df..f114f29 100644 --- a/templates/index.html +++ b/templates/index.html @@ -287,36 +287,42 @@
医疗服务
诊疗能力 · 临床路径 · 医疗质量
+
💊
医药营销
科普 · 科研 · 学术会议 · 患者管理
+
🛡️
医疗支付
医保 · 商保 · 支付闭环
+
🤖
AI 智能
数字人 · 智能平台 · AI 赋能
+
🏛️
公司治理
组织 · 人才 · 制度 · 文化
+
🎯
个人修养
立志 · 责善 · 改过 · 勤学
+