docs+chore: 同步操作说明与端到端自检(字段库/单位换算、因子参数化)
- docs/USAGE.md:
· 因子层一行改为「代码注册表投影 + 参数化实例,参数写在名字里以冻结口径」;
· 新增 `GET/POST/PATCH /api/factors`、`GET /api/factors/templates` 与
`/api/condition-fields`、`/fields` 的说明;
· 新增「参数化因子(2026-10)」块:受控范围、键必须写全参数(缺项就靠可改的
默认值兜底 = 追溯改义,所以拒绝)、口径文案按代码收敛、停用 ≠ 删除、
参数化因子也能当过滤条件;
· 自检清单一并更新(pytest 500 条;verify_strategy_workspace 145 项 skip-job;
verify_ui_alignment 8 页 160 项;新增 verify_unit_conversion、verify_factor_params)。
- scripts/:新增 verify_unit_conversion.py(单位只能在给定范围里选 + 界面单位⇄
基准单位换算)、verify_factor_params.py(参数暴露/界面新建/越界拒绝/停用语义,
跑完自动清掉临时因子);verify_strategy_workspace.py 加 [5.7b] 因子参数化一节,
临时因子的清理挪进 finally(断言中途失败也不给真人库留垃圾)。
- .gitignore:docs/screenshots/ 是临时验证证据,不入库(文件留在磁盘)。
This commit is contained in:
@@ -56,7 +56,7 @@ import urllib.request
|
||||
|
||||
WEB = os.environ.get("QLIB_WEB", "http://127.0.0.1:3000")
|
||||
|
||||
DEFAULT_PAGES = ["/", "/backtest", "/strategies", "/selection", "/experiments", "/stocks", "/factors"]
|
||||
DEFAULT_PAGES = ["/", "/backtest", "/strategies", "/selection", "/fields", "/experiments", "/stocks", "/factors"]
|
||||
|
||||
CHROME_CANDIDATES = [
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
@@ -216,6 +216,8 @@ PROBE = r"""
|
||||
// 多行文本域:高度随行数变化,是「内容决定高度」的控件,
|
||||
// 不参与「同排等高」(它本来就该比单行输入高)与「高度取值归一」比较
|
||||
isTextarea: e.tagName === "TEXTAREA",
|
||||
// 卡片式多选项(整张卡可点):高度由文案决定,不计入「控件高度归一」
|
||||
cardPick: !!(e.closest && e.closest(".strategy-pick__item")),
|
||||
x: r.x, y: r.y, w: r.w, h: r.h, fs: parseFloat(s.fontSize),
|
||||
radius: s.borderRadius, padL: parseFloat(s.paddingLeft), padR: parseFloat(s.paddingRight),
|
||||
placeholder: txt(e.getAttribute("placeholder")).slice(0, 30),
|
||||
@@ -230,9 +232,15 @@ PROBE = r"""
|
||||
required: e.hasAttribute("required"), disabled: e.hasAttribute("disabled") || e.disabled === true,
|
||||
// 勾选/单选控件的真实点击目标是外层 label(.check / .radio-row),
|
||||
// 16px 的方块永远达不到 28px;量 label 才是量「用户能不能点到」。
|
||||
// 但「卡片式」多选 label(.strategy-pick__item:内含名称+说明+因子,
|
||||
// 高度由文案决定,可达上百 px)不是「控件高度」概念,排除出归一比较。
|
||||
hitH: (() => {
|
||||
if (e.type !== "checkbox" && e.type !== "radio") return r.h;
|
||||
const lab = e.closest("label");
|
||||
if (lab && lab.classList.contains("strategy-pick__item")) {
|
||||
// 整张策略卡都是热区:用卡片高度(远大于 28px),而非 16px 方块
|
||||
return +lab.getBoundingClientRect().height.toFixed(1);
|
||||
}
|
||||
return lab ? +lab.getBoundingClientRect().height.toFixed(1) : r.h;
|
||||
})(),
|
||||
// pill / chip 是刻意的胶囊形控件,不参与圆角统一
|
||||
@@ -332,7 +340,8 @@ def check_geometry(g: dict) -> list[tuple[bool, str, str]]:
|
||||
out.append((not mixed, "同排控件等高", detail or f"{len(rows)} 行全部等高"))
|
||||
|
||||
# 2 高度取值归一
|
||||
uniq = sorted({round(c["hitH"], 1) for c in ctrls if not c["optionRow"] and not c["isTextarea"]})
|
||||
uniq = sorted({round(c["hitH"], 1) for c in ctrls
|
||||
if not c["optionRow"] and not c["isTextarea"] and not c.get("cardPick")})
|
||||
out.append((len(uniq) <= 3, "控件高度取值归一(≤3 种)", f"实际 {len(uniq)} 种:{uniq}"))
|
||||
|
||||
# 3 点击目标
|
||||
|
||||
Reference in New Issue
Block a user