skill-memo-ilife 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cmd_read.d.ts +3 -0
- package/dist/cli/cmd_read.d.ts.map +1 -0
- package/dist/cli/cmd_read.js +178 -0
- package/dist/cli/cmd_read.js.map +1 -0
- package/dist/fetch/db.d.ts +31 -0
- package/dist/fetch/db.d.ts.map +1 -0
- package/dist/fetch/db.js +117 -0
- package/dist/fetch/db.js.map +1 -0
- package/dist/fetch/errors.d.ts +11 -0
- package/dist/fetch/errors.d.ts.map +1 -0
- package/dist/fetch/errors.js +18 -0
- package/dist/fetch/errors.js.map +1 -0
- package/dist/fetch/feishu.d.ts +24 -0
- package/dist/fetch/feishu.d.ts.map +1 -0
- package/dist/fetch/feishu.js +117 -0
- package/dist/fetch/feishu.js.map +1 -0
- package/dist/fetch/index.d.ts +6 -0
- package/dist/fetch/index.d.ts.map +1 -0
- package/dist/fetch/index.js +4 -0
- package/dist/fetch/index.js.map +1 -0
- package/dist/help/index.d.ts +3 -0
- package/dist/help/index.d.ts.map +1 -0
- package/dist/help/index.js +2 -0
- package/dist/help/index.js.map +1 -0
- package/dist/help/lookup.d.ts +11 -0
- package/dist/help/lookup.d.ts.map +1 -0
- package/dist/help/lookup.js +38 -0
- package/dist/help/lookup.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/policy/category.d.ts +7 -0
- package/dist/policy/category.d.ts.map +1 -0
- package/dist/policy/category.js +29 -0
- package/dist/policy/category.js.map +1 -0
- package/dist/policy/crud.d.ts +19 -0
- package/dist/policy/crud.d.ts.map +1 -0
- package/dist/policy/crud.js +24 -0
- package/dist/policy/crud.js.map +1 -0
- package/dist/policy/index.d.ts +10 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +6 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/reminder.d.ts +10 -0
- package/dist/policy/reminder.d.ts.map +1 -0
- package/dist/policy/reminder.js +20 -0
- package/dist/policy/reminder.js.map +1 -0
- package/dist/policy/wakewords.d.ts +14 -0
- package/dist/policy/wakewords.d.ts.map +1 -0
- package/dist/policy/wakewords.js +53 -0
- package/dist/policy/wakewords.js.map +1 -0
- package/dist/policy/wish.d.ts +7 -0
- package/dist/policy/wish.d.ts.map +1 -0
- package/dist/policy/wish.js +13 -0
- package/dist/policy/wish.js.map +1 -0
- package/dist/render/envelope.d.ts +6 -0
- package/dist/render/envelope.d.ts.map +1 -0
- package/dist/render/envelope.js +50 -0
- package/dist/render/envelope.js.map +1 -0
- package/dist/render/errors.d.ts +5 -0
- package/dist/render/errors.d.ts.map +1 -0
- package/dist/render/errors.js +10 -0
- package/dist/render/errors.js.map +1 -0
- package/dist/render/html.d.ts +10 -0
- package/dist/render/html.d.ts.map +1 -0
- package/dist/render/html.js +52 -0
- package/dist/render/html.js.map +1 -0
- package/dist/render/index.d.ts +6 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +5 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/templates.d.ts +4 -0
- package/dist/render/templates.d.ts.map +1 -0
- package/dist/render/templates.js +26 -0
- package/dist/render/templates.js.map +1 -0
- package/package.json +32 -0
- package/templates/change_category.html +257 -0
- package/templates/init_report.html +225 -0
- package/templates/memo_query.html +104 -0
- package/templates/sync_report.html +512 -0
- package/templates/wish_complete.html +239 -0
- package/templates/wish_plan.html +272 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>心愿完成向导</title>
|
|
7
|
+
<style><!--SHARED-CSS--></style>
|
|
8
|
+
<style>
|
|
9
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
10
|
+
body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--bg);color:var(--fg);line-height:1.65;-webkit-font-smoothing:antialiased}
|
|
11
|
+
.wrap{max-width:980px;margin:0 auto;padding:48px 22px 90px}
|
|
12
|
+
.hero{background:linear-gradient(180deg,#fff 0%,#f8fbff 100%);border-radius:28px;padding:34px;margin-bottom:20px;box-shadow:var(--shadow,0 1px 2px rgba(0,0,0,.04),0 12px 36px rgba(0,0,0,.06))}
|
|
13
|
+
.eyebrow{font-size:13px;color:var(--blue);font-weight:800;letter-spacing:.08em;text-transform:uppercase;margin-bottom:10px}
|
|
14
|
+
h1{font-size:36px;line-height:1.15;letter-spacing:-.035em;margin-bottom:12px}
|
|
15
|
+
.lead{color:var(--fg2);font-size:17px}
|
|
16
|
+
.meta{margin-top:14px;color:var(--fg3);font-size:13px}
|
|
17
|
+
.badge{display:inline-block;background:var(--soft,#f5f8ff);color:var(--blue);padding:4px 11px;border-radius:999px;font-size:13px;font-weight:700;margin:2px 6px 2px 0}
|
|
18
|
+
.badge.ok{background:#ecfff2;color:#178a3a}
|
|
19
|
+
.badge.warn{background:#fff7e8;color:#b25b00}
|
|
20
|
+
.panel{background:var(--card);border-radius:20px;padding:22px;box-shadow:0 1px 3px rgba(0,0,0,.04);margin:14px 0}
|
|
21
|
+
.panel .hint{font-size:12px;color:var(--fg3);margin-top:6px}
|
|
22
|
+
.toolbar{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0;align-items:center}
|
|
23
|
+
button{border:0;background:var(--blue);color:#fff;border-radius:999px;padding:10px 16px;cursor:pointer;font-size:14px;font-weight:600}
|
|
24
|
+
button.ghost{background:transparent;color:var(--blue);border:1px solid var(--line);min-height:44px}
|
|
25
|
+
button.copy.ghost{background:var(--card);color:var(--blue);border:1.5px solid var(--blue)}
|
|
26
|
+
button.primary{background:var(--blue);color:#fff;font-size:15px;padding:12px 20px}
|
|
27
|
+
input[type=text],textarea{border:1px solid var(--line);border-radius:12px;padding:9px 12px;font-size:14px;font-family:inherit;width:100%;background:var(--card)}
|
|
28
|
+
textarea{min-height:64px;resize:vertical}
|
|
29
|
+
.wish{background:var(--card);border-radius:18px;padding:18px;margin:12px 0;box-shadow:0 1px 3px rgba(0,0,0,.04);transition:box-shadow .15s,opacity .15s}
|
|
30
|
+
.wish.on{opacity:.5}
|
|
31
|
+
.wish.on .content{text-decoration:line-through;color:var(--fg3)}
|
|
32
|
+
.wish-head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
|
|
33
|
+
.wish-id{color:var(--blue);font-weight:800;font-size:15px;min-width:48px}
|
|
34
|
+
.wish-content{font-size:16px;flex:1}
|
|
35
|
+
.wish-meta{display:flex;gap:16px;flex-wrap:wrap;align-items:center;color:var(--fg2);font-size:13px;margin:8px 0}
|
|
36
|
+
.wish-content-input{margin-top:8px}
|
|
37
|
+
.kpi{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:14px 0}
|
|
38
|
+
.kpi .stat{background:var(--card);border-radius:18px;padding:18px;box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
|
39
|
+
.kpi b{display:block;font-size:22px;letter-spacing:-.02em;margin-bottom:4px}
|
|
40
|
+
.kpi span{color:var(--fg2);font-size:13px}
|
|
41
|
+
pre{white-space:pre-wrap;background:#f5f5f7;color:var(--fg);border:1px solid var(--line);border-radius:16px;padding:16px;overflow:auto;font-size:13px;line-height:1.65;max-height:340px;margin-top:14px;font-family:inherit}
|
|
42
|
+
.notice{padding:12px 16px;border-radius:14px;margin:10px 0;font-size:14px}
|
|
43
|
+
.notice.warn{background:#fff7e8;color:#b25b00}
|
|
44
|
+
.notice.err{background:#fff5f5;color:#ff3b30}
|
|
45
|
+
@media(max-width:800px){.wrap{padding:28px 14px 70px}.hero{padding:26px 20px}h1{font-size:30px}.kpi .stat{padding:12px}}
|
|
46
|
+
</style>
|
|
47
|
+
</head>
|
|
48
|
+
<body>
|
|
49
|
+
<div class="wrap">
|
|
50
|
+
<header class="hero">
|
|
51
|
+
<h1 id="title">心愿完成向导</h1>
|
|
52
|
+
<p class="lead" id="subtitle">数据加载中</p>
|
|
53
|
+
<p class="meta" id="meta"></p>
|
|
54
|
+
</header>
|
|
55
|
+
<section id="injectError" style="display:none"></section>
|
|
56
|
+
|
|
57
|
+
<section class="kpi" id="kpis"></section>
|
|
58
|
+
|
|
59
|
+
<section class="panel">
|
|
60
|
+
<div style="font-weight:700;margin-bottom:10px">✏️ 默认打卡内容(可选)</div>
|
|
61
|
+
<p style="color:var(--fg2);font-size:13px;margin-bottom:10px">填了会应用到所有勾选的心愿,也能每条单独改;不填就用心愿原本的内容当打卡内容。</p>
|
|
62
|
+
<input type="text" id="globalContent" placeholder="如:今天完成了 / 2026-07-03 已打卡" />
|
|
63
|
+
<div class="toolbar" style="margin-top:14px">
|
|
64
|
+
<button class="ghost" onclick="applyGlobalContent()">应用到所有勾选</button>
|
|
65
|
+
<button class="ghost" onclick="selectAll(true)">全选</button>
|
|
66
|
+
<button class="ghost" onclick="selectAll(false)">清空选择</button>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
|
|
70
|
+
<section id="wishList"></section>
|
|
71
|
+
|
|
72
|
+
<section class="panel">
|
|
73
|
+
<div style="font-weight:700;margin-bottom:8px">📋 发给 AI 的指令</div>
|
|
74
|
+
<p style="color:var(--fg2);font-size:13px;margin-bottom:12px">复制指令发给 AI,它会把这些心愿标记为完成(心愿转成一条打卡),改完给你回执。</p>
|
|
75
|
+
<div class="toolbar">
|
|
76
|
+
<button class="primary" onclick="adopt()">📋 复制修改指令</button>
|
|
77
|
+
</div>
|
|
78
|
+
<div id="warnArea"></div>
|
|
79
|
+
<pre id="promptOut" style="display:none"></pre>
|
|
80
|
+
</section>
|
|
81
|
+
|
|
82
|
+
<section class="panel">
|
|
83
|
+
<strong>数据与日志</strong>
|
|
84
|
+
<div class="hint">点复制数据保存当前心愿列表 · 点复制日志用于反馈问题(日志不含隐私内容)</div>
|
|
85
|
+
<div class="hm-actions">
|
|
86
|
+
<button class="copy ghost" onclick="copyData()">复制数据</button>
|
|
87
|
+
<button class="copy ghost" onclick="copyLog()">复制日志</button>
|
|
88
|
+
</div>
|
|
89
|
+
</section>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<script id="payload" type="application/json"><!--INJECT-DATA--></script>
|
|
93
|
+
<script><!--SHARED-HELPERS--></script>
|
|
94
|
+
<script>
|
|
95
|
+
(function(){
|
|
96
|
+
"use strict";
|
|
97
|
+
var raw=document.getElementById("payload");
|
|
98
|
+
var payload=null,perr=null;
|
|
99
|
+
try{payload=JSON.parse(raw.textContent||"{}");}catch(e){perr=e.message;}
|
|
100
|
+
if(perr){document.getElementById("injectError").style.display="block";document.getElementById("injectError").innerHTML=window.errorReceipt({message:"数据解析失败",log:perr,payload:null});return;}
|
|
101
|
+
if(!payload||payload.status!=="ok"||!payload.data){document.getElementById("injectError").style.display="block";document.getElementById("injectError").innerHTML=window.errorReceipt({message:(payload&&payload.message)||"数据未注入",retryPrompt:"请重新运行心愿完成(唤醒词:心愿完成)",payload:null});return;}
|
|
102
|
+
window.__hmPayload=payload;
|
|
103
|
+
function data(){return payload.data}
|
|
104
|
+
function items(){return data().items||[]}
|
|
105
|
+
|
|
106
|
+
function init(){
|
|
107
|
+
var d=data();
|
|
108
|
+
document.getElementById('title').textContent=d.title||'心愿完成向导';
|
|
109
|
+
document.getElementById('subtitle').textContent=payload.message||'勾选要完成的心愿';
|
|
110
|
+
var meta=d.meta||{};
|
|
111
|
+
document.getElementById('meta').textContent=`生成时间:${d.generated_at||''} · 场景:${meta.command_cn||d.command||'wish-complete'}`;
|
|
112
|
+
if(d.default_content){document.getElementById('globalContent').value=d.default_content}
|
|
113
|
+
renderKPIs();renderList();
|
|
114
|
+
// success: 心愿列表已加载,等待用户勾选完成
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function renderKPIs(){
|
|
118
|
+
var all=items();
|
|
119
|
+
var withFeishu=all.filter(w=>w.feishu_task_guid).length;
|
|
120
|
+
document.getElementById('kpis').innerHTML=[
|
|
121
|
+
['待完成',all.length,'勾选后转打卡'],
|
|
122
|
+
['已同步飞书',withFeishu,'完成后飞书任务同步标记完成'],
|
|
123
|
+
['其他',all.length-withFeishu,'没有飞书任务,只在本机完成'],
|
|
124
|
+
].map(x=>`<div class="stat"><b>${x[1]}</b><span>${x[0]}</span><div style="color:var(--fg3);font-size:12px;margin-top:4px">${esc(x[2])}</div></div>`).join('');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function renderList(){
|
|
128
|
+
var list=document.getElementById('wishList');
|
|
129
|
+
if(!items().length){list.innerHTML='<div class="panel">'+window.emptyState({icon:'🎯',text:'没有心愿可选',hint:'心愿都完成啦,或先记一条心愿'})+'</div>';return}
|
|
130
|
+
list.innerHTML=items().map(renderWish).join('');
|
|
131
|
+
items().forEach(function(w){
|
|
132
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
133
|
+
if(cb) cb.addEventListener('change',function(e){document.getElementById('wish_'+w.id).classList.toggle('on',e.target.checked)});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function renderWish(w){
|
|
138
|
+
var checked=w.selected?'checked':'';
|
|
139
|
+
return `<article class="wish" id="wish_${w.id}">
|
|
140
|
+
<div class="wish-head">
|
|
141
|
+
<label class="wish-check-label" style="display:flex;align-items:center;gap:12px;flex:1;cursor:pointer">
|
|
142
|
+
<input type="checkbox" id="cb_${w.id}" class="wish-check" ${checked} data-id="${w.id}" />
|
|
143
|
+
<span class="wish-id">#${esc(w.id)}</span>
|
|
144
|
+
<span class="wish-content">${esc(w.content)}</span>
|
|
145
|
+
</label>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="wish-meta">
|
|
148
|
+
${w.category?`<span class="badge">${esc(w.category)}</span>`:''}
|
|
149
|
+
${w.sub_category?`<span class="badge">${esc(w.sub_category)}</span>`:''}
|
|
150
|
+
${w.feishu_task_guid?`<span class="badge ok">已同步飞书</span>`:'<span class="badge">未同步飞书</span>'}
|
|
151
|
+
${w.due?`<span class="badge warn">原排期:${esc(w.due)}</span>`:''}
|
|
152
|
+
</div>
|
|
153
|
+
<div class="wish-content-input">
|
|
154
|
+
<label style="color:var(--fg2);font-size:13px;display:block;margin-bottom:6px">打卡内容(覆盖)</label>
|
|
155
|
+
<textarea id="content_${w.id}" placeholder="默认使用:${esc(w.content).slice(0,20)}${w.content.length>20?'…':''}"></textarea>
|
|
156
|
+
</div>
|
|
157
|
+
</article>`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function applyGlobalContent(){
|
|
161
|
+
var gv=document.getElementById('globalContent').value;
|
|
162
|
+
if(!gv){window.toast('请先填默认打卡内容','再应用到勾选项',{icon:'warn'});return}
|
|
163
|
+
items().forEach(function(w){
|
|
164
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
165
|
+
if(cb&&cb.checked){
|
|
166
|
+
var ti=document.getElementById('content_'+w.id);
|
|
167
|
+
if(ti) ti.value=gv;
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function selectAll(on){
|
|
173
|
+
items().forEach(function(w){
|
|
174
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
175
|
+
if(cb){cb.checked=on;document.getElementById('wish_'+w.id).classList.toggle('on',on)}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function selectedRows(){
|
|
180
|
+
var rows=[];
|
|
181
|
+
items().forEach(function(w){
|
|
182
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
183
|
+
var ti=document.getElementById('content_'+w.id);
|
|
184
|
+
if(cb&&cb.checked){
|
|
185
|
+
rows.push({id:w.id,content:w.content,override:(ti&&ti.value.trim())||'',has_feishu:!!w.feishu_task_guid});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return rows;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function buildPrompt(rows,ctx){
|
|
192
|
+
if(!rows.length) return '未选择任何心愿';
|
|
193
|
+
return [
|
|
194
|
+
'① 场景: 我用心愿完成向导,标记 '+rows.length+' 个心愿为已完成',
|
|
195
|
+
'',
|
|
196
|
+
'② 数据(要完成的心愿):',
|
|
197
|
+
...rows.map(r=>` · 心愿 #${r.id} "${r.content}" → 打卡内容: "${r.override||r.content}"${r.has_feishu?' (飞书任务同步完成)':''}`),
|
|
198
|
+
'',
|
|
199
|
+
'③ 期望: 请把这些心愿标记为完成(每个心愿转成一条打卡),有飞书任务的心愿同步标记完成;完成后给我回执。',
|
|
200
|
+
'',
|
|
201
|
+
'④ 来源: 心愿完成向导 · '+ctx.generated_at,
|
|
202
|
+
].join('\n');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function warnIfAny(rows){
|
|
206
|
+
var nofb=rows.filter(r=>!r.has_feishu);
|
|
207
|
+
if(nofb.length){
|
|
208
|
+
document.getElementById('warnArea').innerHTML=`<div class="notice warn">ℹ️ ${nofb.length} 条心愿未同步飞书,完成时不会触发飞书联动</div>`;
|
|
209
|
+
} else {
|
|
210
|
+
document.getElementById('warnArea').innerHTML='';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function adopt(){
|
|
215
|
+
var rows=selectedRows();
|
|
216
|
+
if(!rows.length){window.toast('未选择任何心愿','勾选要完成的心愿',{icon:'warn'});return}
|
|
217
|
+
warnIfAny(rows);
|
|
218
|
+
var ctx={
|
|
219
|
+
generated_at:data().generated_at||'',
|
|
220
|
+
};
|
|
221
|
+
var text=buildPrompt(rows,ctx);
|
|
222
|
+
var out=document.getElementById('promptOut');
|
|
223
|
+
out.textContent=text;out.style.display='block';
|
|
224
|
+
window.copyText(text,{silent:true});
|
|
225
|
+
window.toast('指令已复制','粘贴发给 AI 即可执行',{icon:'ok'});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function copyData(){window.copyText(window.buildDataText(payload),{silent:true});window.toast('数据已复制','人类可读快照 · 可粘贴到笔记存档',{icon:'copy'});}
|
|
229
|
+
function copyLog(){window.copyText(window.buildLogText(payload),{silent:true});window.toast('日志已复制','诊断信息 · 有异常时粘贴给开发者排查',{icon:'copy'});}
|
|
230
|
+
window.applyGlobalContent=applyGlobalContent;
|
|
231
|
+
window.selectAll=selectAll;
|
|
232
|
+
window.adopt=adopt;
|
|
233
|
+
window.copyData=copyData;
|
|
234
|
+
window.copyLog=copyLog;
|
|
235
|
+
init();
|
|
236
|
+
})();
|
|
237
|
+
</script>
|
|
238
|
+
</body>
|
|
239
|
+
</html>
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>心愿排期向导</title>
|
|
7
|
+
<style><!--SHARED-CSS--></style>
|
|
8
|
+
<style>
|
|
9
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
10
|
+
body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--bg);color:var(--fg);line-height:1.65;-webkit-font-smoothing:antialiased}
|
|
11
|
+
.wrap{max-width:980px;margin:0 auto;padding:48px 22px 90px}
|
|
12
|
+
.hero{background:linear-gradient(180deg,#fff 0%,#f8fbff 100%);border-radius:28px;padding:34px;margin-bottom:20px;box-shadow:var(--shadow,0 1px 2px rgba(0,0,0,.04),0 12px 36px rgba(0,0,0,.06))}
|
|
13
|
+
.eyebrow{font-size:13px;color:var(--blue);font-weight:800;letter-spacing:.08em;text-transform:uppercase;margin-bottom:10px}
|
|
14
|
+
h1{font-size:36px;line-height:1.15;letter-spacing:-.035em;margin-bottom:12px}
|
|
15
|
+
.lead{color:var(--fg2);font-size:17px}
|
|
16
|
+
.meta{margin-top:14px;color:var(--fg3);font-size:13px}
|
|
17
|
+
.badge{display:inline-block;background:var(--soft,#f5f8ff);color:var(--blue);padding:4px 11px;border-radius:999px;font-size:13px;font-weight:700;margin:2px 6px 2px 0}
|
|
18
|
+
.badge.ok{background:#ecfff2;color:#178a3a}
|
|
19
|
+
.badge.warn{background:#fff7e8;color:#b25b00}
|
|
20
|
+
.panel{background:var(--card);border-radius:20px;padding:22px;box-shadow:0 1px 3px rgba(0,0,0,.04);margin:14px 0}
|
|
21
|
+
.panel .hint{font-size:12px;color:var(--fg3);margin-top:6px}
|
|
22
|
+
.toolbar{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0;align-items:center}
|
|
23
|
+
button{border:0;background:var(--blue);color:#fff;border-radius:999px;padding:10px 16px;cursor:pointer;font-size:14px;font-weight:600}
|
|
24
|
+
button.ghost{background:transparent;color:var(--blue);border:1px solid var(--line);min-height:44px}
|
|
25
|
+
button.copy.ghost{background:var(--card);color:var(--blue);border:1.5px solid var(--blue)}
|
|
26
|
+
button.primary{background:var(--blue);color:#fff;font-size:15px;padding:12px 20px}
|
|
27
|
+
input[type=date]{border:1px solid var(--line);border-radius:12px;padding:8px 12px;font-size:15px;font-family:inherit;background:var(--card)}
|
|
28
|
+
.wish{background:var(--card);border-radius:18px;padding:18px;margin:12px 0;box-shadow:0 1px 3px rgba(0,0,0,.04);transition:box-shadow .15s,opacity .15s}
|
|
29
|
+
.wish.on{opacity:.5}
|
|
30
|
+
.wish.on .content{text-decoration:line-through;color:var(--fg3)}
|
|
31
|
+
.wish-head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
|
|
32
|
+
.wish-id{color:var(--blue);font-weight:800;font-size:15px;min-width:48px}
|
|
33
|
+
.wish-check{flex:none}
|
|
34
|
+
.wish-content{font-size:16px;flex:1}
|
|
35
|
+
.wish-meta{display:flex;gap:16px;flex-wrap:wrap;align-items:center;color:var(--fg2);font-size:13px;margin:8px 0 4px}
|
|
36
|
+
.wish-due-input{display:flex;gap:10px;align-items:center}
|
|
37
|
+
.wish-due-input label{color:var(--fg2);font-size:13px}
|
|
38
|
+
.kpi{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:14px 0}
|
|
39
|
+
.kpi .stat{background:var(--card);border-radius:18px;padding:18px;box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
|
40
|
+
.kpi b{display:block;font-size:22px;letter-spacing:-.02em;margin-bottom:4px}
|
|
41
|
+
.kpi span{color:var(--fg2);font-size:13px}
|
|
42
|
+
pre{white-space:pre-wrap;background:#f5f5f7;color:var(--fg);border:1px solid var(--line);border-radius:16px;padding:16px;overflow:auto;font-size:13px;line-height:1.65;max-height:70vh;margin-top:14px;font-family:inherit}
|
|
43
|
+
.notice{padding:12px 16px;border-radius:14px;margin:10px 0;font-size:14px}
|
|
44
|
+
.notice.warn{background:#fff7e8;color:#b25b00}
|
|
45
|
+
.notice.err{background:#fff5f5;color:#ff3b30}
|
|
46
|
+
@media(max-width:800px){.wrap{padding:28px 14px 70px}.hero{padding:26px 20px}h1{font-size:30px}.kpi .stat{padding:12px}}
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<div class="wrap">
|
|
51
|
+
<header class="hero">
|
|
52
|
+
<h1 id="title">心愿排期向导</h1>
|
|
53
|
+
<p class="lead" id="subtitle">数据加载中</p>
|
|
54
|
+
<p class="meta" id="meta"></p>
|
|
55
|
+
</header>
|
|
56
|
+
<section id="injectError" style="display:none"></section>
|
|
57
|
+
|
|
58
|
+
<section class="kpi" id="kpis"></section>
|
|
59
|
+
|
|
60
|
+
<section class="panel">
|
|
61
|
+
<div style="font-weight:700;margin-bottom:10px">📅 全局建议排期日期</div>
|
|
62
|
+
<div class="wish-due-input">
|
|
63
|
+
<input type="date" id="globalDue" />
|
|
64
|
+
<button class="ghost" onclick="applyGlobalToSelected()">应用到所有勾选</button>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="toolbar" style="margin-top:14px">
|
|
67
|
+
<button class="ghost" onclick="selectAll(true)">全选</button>
|
|
68
|
+
<button class="ghost" onclick="selectAll(false)">清空选择</button>
|
|
69
|
+
<button class="ghost" onclick="onlyUnset()">只勾未排期的</button>
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
<section id="wishList"></section>
|
|
74
|
+
|
|
75
|
+
<section class="panel">
|
|
76
|
+
<div style="font-weight:700;margin-bottom:8px">📋 发给 AI 的指令</div>
|
|
77
|
+
<p style="color:var(--fg2);font-size:13px;margin-bottom:12px">复制指令发给 AI,它会按你填的日期更新排期,改完给你回执。</p>
|
|
78
|
+
<div class="toolbar">
|
|
79
|
+
<button class="primary" onclick="adopt()">📋 复制排期指令</button>
|
|
80
|
+
</div>
|
|
81
|
+
<div id="warnArea"></div>
|
|
82
|
+
<pre id="promptOut" style="display:none"></pre>
|
|
83
|
+
</section>
|
|
84
|
+
|
|
85
|
+
<section class="panel">
|
|
86
|
+
<strong>数据与日志</strong>
|
|
87
|
+
<div class="hint">点复制数据保存当前心愿列表 · 点复制日志用于反馈问题(日志不含隐私内容)</div>
|
|
88
|
+
<div class="hm-actions">
|
|
89
|
+
<button class="copy ghost" onclick="copyData()">复制数据</button>
|
|
90
|
+
<button class="copy ghost" onclick="copyLog()">复制日志</button>
|
|
91
|
+
</div>
|
|
92
|
+
</section>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<script id="payload" type="application/json"><!--INJECT-DATA--></script>
|
|
96
|
+
<script><!--SHARED-HELPERS--></script>
|
|
97
|
+
<script>
|
|
98
|
+
(function(){
|
|
99
|
+
"use strict";
|
|
100
|
+
var raw=document.getElementById("payload");
|
|
101
|
+
var payload=null,perr=null;
|
|
102
|
+
try{payload=JSON.parse(raw.textContent||"{}");}catch(e){perr=e.message;}
|
|
103
|
+
if(perr){document.getElementById("injectError").style.display="block";document.getElementById("injectError").innerHTML=window.errorReceipt({message:"数据解析失败",log:perr,payload:null});return;}
|
|
104
|
+
if(!payload||payload.status!=="ok"||!payload.data){document.getElementById("injectError").style.display="block";document.getElementById("injectError").innerHTML=window.errorReceipt({message:(payload&&payload.message)||"数据未注入",retryPrompt:"请重新运行心愿排期(唤醒词:心愿排期)",payload:null});return;}
|
|
105
|
+
window.__hmPayload=payload;
|
|
106
|
+
function data(){return payload.data}
|
|
107
|
+
function items(){return data().items||[]}
|
|
108
|
+
|
|
109
|
+
function init(){
|
|
110
|
+
var d=data();
|
|
111
|
+
document.getElementById('title').textContent=d.title||'心愿排期向导';
|
|
112
|
+
document.getElementById('subtitle').textContent=payload.message||'请勾选并调整排期日期';
|
|
113
|
+
var meta=d.meta||{};
|
|
114
|
+
document.getElementById('meta').textContent=`生成时间:${d.generated_at||''} · 场景:${meta.command_cn||d.command||'wish-batch-plan'}`;
|
|
115
|
+
if(d.suggest_due){
|
|
116
|
+
var g=document.getElementById('globalDue');
|
|
117
|
+
g.value=d.suggest_due;
|
|
118
|
+
}
|
|
119
|
+
renderKPIs();
|
|
120
|
+
renderList();
|
|
121
|
+
// success: 心愿列表已加载,等待用户勾选
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function renderKPIs(){
|
|
125
|
+
var all=items();
|
|
126
|
+
var unset=all.filter(w=>!w.current_due).length;
|
|
127
|
+
var set=all.filter(w=>w.current_due).length;
|
|
128
|
+
document.getElementById('kpis').innerHTML=[
|
|
129
|
+
['可排期的心愿',all.length,''],
|
|
130
|
+
['还没排期',unset,'需要填日期'],
|
|
131
|
+
['已有排期',set,'可改日期'],
|
|
132
|
+
].map(x=>`<div class="stat"><b>${x[1]}</b><span>${x[0]}</span>${x[2]?`<div style="color:var(--fg3);font-size:12px;margin-top:4px">${esc(x[2])}</div>`:''}</div>`).join('');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function renderList(){
|
|
136
|
+
var list=document.getElementById('wishList');
|
|
137
|
+
if(!items().length){list.innerHTML='<div class="panel">'+window.emptyState({icon:'🎯',text:'没有心愿可选',hint:'先记一条心愿,再来排期'})+'</div>';return}
|
|
138
|
+
list.innerHTML=items().map(renderWish).join('');
|
|
139
|
+
items().forEach(function(w){
|
|
140
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
141
|
+
if(cb) cb.addEventListener('change',function(e){document.getElementById('wish_'+w.id).classList.toggle('on',e.target.checked)});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function renderWish(w){
|
|
146
|
+
var due=(w.suggested_due||w.current_due||'').slice(0,10);
|
|
147
|
+
var checked=w.selected?'checked':'';
|
|
148
|
+
return `<article class="wish" id="wish_${w.id}">
|
|
149
|
+
<div class="wish-head">
|
|
150
|
+
<input type="checkbox" id="cb_${w.id}" class="wish-check" ${checked} data-id="${w.id}" />
|
|
151
|
+
<span class="wish-id">#${esc(w.id)}</span>
|
|
152
|
+
<span class="wish-content">${esc(w.content)}</span>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="wish-meta">
|
|
155
|
+
${w.category?`<span class="badge">${esc(w.category)}</span>`:''}
|
|
156
|
+
${w.sub_category?`<span class="badge">${esc(w.sub_category)}</span>`:''}
|
|
157
|
+
${w.current_due?`<span class="badge warn">当前排期:${esc(w.current_due)}</span>`:'<span class="badge">未排期</span>'}
|
|
158
|
+
${w.feishu_task_guid?`<span class="badge ok">已同步飞书</span>`:''}
|
|
159
|
+
</div>
|
|
160
|
+
<div class="wish-due-input">
|
|
161
|
+
<label>排期日期</label>
|
|
162
|
+
<input type="date" id="due_${w.id}" value="${esc(due)}" data-id="${w.id}" />
|
|
163
|
+
</div>
|
|
164
|
+
</article>`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function applyGlobalToSelected(){
|
|
168
|
+
var gv=document.getElementById('globalDue').value;
|
|
169
|
+
if(!gv){window.toast('请先填全局建议日期','再应用到勾选项',{icon:'warn'});return}
|
|
170
|
+
items().forEach(function(w){
|
|
171
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
172
|
+
if(cb&&cb.checked){
|
|
173
|
+
var di=document.getElementById('due_'+w.id);
|
|
174
|
+
if(di) di.value=gv;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function selectAll(on){
|
|
180
|
+
items().forEach(function(w){
|
|
181
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
182
|
+
if(cb){cb.checked=on;document.getElementById('wish_'+w.id).classList.toggle('on',on)}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function onlyUnset(){
|
|
187
|
+
items().forEach(function(w){
|
|
188
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
189
|
+
if(cb){
|
|
190
|
+
var on=!w.current_due;
|
|
191
|
+
cb.checked=on;
|
|
192
|
+
document.getElementById('wish_'+w.id).classList.toggle('on',on);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function selectedRows(){
|
|
198
|
+
var rows=[];
|
|
199
|
+
items().forEach(function(w){
|
|
200
|
+
var cb=document.getElementById('cb_'+w.id);
|
|
201
|
+
var di=document.getElementById('due_'+w.id);
|
|
202
|
+
if(cb&&cb.checked){
|
|
203
|
+
rows.push({id:w.id,content:w.content,due:(di&&di.value)||'',current_due:w.current_due});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
return rows;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function buildPrompt(rows,ctx){
|
|
210
|
+
if(!rows.length) return '未选择任何心愿';
|
|
211
|
+
var missing=rows.filter(r=>!r.due);
|
|
212
|
+
if(missing.length){
|
|
213
|
+
return `⚠️ 以下 ${missing.length} 条被勾选但未填排期日期:\n${missing.map(r=>` · #${r.id} ${r.content}`).join('\n')}\n\n请补全后再复制`;
|
|
214
|
+
}
|
|
215
|
+
return [
|
|
216
|
+
'① 场景: 我用心愿排期向导,给 '+rows.length+' 个心愿设了排期',
|
|
217
|
+
'',
|
|
218
|
+
'② 数据(要更新的心愿):',
|
|
219
|
+
...rows.map(r=>` · 心愿 #${r.id} "${r.content}" → 排期 ${r.due}${r.current_due&&r.current_due!==r.due?` (原排期 ${r.current_due})`:''}`),
|
|
220
|
+
'',
|
|
221
|
+
'③ 期望: 请按以上日期更新这些心愿的排期,飞书任务同步调整;改完给我回执。',
|
|
222
|
+
'',
|
|
223
|
+
'④ 来源: 心愿排期向导 · '+ctx.generated_at,
|
|
224
|
+
].join('\n');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function warnIfAny(rows){
|
|
228
|
+
var notes=[];
|
|
229
|
+
var overwrites=rows.filter(r=>r.current_due&&r.current_due!==r.due);
|
|
230
|
+
if(overwrites.length){
|
|
231
|
+
notes.push(`<div class="notice warn">⚠️ ${overwrites.length} 条心愿将被覆盖原排期:${overwrites.map(r=>`#${r.id}(${r.current_due}→${r.due})`).join(', ')}</div>`);
|
|
232
|
+
}
|
|
233
|
+
var nofb=items().filter(w=>!w.feishu_task_guid);
|
|
234
|
+
var selNoFb=rows.filter(r=>nofb.some(n=>n.id===r.id));
|
|
235
|
+
if(selNoFb.length){
|
|
236
|
+
notes.push(`<div class="notice warn">ℹ️ ${selNoFb.length} 条心愿未同步飞书,可能需先跑 <code>备忘录同步</code> 补建</div>`);
|
|
237
|
+
}
|
|
238
|
+
document.getElementById('warnArea').innerHTML=notes.join('');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function adopt(){
|
|
242
|
+
var rows=selectedRows();
|
|
243
|
+
if(!rows.length){window.toast('未选择任何心愿','勾选要排期的心愿',{icon:'warn'});return}
|
|
244
|
+
var miss=rows.filter(r=>!r.due);
|
|
245
|
+
if(miss.length){
|
|
246
|
+
window.toast('有 '+miss.length+' 条未填排期','请补全或取消勾选',{icon:'warn'});
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
warnIfAny(rows);
|
|
250
|
+
var ctx={
|
|
251
|
+
generated_at:data().generated_at||'',
|
|
252
|
+
};
|
|
253
|
+
var text=buildPrompt(rows,ctx);
|
|
254
|
+
var out=document.getElementById('promptOut');
|
|
255
|
+
out.textContent=text;out.style.display='block';
|
|
256
|
+
window.copyText(text,{silent:true});
|
|
257
|
+
window.toast('指令已复制','粘贴发给 AI 即可执行',{icon:'ok'});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function copyData(){window.copyText(window.buildDataText(payload),{silent:true});window.toast('数据已复制','人类可读快照 · 可粘贴到笔记存档',{icon:'copy'});}
|
|
261
|
+
function copyLog(){window.copyText(window.buildLogText(payload),{silent:true});window.toast('日志已复制','诊断信息 · 有异常时粘贴给开发者排查',{icon:'copy'});}
|
|
262
|
+
window.applyGlobalToSelected=applyGlobalToSelected;
|
|
263
|
+
window.selectAll=selectAll;
|
|
264
|
+
window.onlyUnset=onlyUnset;
|
|
265
|
+
window.adopt=adopt;
|
|
266
|
+
window.copyData=copyData;
|
|
267
|
+
window.copyLog=copyLog;
|
|
268
|
+
init();
|
|
269
|
+
})();
|
|
270
|
+
</script>
|
|
271
|
+
</body>
|
|
272
|
+
</html>
|