5 Commits
v1.4.5 ... main

Author SHA1 Message Date
mac
350c9fb877 v1.5.3 — 日历同步按日期自动填充
- 后端 api_calendar_sync_all 查询后自动去重保存到各日期 checkin
- 不再只填充当前选中的日期
- 弹窗显示已填充天数
2026-06-05 12:19:22 +08:00
mac
19ae81ae6e v1.5.2 — 责善显示改进方案
- 责善列表项追加 → 反思内容
- 红色标注区分改进方案
2026-06-04 17:46:44 +08:00
mac
535af35911 v1.5.1 — 蓝图重命名为人生蓝图,图标改为太阳 2026-06-04 16:49:27 +08:00
mac
fc338f426a v1.5.0 — 蓝图 VI 统一
- 移除头部 mission 文字和六板块卡片
- Tab 改用 SVG 图标(bolt/放大镜/书本/列表)
- 板块用彩色圆点标识(六色 VI)
- 整体风格对齐天机阁设计系统
2026-06-04 16:43:08 +08:00
mac
5300ee426d v1.4.6 — 备注左对齐 2026-06-04 16:39:58 +08:00
4 changed files with 73 additions and 69 deletions

32
app.py
View File

@@ -398,18 +398,46 @@ def api_calendar_sync():
@app.route('/api/calendar-sync-all', methods=['GET']) @app.route('/api/calendar-sync-all', methods=['GET'])
@login_required @login_required
def api_calendar_sync_all(): def api_calendar_sync_all():
"""批量查询过去15天~未来15天的钉钉日程按日期分组返回""" """批量查询过去15天~未来15天的钉钉日程按日期分组返回,并自动填充到对应日期的 checkin"""
import json as _json import json as _json
from datetime import datetime, timedelta from datetime import datetime, timedelta
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
results = [] results = []
saved_days = 0
for delta in range(-15, 16): for delta in range(-15, 16):
d = today + timedelta(days=delta) d = today + timedelta(days=delta)
ds = d.strftime('%Y-%m-%d') ds = d.strftime('%Y-%m-%d')
events = _fetch_dingtalk_events(ds) events = _fetch_dingtalk_events(ds)
if events: if events:
results.append({'date': ds, 'events': events}) results.append({'date': ds, 'events': events})
return jsonify({'ok': True, 'data': results}) # 自动去重填充到对应日期的 checkin
existing = get_checkin(ds)
data = existing['data'] if existing else {'morning': [], 'evening': [], 'study': []}
morning = data.get('morning', [])
existing_texts = set()
for mi in morning:
t = mi if isinstance(mi, str) else mi.get('text', '')
if t.strip():
existing_texts.add(t.strip())
added = False
for evt in events:
summary = (evt.get('summary') or '').strip()
if not summary:
continue
text = f"{evt['time']}{summary}" if evt.get('time') else summary
loc = evt.get('location', '')
if loc:
text += f" @{loc}"
text = text.strip()
if text not in existing_texts:
morning.append(text)
existing_texts.add(text)
added = True
if added:
data['morning'] = morning
save_checkin(ds, data)
saved_days += 1
return jsonify({'ok': True, 'data': results, 'saved_days': saved_days})
# ── 启动 ────────────────────────────────────────────── # ── 启动 ──────────────────────────────────────────────

View File

