修改
This commit is contained in:
+16
-8
@@ -182,10 +182,11 @@ def ai_generate(
|
|||||||
|
|
||||||
def ai_review(trades_text: str, period_title: str, image_paths=None) -> str:
|
def ai_review(trades_text: str, period_title: str, image_paths=None) -> str:
|
||||||
n_img = len(image_paths or [])
|
n_img = len(image_paths or [])
|
||||||
|
period_label = "周" if "周" in str(period_title) else "日"
|
||||||
attach_note = (
|
attach_note = (
|
||||||
f"【系统说明:已向模型附带 {n_img} 张复盘附图(自动K线或上传截图),请结合附图分析第5节。】\n\n"
|
f"ℹ️ 【系统说明:已向模型附带 {n_img} 张复盘附图(自动K线或上传截图),请结合附图分析第5节。】\n\n"
|
||||||
if n_img
|
if n_img
|
||||||
else "【系统说明:本次未附带复盘附图,第5节请写明「无附图,无法看图」;保存复盘记录时可勾选「自动生成K线图」。】\n\n"
|
else "ℹ️ 【系统说明:本次未附带复盘附图,第5节请写明「无附图,无法看图」;保存复盘记录时可勾选「自动生成K线图」。】\n\n"
|
||||||
)
|
)
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
你是一位专业交易教练。下面是用户的{period_title}交易记录,请做简洁、可执行的复盘(中文)。
|
你是一位专业交易教练。下面是用户的{period_title}交易记录,请做简洁、可执行的复盘(中文)。
|
||||||
@@ -198,12 +199,19 @@ def ai_review(trades_text: str, period_title: str, image_paths=None) -> str:
|
|||||||
- 禁止用语:人身攻击、夸张定性(如「致命伤」「灾难」);语气克制、对事不对人。
|
- 禁止用语:人身攻击、夸张定性(如「致命伤」「灾难」);语气克制、对事不对人。
|
||||||
- 若有截图且你能辨认,再结合图讨论;看不清或无明确定位则明确说「无法从图确认」,不得虚构 K 线故事。
|
- 若有截图且你能辨认,再结合图讨论;看不清或无明确定位则明确说「无法从图确认」,不得虚构 K 线故事。
|
||||||
|
|
||||||
【输出结构】
|
【输出格式 — Markdown,必须严格遵守】
|
||||||
1. 总体盈亏结构(紧扣笔数、盈亏数字与 RR,少形容词)
|
- 第一行:**交易复盘报告({period_label}度)**
|
||||||
2. 心态与执行(每笔 1–10 分 + 一句依据;依据必须对应记录字段)
|
- 五个大节标题必须**完全一致**(含 emoji,不要用其它编号或改名):
|
||||||
3. 行为标签(提前离场 / 乱开仓 / 扛单等):仅在有字段或自述支撑时点名;否则写「记录未勾选或未描述,不作强加」
|
**1. 📊 总体盈亏结构**
|
||||||
4. 改进建议(最多 3 条,每条具体可执行)
|
**2. 🧠 心态与执行**
|
||||||
5. 图表(若有且可读):结合价格行为简述;否则一两句说明无法看图分析
|
**3. 🏷️ 行为标签**
|
||||||
|
**4. ✅ 改进建议**
|
||||||
|
**5. 📈 图表分析**
|
||||||
|
- 每节正文用 `- **子项名**:内容` 列表;第4节改进建议用有序列表 `1. 2. 3.`
|
||||||
|
- 第1节至少包含:**笔数/盈亏**、**风险回报比**、**总结**
|
||||||
|
- 第2节至少包含:**得分**(1–10)、**依据**(对应记录字段)
|
||||||
|
- 第5节至少包含:**趋势确认**、**执行路径**(记录不足则写明)
|
||||||
|
- 语气简洁,少形容词;不要输出代码块、不要表格
|
||||||
|
|
||||||
交易记录:
|
交易记录:
|
||||||
{trades_text}
|
{trades_text}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response
|
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response, send_file
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import csv
|
import csv
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@@ -7517,6 +7517,16 @@ def api_reviews():
|
|||||||
return jsonify([row_to_dict(r) for r in rows])
|
return jsonify([row_to_dict(r) for r in rows])
|
||||||
|
|
||||||
|
|
||||||
|
_AI_REVIEW_RENDER_JS = os.path.join(os.path.dirname(BASE_DIR), "static", "ai_review_render.js")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/static/ai_review_render.js")
|
||||||
|
def static_ai_review_render_js():
|
||||||
|
if not os.path.isfile(_AI_REVIEW_RENDER_JS):
|
||||||
|
return Response("not found", status=404, mimetype="text/plain; charset=utf-8")
|
||||||
|
return send_file(_AI_REVIEW_RENDER_JS, mimetype="application/javascript; charset=utf-8")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/export/review_md/<rid>")
|
@app.route("/export/review_md/<rid>")
|
||||||
@login_required
|
@login_required
|
||||||
def export_review_md(rid):
|
def export_review_md(rid):
|
||||||
|
|||||||
@@ -62,6 +62,15 @@
|
|||||||
.pnl-loss{color:#ff6666;font-weight:600}
|
.pnl-loss{color:#ff6666;font-weight:600}
|
||||||
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
||||||
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
||||||
|
.ai-result.ai-result-md,.detail-modal .panel-body.md-review{white-space:normal}
|
||||||
|
.ai-result-md p,.detail-modal .panel-body.md-review p{margin:6px 0;color:#dde2ff}
|
||||||
|
.ai-result-md ul,.ai-result-md ol,.detail-modal .panel-body.md-review ul,.detail-modal .panel-body.md-review ol{margin:6px 0 8px 1.25em;padding:0}
|
||||||
|
.ai-result-md li,.detail-modal .panel-body.md-review li{margin:5px 0;line-height:1.5}
|
||||||
|
.ai-result-md strong,.detail-modal .panel-body.md-review strong{color:#f0f3ff;font-weight:600}
|
||||||
|
.ai-result-md h2,.detail-modal .panel-body.md-review h2{font-size:1.02rem;color:#b8c8ff;margin:14px 0 8px;padding-bottom:4px;border-bottom:1px solid #2e2e45}
|
||||||
|
.ai-result-md h3,.detail-modal .panel-body.md-review h3{font-size:.92rem;color:#c9d4ff;margin:10px 0 6px}
|
||||||
|
.ai-result-md code,.detail-modal .panel-body.md-review code{background:#252538;padding:1px 4px;border-radius:4px;font-size:.82em}
|
||||||
|
.ai-result-md .md-raw-block-title,.detail-modal .panel-body.md-review .md-raw-block-title{margin-top:14px;padding-top:10px;border-top:1px dashed #3a3a55;color:#a8b0d8;font-weight:600}
|
||||||
.price-up{color:#4cd97f}
|
.price-up{color:#4cd97f}
|
||||||
.price-down{color:#ff6666}
|
.price-down{color:#ff6666}
|
||||||
.price-flat{color:#cfd3ef}
|
.price-flat{color:#cfd3ef}
|
||||||
@@ -852,6 +861,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/ai_review_render.js?v=1"></script>
|
||||||
<script>
|
<script>
|
||||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||||
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
||||||
@@ -916,12 +926,39 @@ document.addEventListener("keydown", function(e){
|
|||||||
const card = document.getElementById("review-card");
|
const card = document.getElementById("review-card");
|
||||||
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
||||||
});
|
});
|
||||||
|
function setAiReviewMarkdown(el, rawText){
|
||||||
|
if(!el) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
AiReviewRender.setElementMarkdown(el, rawText || "");
|
||||||
|
} else {
|
||||||
|
el.classList.remove("ai-result-md");
|
||||||
|
el.innerText = rawText || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setDetailBodyPlain(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
body.classList.remove("md-review");
|
||||||
|
body.innerText = text || "";
|
||||||
|
}
|
||||||
|
function setDetailBodyMarkdown(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
body.classList.add("md-review");
|
||||||
|
AiReviewRender.setElementMarkdown(body, text || "");
|
||||||
|
} else {
|
||||||
|
setDetailBodyPlain(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
function openAiInlineResultFullscreen(title, elementId){
|
function openAiInlineResultFullscreen(title, elementId){
|
||||||
const el = document.getElementById(elementId || "daily_result");
|
const el = document.getElementById(elementId || "daily_result");
|
||||||
const text = String((el && el.innerText) || "").trim();
|
const text = (window.AiReviewRender && AiReviewRender.getElementMarkdown)
|
||||||
|
? String(AiReviewRender.getElementMarkdown(el) || "").trim()
|
||||||
|
: String((el && el.innerText) || "").trim();
|
||||||
if(!text){ alert("暂无内容"); return; }
|
if(!text){ alert("暂无内容"); return; }
|
||||||
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
||||||
document.getElementById("detailBody").innerText = text;
|
setDetailBodyMarkdown(text);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -960,7 +997,7 @@ function openJournalDetail(id){
|
|||||||
`备注:${o.note || "无"}`,
|
`备注:${o.note || "无"}`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = detail;
|
setDetailBodyPlain(detail);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
if(o.image){
|
if(o.image){
|
||||||
imgEl.src = `/static/images/${o.image}`;
|
imgEl.src = `/static/images/${o.image}`;
|
||||||
@@ -977,7 +1014,7 @@ function openReviewDetail(id, fullscreen){
|
|||||||
const r = reviewCache[id];
|
const r = reviewCache[id];
|
||||||
if(!r){ return; }
|
if(!r){ return; }
|
||||||
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = r.content || "";
|
setDetailBodyMarkdown(r.content || "");
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -1189,7 +1226,7 @@ function genDaily(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("daily_result");
|
const el=document.getElementById("daily_result");
|
||||||
const wrap=document.getElementById("daily_result_wrap");
|
const wrap=document.getElementById("daily_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
@@ -1204,7 +1241,7 @@ function genWeekly(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("weekly_result");
|
const el=document.getElementById("weekly_result");
|
||||||
const wrap=document.getElementById("weekly_result_wrap");
|
const wrap=document.getElementById("weekly_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response
|
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response, send_file
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import csv
|
import csv
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@@ -7597,6 +7597,16 @@ def api_reviews():
|
|||||||
return jsonify([row_to_dict(r) for r in rows])
|
return jsonify([row_to_dict(r) for r in rows])
|
||||||
|
|
||||||
|
|
||||||
|
_AI_REVIEW_RENDER_JS = os.path.join(os.path.dirname(BASE_DIR), "static", "ai_review_render.js")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/static/ai_review_render.js")
|
||||||
|
def static_ai_review_render_js():
|
||||||
|
if not os.path.isfile(_AI_REVIEW_RENDER_JS):
|
||||||
|
return Response("not found", status=404, mimetype="text/plain; charset=utf-8")
|
||||||
|
return send_file(_AI_REVIEW_RENDER_JS, mimetype="application/javascript; charset=utf-8")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/export/review_md/<rid>")
|
@app.route("/export/review_md/<rid>")
|
||||||
@login_required
|
@login_required
|
||||||
def export_review_md(rid):
|
def export_review_md(rid):
|
||||||
|
|||||||
@@ -62,6 +62,15 @@
|
|||||||
.pnl-loss{color:#ff6666;font-weight:600}
|
.pnl-loss{color:#ff6666;font-weight:600}
|
||||||
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
||||||
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
||||||
|
.ai-result.ai-result-md,.detail-modal .panel-body.md-review{white-space:normal}
|
||||||
|
.ai-result-md p,.detail-modal .panel-body.md-review p{margin:6px 0;color:#dde2ff}
|
||||||
|
.ai-result-md ul,.ai-result-md ol,.detail-modal .panel-body.md-review ul,.detail-modal .panel-body.md-review ol{margin:6px 0 8px 1.25em;padding:0}
|
||||||
|
.ai-result-md li,.detail-modal .panel-body.md-review li{margin:5px 0;line-height:1.5}
|
||||||
|
.ai-result-md strong,.detail-modal .panel-body.md-review strong{color:#f0f3ff;font-weight:600}
|
||||||
|
.ai-result-md h2,.detail-modal .panel-body.md-review h2{font-size:1.02rem;color:#b8c8ff;margin:14px 0 8px;padding-bottom:4px;border-bottom:1px solid #2e2e45}
|
||||||
|
.ai-result-md h3,.detail-modal .panel-body.md-review h3{font-size:.92rem;color:#c9d4ff;margin:10px 0 6px}
|
||||||
|
.ai-result-md code,.detail-modal .panel-body.md-review code{background:#252538;padding:1px 4px;border-radius:4px;font-size:.82em}
|
||||||
|
.ai-result-md .md-raw-block-title,.detail-modal .panel-body.md-review .md-raw-block-title{margin-top:14px;padding-top:10px;border-top:1px dashed #3a3a55;color:#a8b0d8;font-weight:600}
|
||||||
.price-up{color:#4cd97f}
|
.price-up{color:#4cd97f}
|
||||||
.price-down{color:#ff6666}
|
.price-down{color:#ff6666}
|
||||||
.price-flat{color:#cfd3ef}
|
.price-flat{color:#cfd3ef}
|
||||||
@@ -852,6 +861,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/ai_review_render.js?v=1"></script>
|
||||||
<script>
|
<script>
|
||||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||||
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
||||||
@@ -916,12 +926,39 @@ document.addEventListener("keydown", function(e){
|
|||||||
const card = document.getElementById("review-card");
|
const card = document.getElementById("review-card");
|
||||||
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
||||||
});
|
});
|
||||||
|
function setAiReviewMarkdown(el, rawText){
|
||||||
|
if(!el) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
AiReviewRender.setElementMarkdown(el, rawText || "");
|
||||||
|
} else {
|
||||||
|
el.classList.remove("ai-result-md");
|
||||||
|
el.innerText = rawText || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setDetailBodyPlain(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
body.classList.remove("md-review");
|
||||||
|
body.innerText = text || "";
|
||||||
|
}
|
||||||
|
function setDetailBodyMarkdown(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
body.classList.add("md-review");
|
||||||
|
AiReviewRender.setElementMarkdown(body, text || "");
|
||||||
|
} else {
|
||||||
|
setDetailBodyPlain(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
function openAiInlineResultFullscreen(title, elementId){
|
function openAiInlineResultFullscreen(title, elementId){
|
||||||
const el = document.getElementById(elementId || "daily_result");
|
const el = document.getElementById(elementId || "daily_result");
|
||||||
const text = String((el && el.innerText) || "").trim();
|
const text = (window.AiReviewRender && AiReviewRender.getElementMarkdown)
|
||||||
|
? String(AiReviewRender.getElementMarkdown(el) || "").trim()
|
||||||
|
: String((el && el.innerText) || "").trim();
|
||||||
if(!text){ alert("暂无内容"); return; }
|
if(!text){ alert("暂无内容"); return; }
|
||||||
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
||||||
document.getElementById("detailBody").innerText = text;
|
setDetailBodyMarkdown(text);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -960,7 +997,7 @@ function openJournalDetail(id){
|
|||||||
`备注:${o.note || "无"}`,
|
`备注:${o.note || "无"}`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = detail;
|
setDetailBodyPlain(detail);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
if(o.image){
|
if(o.image){
|
||||||
imgEl.src = `/static/images/${o.image}`;
|
imgEl.src = `/static/images/${o.image}`;
|
||||||
@@ -977,7 +1014,7 @@ function openReviewDetail(id, fullscreen){
|
|||||||
const r = reviewCache[id];
|
const r = reviewCache[id];
|
||||||
if(!r){ return; }
|
if(!r){ return; }
|
||||||
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = r.content || "";
|
setDetailBodyMarkdown(r.content || "");
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -1189,7 +1226,7 @@ function genDaily(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("daily_result");
|
const el=document.getElementById("daily_result");
|
||||||
const wrap=document.getElementById("daily_result_wrap");
|
const wrap=document.getElementById("daily_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
@@ -1204,7 +1241,7 @@ function genWeekly(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("weekly_result");
|
const el=document.getElementById("weekly_result");
|
||||||
const wrap=document.getElementById("weekly_result_wrap");
|
const wrap=document.getElementById("weekly_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response
|
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response, send_file
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import csv
|
import csv
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@@ -7004,6 +7004,16 @@ def api_reviews():
|
|||||||
return jsonify([row_to_dict(r) for r in rows])
|
return jsonify([row_to_dict(r) for r in rows])
|
||||||
|
|
||||||
|
|
||||||
|
_AI_REVIEW_RENDER_JS = os.path.join(os.path.dirname(BASE_DIR), "static", "ai_review_render.js")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/static/ai_review_render.js")
|
||||||
|
def static_ai_review_render_js():
|
||||||
|
if not os.path.isfile(_AI_REVIEW_RENDER_JS):
|
||||||
|
return Response("not found", status=404, mimetype="text/plain; charset=utf-8")
|
||||||
|
return send_file(_AI_REVIEW_RENDER_JS, mimetype="application/javascript; charset=utf-8")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/export/review_md/<rid>")
|
@app.route("/export/review_md/<rid>")
|
||||||
@login_required
|
@login_required
|
||||||
def export_review_md(rid):
|
def export_review_md(rid):
|
||||||
|
|||||||
@@ -63,6 +63,15 @@
|
|||||||
.pnl-neutral{color:#cfd3ef;font-weight:600}
|
.pnl-neutral{color:#cfd3ef;font-weight:600}
|
||||||
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
||||||
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
||||||
|
.ai-result.ai-result-md,.detail-modal .panel-body.md-review{white-space:normal}
|
||||||
|
.ai-result-md p,.detail-modal .panel-body.md-review p{margin:6px 0;color:#dde2ff}
|
||||||
|
.ai-result-md ul,.ai-result-md ol,.detail-modal .panel-body.md-review ul,.detail-modal .panel-body.md-review ol{margin:6px 0 8px 1.25em;padding:0}
|
||||||
|
.ai-result-md li,.detail-modal .panel-body.md-review li{margin:5px 0;line-height:1.5}
|
||||||
|
.ai-result-md strong,.detail-modal .panel-body.md-review strong{color:#f0f3ff;font-weight:600}
|
||||||
|
.ai-result-md h2,.detail-modal .panel-body.md-review h2{font-size:1.02rem;color:#b8c8ff;margin:14px 0 8px;padding-bottom:4px;border-bottom:1px solid #2e2e45}
|
||||||
|
.ai-result-md h3,.detail-modal .panel-body.md-review h3{font-size:.92rem;color:#c9d4ff;margin:10px 0 6px}
|
||||||
|
.ai-result-md code,.detail-modal .panel-body.md-review code{background:#252538;padding:1px 4px;border-radius:4px;font-size:.82em}
|
||||||
|
.ai-result-md .md-raw-block-title,.detail-modal .panel-body.md-review .md-raw-block-title{margin-top:14px;padding-top:10px;border-top:1px dashed #3a3a55;color:#a8b0d8;font-weight:600}
|
||||||
.price-up{color:#4cd97f}
|
.price-up{color:#4cd97f}
|
||||||
.price-down{color:#ff6666}
|
.price-down{color:#ff6666}
|
||||||
.price-flat{color:#cfd3ef}
|
.price-flat{color:#cfd3ef}
|
||||||
@@ -691,6 +700,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/ai_review_render.js?v=1"></script>
|
||||||
<script>
|
<script>
|
||||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||||
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
||||||
@@ -754,12 +764,39 @@ document.addEventListener("keydown", function(e){
|
|||||||
const card = document.getElementById("review-card");
|
const card = document.getElementById("review-card");
|
||||||
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
||||||
});
|
});
|
||||||
|
function setAiReviewMarkdown(el, rawText){
|
||||||
|
if(!el) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
AiReviewRender.setElementMarkdown(el, rawText || "");
|
||||||
|
} else {
|
||||||
|
el.classList.remove("ai-result-md");
|
||||||
|
el.innerText = rawText || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setDetailBodyPlain(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
body.classList.remove("md-review");
|
||||||
|
body.innerText = text || "";
|
||||||
|
}
|
||||||
|
function setDetailBodyMarkdown(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
body.classList.add("md-review");
|
||||||
|
AiReviewRender.setElementMarkdown(body, text || "");
|
||||||
|
} else {
|
||||||
|
setDetailBodyPlain(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
function openAiInlineResultFullscreen(title, elementId){
|
function openAiInlineResultFullscreen(title, elementId){
|
||||||
const el = document.getElementById(elementId || "daily_result");
|
const el = document.getElementById(elementId || "daily_result");
|
||||||
const text = String((el && el.innerText) || "").trim();
|
const text = (window.AiReviewRender && AiReviewRender.getElementMarkdown)
|
||||||
|
? String(AiReviewRender.getElementMarkdown(el) || "").trim()
|
||||||
|
: String((el && el.innerText) || "").trim();
|
||||||
if(!text){ alert("暂无内容"); return; }
|
if(!text){ alert("暂无内容"); return; }
|
||||||
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
||||||
document.getElementById("detailBody").innerText = text;
|
setDetailBodyMarkdown(text);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -834,7 +871,7 @@ function openJournalDetail(id){
|
|||||||
`备注:${o.note || "无"}`,
|
`备注:${o.note || "无"}`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = detail;
|
setDetailBodyPlain(detail);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
if(o.image){
|
if(o.image){
|
||||||
imgEl.src = `/static/images/${o.image}`;
|
imgEl.src = `/static/images/${o.image}`;
|
||||||
@@ -851,7 +888,7 @@ function openReviewDetail(id, fullscreen){
|
|||||||
const r = reviewCache[id];
|
const r = reviewCache[id];
|
||||||
if(!r){ return; }
|
if(!r){ return; }
|
||||||
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = r.content || "";
|
setDetailBodyMarkdown(r.content || "");
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -1022,7 +1059,7 @@ function genDaily(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("daily_result");
|
const el=document.getElementById("daily_result");
|
||||||
const wrap=document.getElementById("daily_result_wrap");
|
const wrap=document.getElementById("daily_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
@@ -1037,7 +1074,7 @@ function genWeekly(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("weekly_result");
|
const el=document.getElementById("weekly_result");
|
||||||
const wrap=document.getElementById("weekly_result_wrap");
|
const wrap=document.getElementById("weekly_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response
|
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response, send_file
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import csv
|
import csv
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@@ -6912,6 +6912,16 @@ def api_reviews():
|
|||||||
return jsonify([row_to_dict(r) for r in rows])
|
return jsonify([row_to_dict(r) for r in rows])
|
||||||
|
|
||||||
|
|
||||||
|
_AI_REVIEW_RENDER_JS = os.path.join(os.path.dirname(BASE_DIR), "static", "ai_review_render.js")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/static/ai_review_render.js")
|
||||||
|
def static_ai_review_render_js():
|
||||||
|
if not os.path.isfile(_AI_REVIEW_RENDER_JS):
|
||||||
|
return Response("not found", status=404, mimetype="text/plain; charset=utf-8")
|
||||||
|
return send_file(_AI_REVIEW_RENDER_JS, mimetype="application/javascript; charset=utf-8")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/export/review_md/<rid>")
|
@app.route("/export/review_md/<rid>")
|
||||||
@login_required
|
@login_required
|
||||||
def export_review_md(rid):
|
def export_review_md(rid):
|
||||||
|
|||||||
@@ -62,6 +62,15 @@
|
|||||||
.pnl-loss{color:#ff6666;font-weight:600}
|
.pnl-loss{color:#ff6666;font-weight:600}
|
||||||
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
.flash{padding:10px;background:#1e2533;color:#4cc2ff;border-radius:10px;margin-bottom:12px;text-align:center;border:1px solid #304164}
|
||||||
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
.ai-result{background:#1a1a29;border:1px solid #2e2e45;border-radius:8px;padding:10px;white-space:pre-wrap;max-height:220px;overflow:auto;font-size:.84rem;line-height:1.45;margin-top:8px}
|
||||||
|
.ai-result.ai-result-md,.detail-modal .panel-body.md-review{white-space:normal}
|
||||||
|
.ai-result-md p,.detail-modal .panel-body.md-review p{margin:6px 0;color:#dde2ff}
|
||||||
|
.ai-result-md ul,.ai-result-md ol,.detail-modal .panel-body.md-review ul,.detail-modal .panel-body.md-review ol{margin:6px 0 8px 1.25em;padding:0}
|
||||||
|
.ai-result-md li,.detail-modal .panel-body.md-review li{margin:5px 0;line-height:1.5}
|
||||||
|
.ai-result-md strong,.detail-modal .panel-body.md-review strong{color:#f0f3ff;font-weight:600}
|
||||||
|
.ai-result-md h2,.detail-modal .panel-body.md-review h2{font-size:1.02rem;color:#b8c8ff;margin:14px 0 8px;padding-bottom:4px;border-bottom:1px solid #2e2e45}
|
||||||
|
.ai-result-md h3,.detail-modal .panel-body.md-review h3{font-size:.92rem;color:#c9d4ff;margin:10px 0 6px}
|
||||||
|
.ai-result-md code,.detail-modal .panel-body.md-review code{background:#252538;padding:1px 4px;border-radius:4px;font-size:.82em}
|
||||||
|
.ai-result-md .md-raw-block-title,.detail-modal .panel-body.md-review .md-raw-block-title{margin-top:14px;padding-top:10px;border-top:1px dashed #3a3a55;color:#a8b0d8;font-weight:600}
|
||||||
.price-up{color:#4cd97f}
|
.price-up{color:#4cd97f}
|
||||||
.price-down{color:#ff6666}
|
.price-down{color:#ff6666}
|
||||||
.price-flat{color:#cfd3ef}
|
.price-flat{color:#cfd3ef}
|
||||||
@@ -861,6 +870,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/ai_review_render.js?v=1"></script>
|
||||||
<script>
|
<script>
|
||||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||||
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
const JOURNAL_ENTRY_REASON_OTHER = {{ entry_reason_other_value | tojson }};
|
||||||
@@ -925,12 +935,39 @@ document.addEventListener("keydown", function(e){
|
|||||||
const card = document.getElementById("review-card");
|
const card = document.getElementById("review-card");
|
||||||
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
if(card && card.classList.contains("is-fullscreen")){ toggleReviewCardFullscreen(); }
|
||||||
});
|
});
|
||||||
|
function setAiReviewMarkdown(el, rawText){
|
||||||
|
if(!el) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
AiReviewRender.setElementMarkdown(el, rawText || "");
|
||||||
|
} else {
|
||||||
|
el.classList.remove("ai-result-md");
|
||||||
|
el.innerText = rawText || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setDetailBodyPlain(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
body.classList.remove("md-review");
|
||||||
|
body.innerText = text || "";
|
||||||
|
}
|
||||||
|
function setDetailBodyMarkdown(text){
|
||||||
|
const body = document.getElementById("detailBody");
|
||||||
|
if(!body) return;
|
||||||
|
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
|
||||||
|
body.classList.add("md-review");
|
||||||
|
AiReviewRender.setElementMarkdown(body, text || "");
|
||||||
|
} else {
|
||||||
|
setDetailBodyPlain(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
function openAiInlineResultFullscreen(title, elementId){
|
function openAiInlineResultFullscreen(title, elementId){
|
||||||
const el = document.getElementById(elementId || "daily_result");
|
const el = document.getElementById(elementId || "daily_result");
|
||||||
const text = String((el && el.innerText) || "").trim();
|
const text = (window.AiReviewRender && AiReviewRender.getElementMarkdown)
|
||||||
|
? String(AiReviewRender.getElementMarkdown(el) || "").trim()
|
||||||
|
: String((el && el.innerText) || "").trim();
|
||||||
if(!text){ alert("暂无内容"); return; }
|
if(!text){ alert("暂无内容"); return; }
|
||||||
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
document.getElementById("detailTitle").innerText = title || "AI复盘";
|
||||||
document.getElementById("detailBody").innerText = text;
|
setDetailBodyMarkdown(text);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -969,7 +1006,7 @@ function openJournalDetail(id){
|
|||||||
`备注:${o.note || "无"}`,
|
`备注:${o.note || "无"}`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
document.getElementById("detailTitle").innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = detail;
|
setDetailBodyPlain(detail);
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
if(o.image){
|
if(o.image){
|
||||||
imgEl.src = `/static/images/${o.image}`;
|
imgEl.src = `/static/images/${o.image}`;
|
||||||
@@ -986,7 +1023,7 @@ function openReviewDetail(id, fullscreen){
|
|||||||
const r = reviewCache[id];
|
const r = reviewCache[id];
|
||||||
if(!r){ return; }
|
if(!r){ return; }
|
||||||
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}|${r.target_date || "-"}`;
|
||||||
document.getElementById("detailBody").innerText = r.content || "";
|
setDetailBodyMarkdown(r.content || "");
|
||||||
const imgEl = document.getElementById("detailImage");
|
const imgEl = document.getElementById("detailImage");
|
||||||
imgEl.src = "";
|
imgEl.src = "";
|
||||||
imgEl.style.display = "none";
|
imgEl.style.display = "none";
|
||||||
@@ -1198,7 +1235,7 @@ function genDaily(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("daily_result");
|
const el=document.getElementById("daily_result");
|
||||||
const wrap=document.getElementById("daily_result_wrap");
|
const wrap=document.getElementById("daily_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
@@ -1213,7 +1250,7 @@ function genWeekly(){
|
|||||||
.then(r=>r.json()).then(data=>{
|
.then(r=>r.json()).then(data=>{
|
||||||
const el=document.getElementById("weekly_result");
|
const el=document.getElementById("weekly_result");
|
||||||
const wrap=document.getElementById("weekly_result_wrap");
|
const wrap=document.getElementById("weekly_result_wrap");
|
||||||
el.innerText=data.result;
|
setAiReviewMarkdown(el, data.result);
|
||||||
if(wrap){ wrap.style.display="block"; }
|
if(wrap){ wrap.style.display="block"; }
|
||||||
else { el.style.display="block"; }
|
else { el.style.display="block"; }
|
||||||
loadReviews();
|
loadReviews();
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* AI 日复盘 / 周复盘:Markdown 子集渲染 + 五节大标题图标兜底
|
||||||
|
*/
|
||||||
|
(function (global) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var SECTION_FIXES = [
|
||||||
|
{ re: /^\*\*1\.\s*(?!📊)总体盈亏结构\*\*/m, rep: "**1. 📊 总体盈亏结构**" },
|
||||||
|
{ re: /^\*\*2\.\s*(?!🧠)心态与执行\*\*/m, rep: "**2. 🧠 心态与执行**" },
|
||||||
|
{ re: /^\*\*3\.\s*(?!🏷️)行为标签\*\*/m, rep: "**3. 🏷️ 行为标签**" },
|
||||||
|
{ re: /^\*\*4\.\s*(?!✅)改进建议\*\*/m, rep: "**4. ✅ 改进建议**" },
|
||||||
|
{ re: /^\*\*5\.\s*(?!📈)图表(?:分析)?\*\*/m, rep: "**5. 📈 图表分析**" },
|
||||||
|
{ re: /^1\.\s*(?!📊)总体盈亏结构/m, rep: "**1. 📊 总体盈亏结构**" },
|
||||||
|
{ re: /^2\.\s*(?!🧠)心态与执行/m, rep: "**2. 🧠 心态与执行**" },
|
||||||
|
{ re: /^3\.\s*(?!🏷️)行为标签/m, rep: "**3. 🏷️ 行为标签**" },
|
||||||
|
{ re: /^4\.\s*(?!✅)改进建议/m, rep: "**4. ✅ 改进建议**" },
|
||||||
|
{ re: /^5\.\s*(?!📈)图表/m, rep: "**5. 📈 图表分析**" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function escapeHtml(s) {
|
||||||
|
return String(s || "")
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseInline(raw) {
|
||||||
|
var s = escapeHtml(raw);
|
||||||
|
s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
||||||
|
s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enhanceReviewHeadings(text) {
|
||||||
|
var out = String(text || "");
|
||||||
|
SECTION_FIXES.forEach(function (item) {
|
||||||
|
out = out.replace(item.re, item.rep);
|
||||||
|
});
|
||||||
|
if (/^【系统说明/m.test(out) && !/^ℹ️/m.test(out)) {
|
||||||
|
out = out.replace(/^【系统说明/gm, "ℹ️ 【系统说明");
|
||||||
|
}
|
||||||
|
if (/^原始记录:/m.test(out) && !/^📎/m.test(out)) {
|
||||||
|
out = out.replace(/^原始记录:/gm, "📎 **原始记录**");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkdown(text) {
|
||||||
|
var src = enhanceReviewHeadings(text);
|
||||||
|
var lines = src.replace(/\r\n/g, "\n").split("\n");
|
||||||
|
var html = [];
|
||||||
|
var inUl = false;
|
||||||
|
var inOl = false;
|
||||||
|
|
||||||
|
function closeLists() {
|
||||||
|
if (inUl) {
|
||||||
|
html.push("</ul>");
|
||||||
|
inUl = false;
|
||||||
|
}
|
||||||
|
if (inOl) {
|
||||||
|
html.push("</ol>");
|
||||||
|
inOl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.forEach(function (line) {
|
||||||
|
var trimmed = line.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
closeLists();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var hm = trimmed.match(/^(#{1,3})\s+(.+)$/);
|
||||||
|
if (hm) {
|
||||||
|
closeLists();
|
||||||
|
var level = hm[1].length + 1;
|
||||||
|
if (level > 4) level = 4;
|
||||||
|
html.push("<h" + level + ">" + parseInline(hm[2]) + "</h" + level + ">");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var ulm = trimmed.match(/^[-*]\s+(.+)$/);
|
||||||
|
if (ulm) {
|
||||||
|
if (!inUl) {
|
||||||
|
closeLists();
|
||||||
|
html.push("<ul>");
|
||||||
|
inUl = true;
|
||||||
|
}
|
||||||
|
html.push("<li>" + parseInline(ulm[1]) + "</li>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var olm = trimmed.match(/^\d+\.\s+(.+)$/);
|
||||||
|
if (olm && !/^\*\*/.test(trimmed)) {
|
||||||
|
if (!inOl) {
|
||||||
|
closeLists();
|
||||||
|
html.push("<ol>");
|
||||||
|
inOl = true;
|
||||||
|
}
|
||||||
|
html.push("<li>" + parseInline(olm[1]) + "</li>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeLists();
|
||||||
|
if (/^📎\s*\*\*原始记录\*\*/.test(trimmed) || /^原始记录:/.test(trimmed)) {
|
||||||
|
html.push('<div class="md-raw-block-title">' + parseInline(trimmed) + "</div>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
html.push("<p>" + parseInline(trimmed) + "</p>");
|
||||||
|
});
|
||||||
|
closeLists();
|
||||||
|
return html.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setElementMarkdown(el, rawText) {
|
||||||
|
if (!el) return;
|
||||||
|
var raw = String(rawText || "");
|
||||||
|
el.dataset.markdownRaw = raw;
|
||||||
|
el.classList.add("ai-result-md");
|
||||||
|
el.innerHTML = renderMarkdown(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getElementMarkdown(el) {
|
||||||
|
if (!el) return "";
|
||||||
|
if (el.dataset && el.dataset.markdownRaw != null) {
|
||||||
|
return el.dataset.markdownRaw;
|
||||||
|
}
|
||||||
|
return el.innerText || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
global.AiReviewRender = {
|
||||||
|
enhanceReviewHeadings: enhanceReviewHeadings,
|
||||||
|
renderMarkdown: renderMarkdown,
|
||||||
|
setElementMarkdown: setElementMarkdown,
|
||||||
|
getElementMarkdown: getElementMarkdown,
|
||||||
|
};
|
||||||
|
})(typeof window !== "undefined" ? window : this);
|
||||||
Reference in New Issue
Block a user