@@ -410,7 +410,7 @@
var totalDays = data.length; var totalDays = data.length;
var totalEvents = 0; var totalEvents = 0;
data.forEach(function(d){ totalEvents += d.events.length; }); data.forEach(function(d){ totalEvents += d.events.length; });
summary.innerHTML = '同步完成!共 <b>' + totalDays + '</b> 天有日程,合计 <b>' + totalEvents + '</b> 个会议'; summary.innerHTML = '同步完成!共 <b>' + totalDays + '</b> 天有日程,合计 <b>' + totalEvents + '</b> 个会议' + (res.saved_days ? ',已自动填充 <b>' + res.saved_days + '</b> 天' : '');
// 渲染结果列表 // 渲染结果列表
var html = ''; var html = '';
@@ -1016,7 +1016,6 @@
apiGetStats(function(err, data) { apiGetStats(function(err, data) {
if (err || !data || !data.pillar_breakdown) return; if (err || !data || !data.pillar_breakdown) return;
blueprintData = data.pillar_breakdown; blueprintData = data.pillar_breakdown;
renderBlueprintPillars(blueprintData);
renderBlueprintList(); renderBlueprintList();
}); });
} }
@@ -1045,7 +1044,7 @@
(info.morning_items || []).forEach(function(mi){ items.push({pillar: p, text: mi.text, type: 'morning'}); }); (info.morning_items || []).forEach(function(mi){ items.push({pillar: p, text: mi.text, type: 'morning'}); });
} }
if (blueprintFilter === 'all' || blueprintFilter === 'evening') { if (blueprintFilter === 'all' || blueprintFilter === 'evening') {
(info.evening_items || []).forEach(function(ei){ items.push({pillar: p, text: ei.mistake || ei.improvement, type: 'evening'}); }); (info.evening_items || []).forEach(function(ei){ items.push({pillar: p, text: ei.mistake || ei.improvement, improvement: ei.improvement, type: 'evening'}); });
} }
if (blueprintFilter === 'all' || blueprintFilter === 'study') { if (blueprintFilter === 'all' || blueprintFilter === 'study') {
(info.study_items || []).forEach(function(si){ items.push({pillar: p, text: si.name, type: 'study', done: si.done, note: si.note}); }); (info.study_items || []).forEach(function(si){ items.push({pillar: p, text: si.name, type: 'study', done: si.done, note: si.note}); });
@@ -1058,14 +1057,22 @@
} }
if (empty) empty.style.display = 'none'; if (empty) empty.style.display = 'none';
var typeLabels = {morning: '立志', evening: '责善', study: '勤学'}; var typeLabels = {morning: '立志', evening: '责善', study: '勤学'};
var pillarColors = {
'医疗服务': '#4A6CF7', '医药营销': '#D97706', '医疗支付': '#059669',
'AI 智能': '#7C3AED', '公司治理': '#DC2626', '个人修养': '#0891B2'
};
var html = ''; var html = '';
items.forEach(function(item){ items.forEach(function(item){
var noteHtml = item.type === 'study' && item.note ? '<span class="bp-item-note">' + esc(item.note) + '</span>' : ''; var dot = '<span class="bp-pillar-dot" style="background:' + (pillarColors[item.pillar.name] || '#94A3B8') + '"></span>';
var extra = '';
if (item.type === 'study' && item.note) extra = '<span class="bp-item-note">' + esc(item.note) + '</span>';
if (item.type === 'evening' && item.improvement) extra = '<span class="bp-item-note evening">→ ' + esc(item.improvement) + '</span>';
html += '<div class="bp-list-item">' + html += '<div class="bp-list-item">' +
'<span class="bp-item-pillar">' + item.pillar.emoji + ' ' + item.pillar.name + '</span>' + dot +
'<span class="bp-item-pillar">' + item.pillar.name + '</span>' +
'<span class="bp-item-type ' + item.type + '">' + (typeLabels[item.type] || item.type) + '</span>' + '<span class="bp-item-type ' + item.type + '">' + (typeLabels[item.type] || item.type) + '</span>' +
'<span class="bp-item-text">' + esc(item.text) + '</span>' + '<span class="bp-item-text">' + esc(item.text) + '</span>' +
noteHtml + extra +
'</div>'; '</div>';
}); });
list.innerHTML = html; list.innerHTML = html;

View File

@@ -1428,54 +1428,18 @@ body {
Blueprint Panel Blueprint Panel
═══════════════════════════════════════════ */ ═══════════════════════════════════════════ */
.blueprint-mission {
font-size: 15px;
font-weight: 600;
color: var(--primary);
margin-bottom: 24px;
padding: 12px 16px;
background: var(--primary-light);
border-radius: var(--radius);
text-align: center;
}
.blueprint-pillars {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.blueprint-pillar {
background: var(--card);
border: 1.5px solid var(--border);
border-radius: var(--radius);
padding: 20px 16px;
text-align: center;
transition: border-color 0.2s, box-shadow 0.2s;
}
.blueprint-pillar:hover {
border-color: var(--primary);
box-shadow: 0 4px 16px rgba(74,108,247,0.08);
}
.bp-icon { font-size: 32px; margin-bottom: 8px; }
.bp-name { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.bp-desc { font-size: 11px; color: var(--text-muted); margin-bottom: 10px; line-height: 1.5; }
.bp-count { font-size: 12px; font-weight: 600; color: var(--primary); display: flex; gap: 10px; justify-content: center; }
.bp-type { display: inline-flex; align-items: center; gap: 2px; white-space: nowrap; }
.bp-type.morning { color: var(--warning); }
.bp-type.evening { color: var(--danger); }
.bp-type.study { color: var(--success); }
/* Tab 筛选 */ /* Tab 筛选 */
.bp-tabs { .bp-tabs {
display: flex; display: flex;
gap: 4px; gap: 4px;
margin-bottom: 16px; margin-bottom: 20px;
border-bottom: 1.5px solid var(--border); border-bottom: 1.5px solid var(--border);
padding-bottom: 0; padding-bottom: 0;
} }
.bp-tab { .bp-tab {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 8px 16px; padding: 8px 16px;
border: none; border: none;
background: none; background: none;
@@ -1488,8 +1452,10 @@ body {
transition: all 0.15s; transition: all 0.15s;
font-family: inherit; font-family: inherit;
} }
.bp-tab .icon-sm { opacity: 0.6; }
.bp-tab:hover { color: var(--text); } .bp-tab:hover { color: var(--text); }
.bp-tab.active { color: var(--primary); border-bottom-color: var(--primary); } .bp-tab.active { color: var(--primary); border-bottom-color: var(--primary); }
.bp-tab.active .icon-sm { opacity: 1; }
/* 明细列表 */ /* 明细列表 */
.bp-list { .bp-list {
@@ -1507,11 +1473,16 @@ body {
transition: background 0.15s; transition: background 0.15s;
} }
.bp-list-item:hover { background: var(--bg); } .bp-list-item:hover { background: var(--bg); }
.bp-pillar-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.bp-item-pillar { .bp-item-pillar {
font-size: 12px; font-size: 12px;
color: var(--text-dim); color: var(--text-dim);
white-space: nowrap; white-space: nowrap;
min-width: 80px;
} }
.bp-item-type { .bp-item-type {
font-size: 11px; font-size: 11px;
@@ -1524,7 +1495,6 @@ body {
.bp-item-type.evening { background: var(--danger-light); color: var(--danger); } .bp-item-type.evening { background: var(--danger-light); color: var(--danger); }
.bp-item-type.study { background: var(--success-light); color: var(--success); } .bp-item-type.study { background: var(--success-light); color: var(--success); }
.bp-item-text { .bp-item-text {
flex: 1;
font-size: 13px; font-size: 13px;
color: var(--text); color: var(--text);
overflow: hidden; overflow: hidden;
@@ -1534,12 +1504,10 @@ body {
.bp-item-note { .bp-item-note {
font-size: 11px; font-size: 11px;
color: var(--text-muted); color: var(--text-muted);
margin-left: 8px; margin-left: 4px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.bp-item-note.evening { color: var(--danger); }
.bp-list-empty { .bp-list-empty {
font-size: 13px; font-size: 13px;
color: var(--text-muted); color: var(--text-muted);
@@ -1547,8 +1515,6 @@ body {
padding: 32px 0; padding: 32px 0;
} }
/* 移除旧样式 */
@media (max-width: 900px) { @media (max-width: 900px) {
.blueprint-pillars { grid-template-columns: repeat(2, 1fr); } .quad-grid { grid-template-columns: 1fr; grid-template-rows: auto; }
} }

View File

@@ -108,8 +108,8 @@
历史记录 历史记录
</a> </a>
<a class="nav-item" data-panel="blueprint" onclick="switchPanel('blueprint')"> <a class="nav-item" data-panel="blueprint" onclick="switchPanel('blueprint')">
<svg class="icon-sm"><use href="#icon-star"/></svg> <svg class="icon-sm"><use href="#icon-sun"/></svg>
蓝图 人生蓝图
</a> </a>
</nav> </nav>
@@ -276,19 +276,22 @@
<!-- ── 蓝图面板 ── --> <!-- ── 蓝图面板 ── -->
<section class="panel" id="panel-blueprint"> <section class="panel" id="panel-blueprint">
<div class="panel-header"> <div class="panel-header">
<h2>蓝图 · 数字医疗集团</h2> <h2>人生蓝图</h2>
</div> </div>
<div class="blueprint-mission">
一辈子,干成一件事 — 用 AI 重构医疗全链路
</div>
<!-- 六板块卡片 -->
<div class="blueprint-pillars" id="blueprint-pillars"></div>
<!-- Tab 筛选 --> <!-- Tab 筛选 -->
<div class="bp-tabs"> <div class="bp-tabs">
<button class="bp-tab active" data-filter="all" onclick="filterBlueprint(this)">全部</button> <button class="bp-tab active" data-filter="all" onclick="filterBlueprint(this)">
<button class="bp-tab" data-filter="morning" onclick="filterBlueprint(this)">🥼 立志</button> <svg class="icon-sm"><use href="#icon-list-bullet"/></svg> 全部
<button class="bp-tab" data-filter="evening" onclick="filterBlueprint(this)">🔍 责善</button> </button>
<button class="bp-tab" data-filter="study" onclick="filterBlueprint(this)">📚 勤学</button> <button class="bp-tab" data-filter="morning" onclick="filterBlueprint(this)">
<svg class="icon-sm"><use href="#icon-bolt"/></svg> 立志
</button>
<button class="bp-tab" data-filter="evening" onclick="filterBlueprint(this)">
<svg class="icon-sm"><use href="#icon-magnifying-glass"/></svg> 责善
</button>
<button class="bp-tab" data-filter="study" onclick="filterBlueprint(this)">
<svg class="icon-sm"><use href="#icon-book-open"/></svg> 勤学
</button>
</div> </div>
<!-- 明细列表 --> <!-- 明细列表 -->
<div class="bp-list" id="bp-list"></div> <div class="bp-list" id="bp-list"></div>