yuan 3 周之前
父節點
當前提交
17cfb3427e
共有 12 個文件被更改,包括 248 次插入 和 88 次删除
  1. 二進制
      .DS_Store
  2. 62 25
      CMS-UI完整设计.html
  3. 1 1
      src/README.md
  4. 58 19
      src/app.js
  5. 5 4
      src/book.js
  6. 29 22
      src/data.js
  7. 1 3
      src/data2.js
  8. 17 0
      src/frame.css
  9. 15 0
      src/pages.css
  10. 13 4
      src/pages.js
  11. 40 10
      src/test.mjs
  12. 7 0
      src/使用说明.txt

二進制
.DS_Store


File diff suppressed because it is too large
+ 62 - 25
CMS-UI完整设计.html


+ 1 - 1
src/README.md

@@ -13,7 +13,7 @@
 | `pages.css` | 新增页面:首页、页内页签、清算、A股自动化配置(`.acf-*`)、TRS 下单、票据详情、确认框、个人中心、404/401、登录、反馈类;短表单弹窗(`.sf-*`,信息块 2026-10-18 按前端 `DialogTiles` 回改:默认等分、数值一行放不下的块按内容加宽,数值 18px、过长折行不截断;方案稿在 `../../补录信息块编号折行·方案预览/`) |
 | `theme.css` | 令牌之外的结构差异(渐变勾选框、渐变页签线等,读 `--grad`,两套共用)+ 深色专属的几条(`[data-dark]`、`[data-v="db"]`),最后加载 |
 | `fonts/` | Inter 400/500/600(构建时内嵌,F 的数字字体) |
-| `app.js` | 画布核心:图标、令牌注入、框架 A 外壳、列表页(TRS 委托查询是母单→子单树形行)、树形行表格 `treeTableHTML`(展开状态记在画布状态 `st.tree[表格键]`,动作 `tree` / `treeall`)、弹窗 J(补录、新建投资者)、抽屉 E(订单详情)、画布挂载与交互;对外暴露 `window.UI` |
+| `app.js` | 画布核心:图标、令牌注入、框架 A 外壳、列表页(TRS 委托查询是母单→子单树形行,2026-10-16 按 trs/order 前端实现回改:展开列 60px 带子单笔数、子单行 40px 铺浅底、合并 6 列写「子单 N · 通道名」、每张母单都有子单;样式在 `pages.css` 的 `.tbl.ord`)、树形行表格 `treeTableHTML`(展开状态记在画布状态 `st.tree[表格键]`,动作 `tree` / `treeall`)、列展示(2026-10-18 从六版里选定方案 2:搜索区按钮组末尾竖线 + 齿轮图标按钮,`csBtnHTML` / `csPanelHTML`,隐藏的列记在 `st.cs[表格键]`,动作 `csvis` / `csall` / `csreset`;发行、销售端订单管理和 pages.js 的风险监控、变更记录页签接入,TRS 没接;方案稿在 `../../列展示按钮位置·方案预览/`)、弹窗 J(补录、新建投资者)、抽屉 E(订单详情)、画布挂载与交互;对外暴露 `window.UI` |
 | `pages.js` | 新增页面与弹层的渲染,登记到 `UI.pageRenderers` / `overlayRenderers` / `canvasRenderers`。A股自动化配置的状态记在画布状态 `st.ac`(交易卡片编辑、快照、下拉/级联弹层、风控/清算编辑),动作 `ac*`;校验文案、区间推导、开关变更确认与 issue_frontend 的 `use-auto-config` / `auto-config-utils` 一致;数字框用 `input` 事件只改状态和相关文字,不整张重绘 |
 | `book.js` / `book.css` / `template.html` | 设计稿外壳:目录、舞台、说明面板、规范摘要、全屏、路由。自动化配置的查看/编辑两个变体共用一份画布状态,切变体=进入/退出编辑;画面里点编辑、取消、保存时经 `UI.syncVariant` 同步变体按钮和地址 |
 

+ 58 - 19
src/app.js

@@ -22,6 +22,10 @@ window.UI = (function () {
     coin: '<circle cx="12" cy="12" r="8.5"/><path d="m9 7.5 3 3.5 3-3.5M8.5 12h7M8.5 15h7M12 11v6"/>',
     check: '<circle cx="12" cy="12" r="8.5"/><path d="m8.5 12 2.5 2.5 4.5-5"/>',
     gear: '<circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="7.6" stroke-dasharray="3.1 2.87"/>',
+    setting: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>',
+    rank: '<path d="M12 3v18M3 12h18"/><path d="m9 6 3-3 3 3M9 18l3 3 3-3M6 9l-3 3 3 3M18 9l3 3-3 3"/>',
+    top: '<path d="M12 20V4"/><path d="m6 10 6-6 6 6"/>',
+    bottom: '<path d="M12 4v16"/><path d="m6 14 6 6 6-6"/>',
     cpu: '<rect x="6" y="6" width="12" height="12" rx="2"/><rect x="9.5" y="9.5" width="5" height="5" rx=".5"/><path d="M9.5 3v3M14.5 3v3M9.5 18v3M14.5 18v3M3 9.5h3M3 14.5h3M18 9.5h3M18 14.5h3"/>',
     building: '<path d="M4.5 20V5a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v15"/><path d="M14.5 9.5h4a1 1 0 0 1 1 1V20"/><path d="M3 20h18M8 8h3M8 11.5h3M8 15h3"/>',
     clock: '<circle cx="12" cy="12" r="8.5"/><path d="M12 7.5V12l3 2"/>',
@@ -96,8 +100,8 @@ window.UI = (function () {
   /* ---------------- 三端 ---------------- */
   const listActions = [["查找", "primary", "search"], ["重置", "", "refresh"], ["数据导出", "", "download"]];
   const ENDS = {
-    issue: { short: "发行", sys: D.sysName.issue, logo: "发", home: "共赢票据管理/订单管理", extra: "共赢票据管理/票据管理", search: D.issueSearch, actions: D.issueActions, columns: D.issueColumns, rows: D.issueRows.filter((r) => r.sub === 0), sel: true, op: true, src: "issue_frontend/src/views/issue/order" },
-    sales: { short: "销售", sys: D.sysName.sales, logo: "销", home: "共赢票据管理/订单管理", extra: "投资者管理/投资者信息", search: D.salesSearch, actions: listActions, columns: D.salesColumns, rows: D.salesRows.filter((r) => r.sub === 0), sel: false, op: true, src: "sales_frontend/src/views/sales/order" },
+    issue: { short: "发行", sys: D.sysName.issue, logo: "发", home: "共赢票据管理/订单管理", extra: "共赢票据管理/票据管理", search: D.issueSearch, actions: D.issueActions, columns: D.issueColumns, rows: D.issueRows.filter((r) => r.sub === 0), sel: true, op: true, colset: true, src: "issue_frontend/src/views/issue/order" },
+    sales: { short: "销售", sys: D.sysName.sales, logo: "销", home: "共赢票据管理/订单管理", extra: "投资者管理/投资者信息", search: D.salesSearch, actions: listActions, columns: D.salesColumns, rows: D.salesRows.filter((r) => r.sub === 0), sel: false, op: true, colset: true, src: "sales_frontend/src/views/sales/order" },
     trs: { short: "TRS", sys: D.sysName.trs, logo: "T", home: "用户管理/委托查询", extra: "用户管理/成交查询", search: D.trsSearch, actions: D.trsActions, columns: D.trsColumns, rows: D.trsRows.filter((r) => !r.sub), sel: false, op: false, tree: true, src: "trs_frontend/src/views/trs/order" }, // tree:母单→子单树形行
   };
   const leafName = (key) => (key.includes("/") ? key.split("/")[1] : key);
@@ -109,7 +113,7 @@ window.UI = (function () {
     const act = !active || active === "home" ? e.home : active;
     const tags = ["首页", e.home, e.extra];
     if (!tags.includes(act)) tags.push(act);
-    return { end, col: false, open: new Set([groupName(act)].filter(Boolean)), active: act, tags, sel: new Set(), sfOpen: false, pop: null, ctx: null, msq: "", ovls: [], tab: {}, variant: null, tree: {} }; // tree:树形行表格键 -> 展开的父行键
+    return { end, col: false, open: new Set([groupName(act)].filter(Boolean)), active: act, tags, sel: new Set(), sfOpen: false, pop: null, ctx: null, msq: "", ovls: [], tab: {}, variant: null, tree: {}, cs: {} }; // tree:树形行表格键 -> 展开的父行键;cs:列展示表格键 -> 隐藏的列键
   }
 
   /* ---------------- 全局显示选项(设计稿外壳可切换) ---------------- */
@@ -141,7 +145,7 @@ window.UI = (function () {
       <span>前往</span><div class="ipt" style="width:52px;justify-content:center"><span class="num">1</span></div><span>页</span></div>`;
   }
   function ctlHTML(f) {
-    if (f.type === "daterange") return `<div class="ipt">${ic("calendar")}<span class="val num">${f.value[0]}</span><span class="sep">${f.sep}</span><span class="val num">${f.value[1]}</span></div>`;
+    if (f.type === "daterange") return `<div class="ipt">${ic("calendar")}${f.value ? `<span class="val num">${f.value[0]}</span><span class="sep">${f.sep}</span><span class="val num">${f.value[1]}</span>` : `<span class="ph">${esc(f.ph[0])}</span><span class="sep">${f.sep}</span><span class="ph">${esc(f.ph[1])}</span>`}</div>`;
     if (f.type === "select") {
       const vals = Array.isArray(f.value) ? f.value : [];
       return `<div class="ipt">${vals.length ? `<span class="val chips">${vals.map((v) => `<span class="chipv">${esc(v)}</span>`).join("")}</span>` : `<span class="ph">${esc(f.ph)}</span>`}${ic("down")}</div>`;
@@ -211,36 +215,68 @@ window.UI = (function () {
     return `<div class="tbl-wrap ${opts.cls || ""}" ${opts.keep ? `data-keep="${opts.keep}"` : ""}><table class="tbl"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table></div>`;
   }
 
+  /* ---------------- 列展示(2026-10-18 从六版里选定方案 2:搜索区按钮组末尾的齿轮图标按钮,面板从按钮下方弹出;方案稿在 ../../列展示按钮位置·方案预览/) ---------------- */
+  // st.cs[g] 是表格 g 里被隐藏的列键;items 是面板里的列 { k, label, fixed },按「列首固定 → 不固定 → 列尾固定」分段(ColumnSettings.vue 的分段规则)
+  function csSet(st, g) {
+    if (!st.cs) st.cs = {};
+    if (!st.cs[g]) st.cs[g] = new Set();
+    return st.cs[g];
+  }
+  // 面板:顶部全选(半选表示有列被勾掉)和重置;每行拖动手柄、勾选、列名、固定列首/列尾两个图标(拖动与固定在样板里只画不动)
+  function csPanelHTML(st, g, items) {
+    const hid = csSet(st, g);
+    const shown = items.filter((it) => !hid.has(it.k)).length;
+    const groups = ["left", "none", "right"].map((f) => items.filter((it) => (it.fixed || "none") === f)).filter((x) => x.length);
+    const row = (it) => `<div class="cs-i"><span class="cs-drag" title="拖动排序">${ic("rank")}</span><span class="ck ${hid.has(it.k) ? "" : "on"}" data-act="csvis" data-g="${g}" data-k="${esc(it.k)}">${CHECK}</span><span class="cs-nm" title="${esc(it.label)}">${esc(it.label)}</span><span class="cs-pins"><span class="cs-pin ${it.fixed === "left" ? "on" : ""}" title="固定在列首">${ic("top")}</span><span class="cs-pin ${it.fixed === "right" ? "on" : ""}" title="固定在列尾">${ic("bottom")}</span></span></div>`;
+    const head = `<span class="chk-l" data-act="csall" data-g="${g}" data-all="${esc(items.map((it) => it.k).join("|"))}"><span class="ck ${shown === items.length ? "on" : shown ? "half" : ""}">${shown === items.length ? CHECK : ""}</span>列展示</span><span class="btn txt" data-act="csreset" data-g="${g}">重置</span>`;
+    return `<div class="fl below-r"><div class="pop cs-pop"><div class="cs-hd">${head}</div><div class="cs-bd">${groups.map((x) => `<div class="cs-g">${x.map(row).join("")}</div>`).join("")}</div></div></div>`;
+  }
+  // 图标按钮(设计稿 .btn.icon,32px 见方):搜索区竖线右边对结果表的操作(数据导出等)只画图标,悬停提示按钮名(10-19)
+  const ibtnHTML = ([t, , icn]) => `<span class="btn icon" data-act="noop" title="${esc(t)}">${ic(icn)}</span>`;
+  // 按钮:32px 齿轮图标,悬停提示「列展示」;打开时按钮保持悬停态
+  const csBtnHTML = (st, g, items) => `<div class="anchor"><span class="btn icon ${st.pop === "cs:" + g ? "is-hover" : ""}" data-act="pop" data-pop="cs:${g}" title="列展示">${ic("setting")}</span>${st.pop === "cs:" + g ? csPanelHTML(st, g, items) : ""}</div>`;
+
   /* ---------------- 列表页(搜索区 + 表格) ---------------- */
   function listHTML(st) {
     const e = ENDS[st.end];
     const lw = Math.max(...e.search.map((f) => f.label.length)) * 14;
+    // SearchForm 规则:按钮组占最后一行剩余的格子、靠右,放不下就整体换行;收起态从最后一个条件起逐个隐藏,直到按钮组落回第 2 行。
+    // 1440 宽下一列约 366px:竖线右边的数据导出、列展示都是 32px 图标按钮(10-19 数据导出改图标),带齿轮的按钮组约 330px,一列放得下,收起态露 5 个条件
     const collapsible = e.search.length > 5;
     const fields = st.sfOpen || !collapsible ? e.search : e.search.slice(0, 5);
     const rem = 3 - (fields.length % 3);
-    const acts = `<div class="sf-act" style="grid-column:span ${rem}">${e.actions.map(([t, type, icn]) => `<span class="btn ${type === "primary" ? "pri" : ""}" data-act="noop">${ic(icn)}${t}</span>`).join("")}${collapsible ? `<span class="btn txt ${st.sfOpen ? "up" : ""}" data-act="sf">${st.sfOpen ? "收起" : "展开"}${ic("down")}</span>` : ""}</div>`;
+    const abtn = ([t, type, icn]) => `<span class="btn ${type === "primary" ? "pri" : ""}" data-act="noop">${ic(icn)}${t}</span>`;
+    // 列展示(2026-10-18 方案 2):「查找/重置」后一条竖线,再「数据导出」和齿轮,竖线右边一律图标按钮(10-19),「展开」仍在最末;面板里的列 = 操作列 + 数据列,固定在左的归「列首固定」段
+    const csKey = "list-" + st.end, hid = csSet(st, csKey);
+    const csItems = e.colset ? [...(e.op ? [{ k: "__op", label: "操作", fixed: "left" }] : []), ...e.columns.map((c) => ({ k: c.key, label: c.title, fixed: c.fx ? "left" : undefined }))] : [];
+    const abtns = e.colset ? [...e.actions.slice(0, 2).map(abtn), '<span class="sep"></span>', ...e.actions.slice(2).map(ibtnHTML), csBtnHTML(st, csKey, csItems)] : e.actions.map(abtn);
+    const acts = `<div class="sf-act" style="grid-column:span ${rem}">${abtns.join("")}${collapsible ? `<span class="btn txt ${st.sfOpen ? "up" : ""}" data-act="sf">${st.sfOpen ? "收起" : "展开"}${ic("down")}</span>` : ""}</div>`;
     const search = `<div class="card"><div class="sf">${fields.map((f) => `<div class="sf-i"><label style="width:${lw}px">${esc(f.label)}</label>${ctlHTML(f)}</div>`).join("")}${acts}</div></div>`;
 
     const cols = [];
-    if (e.tree) cols.push({ tg: true, w: 40 });
+    if (e.tree) cols.push({ tg: true, w: 60 }); // 展开列 60px:细箭头 + 子单笔数(trs/order 的 OrderTreeToggle)
     if (e.sel) cols.push({ sel: true, w: 48, fx: true });
-    if (e.op) cols.push({ op: true, title: "操作", w: 72, fx: true });
-    e.columns.forEach((c) => cols.push(c));
+    if (e.op && !hid.has("__op")) cols.push({ op: true, title: "操作", w: 72, fx: true });
+    e.columns.forEach((c) => { if (!hid.has(c.key)) cols.push(c); }); // 列展示里勾掉的列不画
     let left = 0;
     const fx = cols.filter((c) => c.fx);
     cols.forEach((c) => { if (c.fx) { c._left = left; left += c.w; } });
     const lastFx = fx[fx.length - 1];
     const allOn = e.rows.length && e.rows.every((r) => st.sel.has(r.no));
     const someOn = e.rows.some((r) => st.sel.has(r.no));
-    const thCls = (c) => [c.fx ? "fx" : "", c === lastFx ? "fx-end" : "", c.num || c.align === "right" ? "r" : ""].join(" ");
+    const thCls = (c) => [c.fx ? "fx" : "", c === lastFx ? "fx-end" : "", c.num || c.align === "right" ? "r" : c.align === "center" ? "c" : ""].join(" ");
     const pos = (c) => (c.fx ? `left:${c._left}px;` : "");
-    // 树形行(TRS 委托查询:母单→卖出跨通道拆单后的子单,trs/order/index.vue 的 row-key + tree-props + span-method)
-    const kidsOf = (r) => (e.tree ? D.trsRows.filter((k) => k.sub && k.no.startsWith(r.no + "-")) : []);
+    // 树形行(TRS 委托查询,2026-10-16 按前端实现回改:trs/order/index.vue + order-tree.ts 把母单与子单拍平成一维,不用 EP 树形模式;
+    // 每张母单都有子单——单通道单由后端用主单补一条子行承载通道,卖出跨通道拆单按库内子单)
+    const kidsOf = (r) => (e.tree ? D.trsRows.filter((k) => k.sub && k.parent === r.no) : []);
     const kidKeys = e.rows.filter((r) => kidsOf(r).length).map((r) => r.no);
-    const tree = e.tree ? treeSet(st, "list", kidKeys.slice(0, 1)) : null; // 代码现状默认折叠;样板默认展开第一单好看清样子
+    const firstSplit = e.tree ? e.rows.find((r) => kidsOf(r).length > 1) : null;
+    const tree = e.tree ? treeSet(st, "list", [...new Set([kidKeys[0], firstSplit && firstSplit.no].filter(Boolean))]) : null; // 代码默认全部折叠;样板默认展开第一单(单通道 1 条子单)和第一张拆单(2 条)好看清样子
     const allOpen = kidKeys.length > 0 && kidKeys.every((k) => tree.has(k));
-    const MERGE = 7; // 子单行把「用户ID~股票名称」7 列合并成一格,写通道/交易渠道
-    const PARENT_ONLY = ["sccy", "operator", "otype", "omode", "ip", "lbs", "vpn"]; // 母单级字段,子单行留空(按代码)
+    const MERGE = 6; // 子单行把「用户ID~股票名称」6 列合并成一格,写「子单 N · 通道名」;交易渠道起按列落位(order-tree.ts 的 CHILD_MERGE_SPAN)
+    const PARENT_ONLY = ["ccy", "sccy", "operator", "otype", "omode", "ip", "lbs", "vpn", "remark"]; // 母单级字段,子单行留空(按代码)
+    // 委托状态格(OrderStatusCell):有撤单结果时在状态标签下叠小标签和一行 12px 原因,该行随内容加高
+    const cell = (c, r) => (c.key === "status" && r.cancel ? `<div class="st-stack">${cellHTML(c, r)}<span class="tg tg-${r.cancel[1]} sm">${esc(r.cancel[0])}</span><span class="st-why">${esc(r.cancel[2])}</span></div>` : cellHTML(c, r));
     const head = cols.map((c) => {
       const style = `${pos(c)}min-width:${c.w}px;width:${c.w}px`;
       if (c.tg) return `<th class="w-tg">${kidKeys.length ? tglHTML(allOpen, "list", undefined, "all", kidKeys) : ""}</th>`;
@@ -252,16 +288,16 @@ window.UI = (function () {
       const kids = kidsOf(r), has = kids.length > 0, open = has && tree.has(r.no);
       let h = `<tr class="${on ? "is-sel" : ""} ${has ? "p has" : ""}" ${has ? `data-act="tree" data-g="list" data-k="${esc(r.no)}"` : ""}>${cols.map((c) => {
         const cls = thCls(c) + (c.key === "org" || c.key === "remark" || c.key === "stock" ? " ellip" : "");
-        if (c.tg) return `<td class="w-tg">${has ? tglHTML(open, "list", r.no) : ""}</td>`;
+        if (c.tg) return `<td class="w-tg">${has ? tglHTML(open, "list", r.no) + `<i class="cnt num">${kids.length}</i>` : ""}</td>`;
         if (c.sel) return `<td class="${cls}" style="${pos(c)}"><span class="ck ${on ? "on" : ""}" data-act="rowsel" data-no="${esc(r.no)}">${CHECK}</span></td>`;
         if (c.op) return `<td class="${cls}" style="${pos(c)}"><span class="btn txt" data-act="${st.end === "issue" ? "ovopen" : "toast"}" data-o="order" data-msg="销售端订单详情用同一个抽屉,只是没有「对冲执行」卡片">详情</span></td>`;
-        return `<td class="${cls}" style="${pos(c)}">${cellHTML(c, r)}</td>`;
+        return `<td class="${cls}" style="${pos(c)}">${cell(c, r)}</td>`;
       }).join("")}</tr>`;
-      if (open) h += kids.map((k, j) => `<tr class="s ${j === kids.length - 1 ? "last" : ""}"><td class="w-tg"></td><td colspan="${MERGE}"><span class="ind"><span class="lbl">通道</span> <span class="num">${esc(k.route)}</span><span class="t4"> · </span><span class="lbl">交易渠道</span> ${tag("trade_channel", k.channel)}</span></td>${e.columns.slice(MERGE).map((c) => `<td class="${c.num ? "r" : ""}">${PARENT_ONLY.includes(c.key) ? "" : cellHTML(c, k)}</td>`).join("")}</tr>`).join("");
+      if (open) h += kids.map((k, j) => `<tr class="s ${j === kids.length - 1 ? "last" : ""}"><td class="w-tg"></td><td colspan="${MERGE}"><span class="sub-lbl"><span class="lbl">子单 ${k.sub}</span><i class="dot"></i><span class="num">${esc(k.route)}</span></span></td>${e.columns.slice(MERGE).map((c) => `<td class="${thCls(c)}">${PARENT_ONLY.includes(c.key) ? "" : cell(c, k)}</td>`).join("")}</tr>`).join("");
       return h;
     }).join("");
     const selbar = st.sel.size ? `<div class="selbar">已选择 <b>${st.sel.size}</b> 项<span class="btn txt" data-act="clearsel">清空</span></div>` : "";
-    return `<div class="pg-list">${search}<div class="card tb-card">${selbar}<div class="tbl-wrap" data-keep="tbl"><table class="tbl"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table></div>${pagerHTML(e.rows.length)}</div></div>`;
+    return `<div class="pg-list">${search}<div class="card tb-card">${selbar}<div class="tbl-wrap" data-keep="tbl"><table class="tbl ${e.tree ? "ord" : ""}"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table></div>${pagerHTML(e.rows.length)}</div></div>`;
   }
   function skelHTML(text) {
     const bars = (n) => Array.from({ length: n }, (_, i) => `<div class="bar" style="width:${[92, 86, 95, 78, 90, 84, 97, 81, 88, 93][i % 10]}%"></div>`).join("");
@@ -632,6 +668,9 @@ window.UI = (function () {
         case "tree": { const s = treeSet(st, el.dataset.g); const k = el.dataset.k; s.has(k) ? s.delete(k) : s.add(k); break; }
         case "treeall": { const all = (el.dataset.all || "").split("|").filter(Boolean); const s = treeSet(st, el.dataset.g); st.tree[el.dataset.g] = new Set(all.every((k) => s.has(k)) ? [] : all); break; }
         case "ptab": st.tab[el.dataset.g] = el.dataset.v; break;
+        case "csvis": { const s = csSet(st, el.dataset.g); const k = el.dataset.k; s.has(k) ? s.delete(k) : s.add(k); break; }
+        case "csall": { const s = csSet(st, el.dataset.g); st.cs[el.dataset.g] = new Set(s.size ? [] : (el.dataset.all || "").split("|").filter(Boolean)); break; } // 有列被勾掉 → 全部显示;否则全部隐藏
+        case "csreset": st.cs[el.dataset.g] = new Set(); break;
         case "ctx": {
           const k = st.ctx && st.ctx.k;
           const d = el.dataset.do;
@@ -681,7 +720,7 @@ window.UI = (function () {
 
   injectTokens();
   return { D, D2, T, $, $$, esc, ic, CHECK, EMPTY, dict, tag, ENDS, leafName, groupName, newCanvasState, OV, OPT, resetOV,
-    pagerHTML, ctlHTML, cellHTML, cellAuto, tableHTML, treeTableHTML, treeSet, tglHTML, btn, listHTML, skelHTML, pageRenderers, overlayRenderers, canvasRenderers, extraActions,
+    pagerHTML, ctlHTML, cellHTML, cellAuto, tableHTML, treeTableHTML, treeSet, tglHTML, csSet, csBtnHTML, ibtnHTML, csPanelHTML, btn, listHTML, skelHTML, pageRenderers, overlayRenderers, canvasRenderers, extraActions,
     kv, kvVal, item, ctl, lblW, numUnit, dgWrap, dgCard, sfDialog, stepper, closeX, btnCancel, drCard, tiles, errHTML,
     renderCanvas, fitCanvas, mount, navigate, toast, shellHTML, setTheme, toggleTheme, THEME_PICK };
 })();

+ 5 - 4
src/book.js

@@ -70,7 +70,8 @@
       ["抽屉", "E", "卡片+描述表", "宽 1040px;头部是编号、状态标签和一行关键信息;灰底上排卡片,键值用带边框的描述表(灰底标签格,一行 3 对);汇总做成数字卡片。", "drawer"],
       ["登录页", "C", "右侧浮卡·全幅底图", "整页蓝白渐变底色叠淡线条装饰;表单卡片 440px 靠右浮着,输入框按代码现状 40px;左侧只放系统名和版本号(10-08 去掉了本系统业务模块和三端入口)。(10-07 追加的分项选型类别,选 C。)", "login"],
       ["深色皮肤", "B", "石墨灰", "中性暗灰不偏蓝;扁平,卡片靠 1px 描边分区,没有渐变、光晕和投影;主按钮实心蓝;状态标签只描边不铺底。尺寸、圆角、字体、排布和浅色完全相同,只换令牌取值。(10-08 三版深色候选里选 B;A 夜空蓝、C 极光玻璃未选。)", "kit"],
-      ["树形行", "3", "树线不铺底", "父子行在同一张表里(母单/子单、汇总/明细、证券/通道):最左 40px 展开列放细箭头,表头同位置是全部展开/折叠;子行 36px、字号同父行、次级字色、不铺底色,靠「└」树线和首格缩进区分,属于父行的字段留空。(10-10 从 10 个版本里选定。)", "tree"],
+      ["树形行", "3", "树线不铺底", "父子行在同一张表里(母单/子单、汇总/明细、证券/通道):最左 40px 展开列放细箭头,表头同位置是全部展开/折叠;子行 36px、字号同父行、次级字色、不铺底色,靠「└」树线和首格缩进区分,属于父行的字段留空。(10-10 从 10 个版本里选定;10-16 委托查询按前端实现回改:展开列 60px 带子单笔数、子单行 40px 铺浅底,另两页仍按版本 3。)", "tree"],
+      ["列展示按钮位置", "2", "搜索区·图标按钮", "「列展示」(列的显隐、顺序、固定)不再独占表格卡片顶部的工具条:放在搜索区按钮组末尾——「查找/重置」后一条竖线,再「数据导出」和「列展示」两个 32px 图标按钮(下载、齿轮,悬停提示按钮名),「展开」仍在最末;面板从齿轮下方弹出,勾掉的列当场消失。前端接了列展示的是订单管理(发行、销售)、票据管理、风险监控、风险状态变更记录;TRS 委托查询没接,不画。(10-18 从六版里选定,方案稿在 列展示按钮位置·方案预览;10-19 数据导出由文字按钮改为图标,带齿轮的按钮组一列放得下,方案稿在 搜索区按钮组同行·方案预览。)", "list"],
       ["自动化配置", "2", "分区网格", "A股自动化配置的交易卡片:报价一个面板、报价表撑满,4 类订单自动化 2×2 子面板,买入/卖出、强平/到期并排;卡片头铺满、按钮在右;查看态开关写成状态标签、编辑态才出现开关;人工行两列合并、分级之间加分组线;表格整表居中(沿用原型),区间格分三栏让「~」上下对齐。(10-14 从三版里选定;同日前端实现后,本稿按实现回改。)", "autocfg"],
     ];
     const dims = [
@@ -79,15 +80,15 @@
       ["圆角", "控件 8px、卡片 14px、标签 6px、弹窗 16px"], ["投影", "卡片和主按钮用淡蓝投影;弹层 0 10px 28px;弹窗与抽屉 0 24px 64px"],
       ["侧栏与顶栏", "侧栏 220px 浅色(深色皮肤下近黑),收起后 64px;顶栏 52px;标签页栏 38px;内容区四周留 16px,卡片间距 12px"], ["页签", "框架标签页 28px 卡片式,当前页带主色圆点;页面级页签 40px 无底色;卡片内页签 48px 在卡片标题栏里;抽屉内页签 42px"],
       ["弹窗宽度", "短表单 640px、长表单 920px、确认框 420px、修改密码 480px、TRS 下单 880px"], ["抽屉宽度", "1040px(订单详情、票据详情)"],
-      ["树形行(父子同表)", "展开列 40px 在最左,细箭头展开时转 90°,没有子行的父行不显示图标,点图标或整行都能展开;表头同位置是全部展开/折叠;子行 36px、字号同父行 14px、次级字色、不铺底;树线用主色描边色,首格缩进 16px;属于父行的字段在子行留空"],
-      ["搜索区", "一张卡片,3 列等宽,标签在左、按本页最长标签统一宽度;超过 2 行折叠"], ["描述表", "标签格 116px 灰底,一行 3 对;标签长的分组一行 2 对(标签格 190px);单列 132px"],
+      ["树形行(父子同表)", "展开列 40px 在最左(委托查询 60px,箭头右边 12px 子单笔数),细箭头展开时转 90°,没有子行的父行不显示图标,点图标或整行都能展开;表头同位置是全部展开/折叠;子行 36px、字号同父行 14px、次级字色、不铺底(委托查询按前端实现:40px、铺填充 2 浅底);树线用主色描边色,首格缩进 16px;属于父行的字段在子行留空"],
+      ["搜索区", "一张卡片,3 列等宽,标签在左、按本页最长标签统一宽度;超过 2 行折叠;按钮组:查找、重置 | 数据导出、列展示(竖线右边都是 32px 图标按钮:下载、齿轮)、展开"], ["描述表", "标签格 116px 灰底,一行 3 对;标签长的分组一行 2 对(标签格 190px);单列 132px"],
       ["配置卡片(开关 + 区间表)", "卡片头标题在左、按钮在右,编辑态标题旁「编辑中」、卡片一圈主色描边;查看态开关写成「已开启/已关闭」标签,编辑态才出现开关;区间左闭右开,写成「下限 ~ 上限」,分三栏:下限靠右、「~」居中、上限靠左,末档「及以上」;表格整表居中(沿用原型),编辑态带控件的行 49px;卡片内子面板 12px 圆角、44px 浅底头部,两列间距 12px(见「A股自动化配置」)"],
       ["不放的东西", "页面标题(面包屑和标签页已有页面名)、界面尺寸切换;首页现状版按现状保留了天气那句(待确认),工作台版不放"],
     ];
     const swatches = T.palette.map(([g, items]) => `<div class="sp-grp"><h4>${g}</h4><div class="sp-sw">${items.map(([l, k]) => `<div class="sp-swc"><i style="background:${T.tokens[k]}"></i><b>${l}</b><span>${T.tokens[k]}</span></div>`).join("")}</div></div>`).join("");
     const map = [
       ["基础视觉", "F 晴空渐变", ["kit"]], ["整体框架", "A 经典三段", ["home", "list"]], ["登录页", "C 右侧浮卡·全幅底图;左侧只有系统名和版本号", ["login"]],
-      ["搜索区", "3 列标签在左,可折叠;无标签时只放占位", ["list", "tabs"]], ["表格", "固定列、选择条、分页、空状态、加载态", ["list", "clearing", "drawer-note", "feedback"]], ["树形行", "父子同表:委托查询母单/子单、成交统计汇总/明细、持仓证券/通道(版本 3 树线不铺底)", ["list", "tree"]],
+      ["搜索区", "3 列标签在左,可折叠;无标签时只放占位;按钮组末尾竖线后是数据导出和列展示,都是图标按钮(10-18 定,10-19 数据导出改图标)", ["list", "tabs"]], ["表格", "固定列、选择条、分页、空状态、加载态、列展示面板", ["list", "clearing", "drawer-note", "feedback"]], ["树形行", "父子同表:委托查询母单/子单(10-16 按前端实现回改)、成交统计汇总/明细、持仓证券/通道(版本 3 树线不铺底)", ["list", "tree"]],
       ["弹窗", "长表单 J 卡片分区;短表单标签在左+信息块+结果块(10-08 布局);TRS 下单同一布局加行情栏", ["dlg-fill", "dlg-investor", "dlg-trs", "profile"]], ["抽屉", "E 卡片+描述表", ["drawer", "drawer-note"]],
       ["详情展示", "带边框描述表 + 数字卡片", ["drawer", "drawer-note", "profile", "dlg-trs"]], ["页内标签页", "页面级/卡片内/抽屉内三种位置", ["tabs", "clearing", "drawer-note", "dlg-trs"]],
       ["反馈类", "确认框、提示、通知、空状态、加载、结果、校验", ["confirm", "feedback"]], ["首页", "现状:一张问候卡(头像、问候语、天气一句);工作台版(状态数字、待处理表、快捷入口)整套保留在变体里,等首页接口完善后切回", ["home"]], ["其他页", "个人中心、404 / 401;配置类页面以 A股自动化配置为样板(10-14 选定版本 2 分区网格)", ["profile", "err", "autocfg"]],

+ 29 - 22
src/data.js

@@ -293,7 +293,7 @@ window.SPEC_DATA = (function () {
     { label: "用户ID", type: "input", ph: "精确搜索" }, { label: "手机号", type: "input", ph: "精确搜索" }, { label: "委托编号", type: "input", ph: "精确搜索" },
     { label: "账户", type: "input", ph: "模糊搜索" }, { label: "用户名称", type: "input", ph: "模糊搜索" }, { label: "股票代码/名称", type: "input", ph: "模糊搜索" },
     { label: "委托状态", type: "select", ph: "全部", multiple: true }, { label: "下单类型", type: "select", ph: "全部" }, { label: "下单方式", type: "select", ph: "全部" },
-    { label: "委托日期", type: "daterange", value: ["2026-09-25", "2026-09-25"], sep: "至" },
+    { label: "委托日期", type: "daterange", ph: ["起始", "结束"], sep: "至" }, // 前端没有默认日期,显示占位
   ];
   const trsActions = [["查找", "primary", "search"], ["重置", "", "refresh"], ["导出", "", "download"]];
 
@@ -350,29 +350,32 @@ window.SPEC_DATA = (function () {
   };
 
 
-  // 委托查询表格列(trs/order/index.vue)。align 为代码现状;group 为激进套的分组
+  // 委托查询表格列(trs/order/index.vue,2026-10-16 按前端现状回改):列序按原型(用户 4 列 → 股票代码 → 股票名称 → 交易渠道 → …),
+  // w 取列上的建议宽度;align 为前端现状(文字列居中、数字列右对齐、备注左对齐,原型口径);group 为激进套的分组
+  const ctr = "center";
   const trsColumns = [
-    { key: "uid", title: "用户ID", w: 90, group: "用户" }, { key: "acct", title: "账户", w: 120, group: "用户" },
-    { key: "uname", title: "用户名称", w: 110, group: "用户" }, { key: "phone", title: "手机号", w: 120, group: "用户" },
-    { key: "channel", title: "交易渠道", w: 100, tag: "trade_channel", group: "委托" },
-    { key: "code", title: "股票代码", w: 100, group: "委托" }, { key: "sname", title: "股票名称", w: 110, group: "委托" },
-    { key: "dir", title: "委托方向", w: 90, tag: "direction", group: "委托" }, { key: "ptype", title: "报价方式", w: 90, tag: "price_req_type", group: "委托" },
-    { key: "status", title: "委托状态", w: 180, tag: "trs_order_status", group: "委托" },
+    { key: "uid", title: "用户ID", w: 90, align: ctr, group: "用户" }, { key: "acct", title: "账户", w: 120, align: ctr, group: "用户" },
+    { key: "uname", title: "用户名称", w: 110, align: ctr, group: "用户" }, { key: "phone", title: "手机号", w: 130, align: ctr, group: "用户" },
+    { key: "code", title: "股票代码", w: 100, align: ctr, group: "委托" }, { key: "sname", title: "股票名称", w: 110, align: ctr, group: "委托" },
+    { key: "channel", title: "交易渠道", w: 100, align: ctr, tag: "trade_channel", group: "委托" },
+    { key: "dir", title: "委托方向", w: 90, align: ctr, tag: "direction", group: "委托" }, { key: "ptype", title: "报价方式", w: 90, align: ctr, tag: "price_req_type", group: "委托" },
+    { key: "status", title: "委托状态", w: 180, align: ctr, tag: "trs_order_status", group: "委托" },
     { key: "qty", title: "委托数量", w: 110, num: true, group: "委托" }, { key: "price", title: "委托价格", w: 100, num: true, group: "委托" },
-    { key: "otime", title: "委托时间", w: 160, group: "委托" },
+    { key: "otime", title: "委托时间", w: 185, align: ctr, group: "委托" },
     { key: "fqty", title: "成交数量", w: 110, num: true, group: "成交" }, { key: "fprice", title: "成交价格", w: 100, num: true, group: "成交" },
-    { key: "ccy", title: "交易币种", w: 90, group: "成交" }, { key: "famt", title: "成交金额", w: 130, num: true, group: "成交" },
-    { key: "ftime", title: "成交时间", w: 160, group: "成交" },
+    { key: "ccy", title: "交易币种", w: 90, align: ctr, group: "成交" }, { key: "famt", title: "成交金额", w: 140, num: true, group: "成交" },
+    { key: "ftime", title: "成交时间", w: 185, align: ctr, group: "成交" },
     { key: "comm", title: "佣金", w: 100, num: true, group: "费用" }, { key: "stamp", title: "印花税", w: 100, num: true, group: "费用" },
     { key: "fee", title: "规费", w: 100, num: true, group: "费用" }, { key: "total", title: "交易总费用", w: 110, num: true, group: "费用" },
-    { key: "amt", title: "发生金额(交易币种)", w: 170, num: true, group: "结算" }, { key: "sccy", title: "结算币种", w: 90, group: "结算" },
+    { key: "amt", title: "发生金额(交易币种)", w: 170, num: true, group: "结算" }, { key: "sccy", title: "结算币种", w: 90, align: ctr, group: "结算" },
     { key: "samt", title: "发生金额(结算币种)", w: 170, num: true, group: "结算" },
-    { key: "no", title: "委托编号", w: 200, group: "来源" }, { key: "operator", title: "操作方", w: 120, group: "来源" },
-    { key: "otype", title: "下单类型", w: 100, tag: "order_type", group: "来源" }, { key: "omode", title: "下单方式", w: 100, tag: "order_mode", group: "来源" },
-    { key: "ip", title: "IP", w: 130, group: "来源" }, { key: "lbs", title: "LBS", w: 130, group: "来源" },
-    { key: "vpn", title: "是否用VPN", w: 100, group: "来源" }, { key: "remark", title: "备注", w: 140, group: "来源" },
+    { key: "no", title: "委托编号", w: 200, align: ctr, group: "来源" }, { key: "operator", title: "操作方", w: 120, align: ctr, group: "来源" },
+    { key: "otype", title: "下单类型", w: 100, align: ctr, tag: "order_type", group: "来源" }, { key: "omode", title: "下单方式", w: 100, align: ctr, tag: "order_mode", group: "来源" },
+    { key: "ip", title: "IP", w: 150, align: ctr, group: "来源" }, { key: "lbs", title: "LBS", w: 130, align: ctr, group: "来源" },
+    { key: "vpn", title: "是否用VPN", w: 100, align: ctr, group: "来源" }, { key: "remark", title: "备注", w: 140, group: "来源" },
   ];
   // [用户, 渠道, 股票, 方向, 报价, 状态, 数量, 价格, 成交数量, 成交价, 下单类型, 下单方式, 子单数]
+  // 子单数 0 = 单通道单(2026-10-16 起后端用主单补一条通道子行,子单编号同主单);2 = 卖出跨通道拆单(子单编号带 -1/-2)
   const trsPlan = [
     [0, "QFII", 0, "买入", "限价", "已报", 2000, 11.62, 0, 0, "正常下单", "iOS", 0],
     [1, "QFII", 1, "卖出", "限价", "已成", 2000, 245.2, 2000, 245.18, "后台下单", "CRM", 2],
@@ -387,24 +390,28 @@ window.SPEC_DATA = (function () {
   ];
   const trsUsers = [["10021", "ceshi1", "李四测"], ["10034", "zhangmy", "张明远"], ["10058", "wangzh", "王子豪"], ["10077", "chensy", "陈思雨"], ["10092", "liujg", "刘建国"], ["10105", "zhaoyl", "赵雅琳"]];
   const trsStocks = [["000007", "全新好"], ["300750", "宁德时代"], ["600519", "贵州茅台"], ["601318", "中国平安"], ["600036", "招商银行"], ["601899", "紫金矿业"], ["688981", "中芯国际"], ["000333", "美的集团"], ["601012", "隆基绿能"], ["002594", "比亚迪"]];
+  const trsRoutes = ["VIL001", "CMSI-QFII", "HTSC-01"]; // 通道账户名,子单行写「子单 N · 通道名」
   const trsRows = [];
   trsPlan.forEach((p, i) => {
     const u = trsUsers[p[0]], s = trsStocks[p[2]], t = ["10:42:16", "10:15:03", "09:48:51", "09:41:27", "09:38:02", "09:35:44", "09:33:10", "09:31:58", "09:31:05", "09:30:12"][i];
+    const split = p[12] > 1, n = split ? p[12] : 1; // 子单笔数:拆单按计划,单通道单也有 1 条
+    const no = "T20260925" + String(431 - i * 9).padStart(5, "0");
     const famt = p[8] * p[9], comm = famt * 0.0003, stamp = p[3] === "卖出" ? famt * 0.0005 : 0, fee = famt * 0.0000687, total = comm + stamp + fee;
     const amt = famt ? (p[3] === "买入" ? -(famt + total) : famt - total) : 0;
+    // 母单级字段(交易币种、结算币种、操作方、下单类型、下单方式、IP、LBS、VPN、备注)子单留空,和前端一致
     const mk = (sub, qty, fq) => ({
-      sub, kids: sub ? 0 : p[12], uid: u[0], acct: u[1], uname: u[2], phone: "1380000" + String(1001 + p[0]), channel: sub === 2 ? (p[1] === "QFII" ? "深沪港通" : "QFII") : p[1],
+      sub, kids: sub ? 0 : n, parent: sub ? no : "", uid: u[0], acct: u[1], uname: u[2], phone: "1380000" + String(1001 + p[0]), channel: sub === 2 ? (p[1] === "QFII" ? "深沪港通" : "QFII") : p[1],
       code: s[0], sname: s[1], dir: p[3], ptype: p[4], status: p[5], qty: fmt(qty, 0), price: fmt(p[7]), otime: "2026-09-25 " + t,
-      fqty: fmt(fq, 0), fprice: fq ? fmt(p[9]) : "--", ccy: "CNY", famt: fq ? fmt(fq * p[9]) : "--", ftime: fq ? "2026-09-25 " + t.slice(0, 6) + "59" : "--",
+      fqty: fmt(fq, 0), fprice: fq ? fmt(p[9]) : "--", ccy: sub ? "" : "CNY", famt: fq ? fmt(fq * p[9]) : "--", ftime: fq ? "2026-09-25 " + t.slice(0, 6) + "59" : "--",
       comm: fq ? fmt(comm * fq / (p[8] || 1)) : "--", stamp: fq ? fmt(stamp * fq / (p[8] || 1)) : "--", fee: fq ? fmt(fee * fq / (p[8] || 1)) : "--", total: fq ? fmt(total * fq / (p[8] || 1)) : "--",
       amt: fq ? fmt(amt * fq / (p[8] || 1)) : "--", sccy: sub ? "" : "HKD", samt: fq ? fmt(amt * fq / (p[8] || 1) * 1.0944) : "--",
-      no: "T20260925" + String(431 - i * 9).padStart(5, "0") + (sub ? "-" + sub : ""), operator: sub ? "" : p[10] === "正常下单" ? "--" : "admin",
+      no: split && sub ? no + "-" + sub : no, operator: sub ? "" : p[10] === "正常下单" ? "--" : "admin",
       otype: sub ? "" : p[10], omode: sub ? "" : p[11], ip: "203.0.113." + (20 + i), lbs: ["上海", "深圳", "香港", "北京"][i % 4], vpn: i === 3 ? "是" : "否",
-      remark: p[5] === "废单" ? "超出持仓可卖数量" : "", route: sub ? ["VIL001", "CMSI-QFII", "HTSC-01"][sub - 1] : "", // 子单(卖出跨通道拆单)的通道账户名
-      cancel: p[5] === "待撤" ? ["撤单结果未知", "danger", "上轮撤单未取得明确结果,可人工再次撤单"] : null,
+      remark: !sub && p[5] === "废单" ? "超出持仓可卖数量" : "", route: sub ? (split ? trsRoutes[sub - 1] : trsRoutes[i % 3]) : "", // 子单的通道账户名
+      cancel: p[5] === "待撤" ? ["撤单结果未知", "danger", "上轮撤单未取得明确结果,可人工再次撤单"] : null, // 委托状态格下叠的撤单结果(OrderStatusCell)
     });
     trsRows.push(mk(0, p[6], p[8]));
-    for (let k = 1; k <= p[12]; k++) trsRows.push(mk(k, p[6] / p[12], p[8] / p[12]));
+    for (let k = 1; k <= n; k++) trsRows.push(mk(k, p[6] / n, p[8] / n));
   });
 
   /* ---------- 销售端 · 订单管理(sales/order/components/OrderSearchForm.vue、OrderTable.vue,management 模式) ---------- */

File diff suppressed because it is too large
+ 1 - 3
src/data2.js


+ 17 - 0
src/frame.css

@@ -110,6 +110,23 @@
 .sf-act .btn.txt { margin-left: 4px; }
 .sf-act .btn.txt .ic { transition: transform .2s; }
 .sf-act .btn.txt.up .ic { transform: rotate(180deg); }
+/* 列展示(2026-10-18 方案 2):按钮组里「查找/重置」后一条竖线,再「数据导出」和齿轮图标按钮;面板从齿轮下方弹出 */
+.sf-act .sep { width: 1px; height: 20px; margin: 0 4px; background: var(--bd-ctl); }
+.sf-act .btn.icon .ic { width: 16px; height: 16px; }
+.cv .card:has(.sf-act .fl) { position: relative; z-index: 41; }
+.cv .pop.cs-pop { width: 336px; padding: 12px; }
+.cs-hd { display: flex; align-items: center; justify-content: space-between; padding: 0 4px 8px; border-bottom: 1px solid var(--bd); }
+.cs-bd { max-height: 480px; padding-top: 4px; overflow-y: auto; }
+.cs-g + .cs-g { margin-top: 4px; padding-top: 4px; border-top: 1px dashed var(--bd); }
+.cs-i { display: grid; grid-template-columns: 16px 16px minmax(0, 1fr) auto; gap: 8px; align-items: center; min-height: 36px; padding: 0 4px; border-radius: var(--r-ctl); }
+.cs-i:hover { background: var(--fill-1); }
+.cs-drag { display: inline-flex; color: var(--t4); cursor: grab; }
+.cs-drag .ic, .cs-pin .ic { width: 14px; height: 14px; }
+.cs-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.cs-pins { display: flex; gap: 4px; }
+.cs-pin { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 4px; color: var(--t3); cursor: pointer; }
+.cs-pin.on { color: var(--c-primary); }
+.cs-pin:hover { color: var(--c-link); }
 .tb-card { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
 .tb-card .tbl-wrap { flex: 1; }
 .tb-card .pager { border-top: 1px solid var(--row-bd); flex: none; }

+ 15 - 0
src/pages.css

@@ -356,3 +356,18 @@
 .res-ic.error { color: var(--dang); }
 .result b { font-size: 18px; line-height: 26px; font-weight: 600; color: var(--t1); }
 .result p { margin: 6px 0 18px; font-size: 13px; color: var(--t2); max-width: 360px; }
+
+/* ---------- TRS 委托查询树形行(2026-10-16 按 trs/order 前端实现回改,只作用于这一张表 .tbl.ord;成交统计、资产信息仍按版本 3)----------
+   展开列 60px:细箭头右边 12px 次要色的子单笔数(OrderTreeToggle);悬停母单行时箭头显出浅底;
+   子单行 40px、次级字色、铺填充 2 浅底(不开斑马纹;填充 1 和行悬停色几乎相同,悬停看不出变化),树线同版本 3;
+   合并格「子单 N · 通道名」左对齐缩进 16px(order-child-label);委托状态格有撤单结果时叠小标签和一行 12px 原因(OrderStatusCell) */
+.cv .tbl.ord th.w-tg, .cv .tbl.ord td.w-tg { width: 60px; min-width: 60px; }
+.cv .tbl.ord .cnt { display: inline-block; min-width: 16px; margin-left: 2px; font-size: 12px; line-height: 16px; font-style: normal; color: var(--t3); text-align: center; vertical-align: middle; }
+.cv .tbl.ord tr.p.has:hover .tgl { background: var(--hover-veil); }
+.cv .tbl.ord tr.s td { height: 40px; background: var(--fill-2); }
+.cv .tbl.ord tbody tr.s:hover td { background: var(--row-hover); }
+.cv .tbl.ord .sub-lbl { display: block; padding-left: 16px; text-align: left; }
+.cv .tbl.ord .sub-lbl .dot { display: inline-block; width: 3px; height: 3px; margin: 0 8px; vertical-align: middle; border-radius: 50%; background: var(--t4); }
+.cv .tbl.ord .st-stack { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 0; }
+.cv .tbl.ord .st-stack .tg.sm { height: 20px; padding: 0 7px; }
+.cv .tbl.ord .st-stack .st-why { font-size: 12px; line-height: 16px; color: var(--td-t); }

+ 13 - 4
src/pages.js

@@ -45,10 +45,14 @@
     const cur = st.tab[g] || tabs[0][0];
     return `<div class="${cls}">${tabs.map(([k, l]) => `<span class="tab ${k === cur ? "on" : ""}" data-act="ptab" data-g="${g}" data-v="${k}">${l}</span>`).join("")}</div>`;
   };
-  const searchCard = (fields, actions, withLabel) => {
+  // tools:表格工具(列展示按钮),放在按钮组末尾——「查找/重置」后一条竖线,再导出等图标按钮和工具(2026-10-18 方案 2,10-19 导出改图标)
+  const searchCard = (fields, actions, withLabel, tools = "") => {
     const lw = withLabel ? Math.max(...fields.map((f) => f.label.length)) * 14 : 0;
     const rem = 3 - (fields.length % 3);
-    const acts = `<div class="sf-act" style="grid-column:span ${rem}">${actions.map(([t, type, icn]) => `<span class="btn ${type === "primary" ? "pri" : ""}" data-act="noop">${ic(icn)}${t}</span>`).join("")}</div>`;
+    const abtn = ([t, type, icn]) => `<span class="btn ${type === "primary" ? "pri" : ""}" data-act="noop">${ic(icn)}${t}</span>`;
+    // 竖线右边对结果表的操作一律图标按钮(10-19 数据导出改图标);没有列展示就没有竖线,导出仍是文字按钮
+    const main = actions.slice(0, 2).map(abtn).join(""), rest = actions.slice(2).map(tools ? U.ibtnHTML : abtn).join("");
+    const acts = `<div class="sf-act" style="grid-column:span ${rem}">${main}${tools ? `<span class="sep"></span>${rest}${tools}` : rest}</div>`;
     return `<div class="card"><div class="sf">${fields.map((f) => `<div class="sf-i">${withLabel ? `<label style="width:${lw}px">${esc(f.label)}</label>` : ""}${U.ctlHTML(f)}</div>`).join("")}${acts}</div></div>`;
   };
 
@@ -69,10 +73,15 @@
         if (i === 11) return v.startsWith("+") ? [v, "up"] : v.startsWith("-") ? [v, "dn"] : v;
         return v;
       };
-      body = `${searchCard(R.search, R.actions, false)}<div class="card tb-card">${tableHTML(R.cols, R.rows, { cell, keep: "risk-tbl" })}${U.pagerHTML(R.rows.length)}</div>`;
+      // 列展示:RiskMonitorTable 的操作、风险状态固定在左;勾掉的列不画(cell 按原列序号取值)
+      const items = R.cols.map((c, i) => ({ k: c[0], label: c[0], fixed: i === 0 || c[0] === "风险状态" ? "left" : undefined }));
+      const vis = R.cols.map((c, i) => i).filter((i) => !U.csSet(st, "risk").has(R.cols[i][0]));
+      body = `${searchCard(R.search, R.actions, false, U.csBtnHTML(st, "risk", items))}<div class="card tb-card">${tableHTML(vis.map((i) => R.cols[i]), R.rows, { cell: (r, i) => cell(r, vis[i]), keep: "risk-tbl" })}${U.pagerHTML(R.rows.length)}</div>`;
     } else if (cur === "status-log") {
       const cell = (r, i) => (i === 4 || i === 5 ? [r[i], "note_risk_status"] : r[i]);
-      body = `${searchCard(R.search.slice(2, 8), R.actions.slice(0, 2), false)}<div class="card tb-card">${tableHTML(R.statusLogCols, R.statusLog, { cell, keep: "risk-log" })}${U.pagerHTML(R.statusLog.length)}</div>`;
+      const items = R.statusLogCols.map((c) => ({ k: c[0], label: c[0] }));
+      const vis = R.statusLogCols.map((c, i) => i).filter((i) => !U.csSet(st, "risk-log").has(R.statusLogCols[i][0]));
+      body = `${searchCard(R.search.slice(2, 8), R.actions.slice(0, 2), false, U.csBtnHTML(st, "risk-log", items))}<div class="card tb-card">${tableHTML(vis.map((i) => R.statusLogCols[i]), R.statusLog, { cell: (r, i) => cell(r, vis[i]), keep: "risk-log" })}${U.pagerHTML(R.statusLog.length)}</div>`;
     } else {
       const cell = (r, i) => (i === 4 ? [r[i], "send_status"] : r[i]);
       body = `${searchCard(R.search.slice(2, 5), R.actions.slice(0, 2), false)}<div class="card tb-card">${tableHTML(R.notifyCols, R.notify, { cell, keep: "risk-ntf" })}${U.pagerHTML(R.notify.length)}</div>`;

+ 40 - 10
src/test.mjs

@@ -99,8 +99,20 @@ ok((await st()).active === "首页", "点标签页回首页");
 await page.click(`${cv} .hm-sc >> nth=3`);
 ok((await st()).active === "风控管理/共赢票据风险监控", "快捷入口打开风险监控");
 ok(await visible(`${cv} .ptabs`), "风险监控页有页面级页签");
+ok((await count(`${cv} [data-pop="cs:risk"]`)) === 1 && (await count(`${cv} .sf-act .sep ~ .btn.icon[title="数据导出"]`)) === 1 && !(await has(`${cv} .tb-card .tb-bar`)), "风险监控搜索区末尾有列展示,数据导出是图标按钮,表格卡片没有工具条");
+await page.click(`${cv} [data-pop="cs:risk"]`);
+ok((await text(`${cv} .cs-pop .cs-g >> nth=0`)).includes("风险状态") && (await count(`${cv} .cs-pop .cs-g >> nth=0 >> .cs-i`)) === 2, "风险监控面板:操作、风险状态在固定段");
+const riskTh = await count(`${cv} .tbl thead th`);
+await page.click(`${cv} .cs-pop [data-act="csvis"] >> nth=1`);
+ok((await count(`${cv} .tbl thead th`)) === riskTh - 1 && !(await text(`${cv} .tbl thead`)).includes("风险状态"), "勾掉风险状态列:表里没了");
+await page.click(`${cv} .cs-pop [data-act="csreset"]`);
+await page.click(`${cv} .ptabs`);
+ok((await count(`${cv} .tbl thead th`)) === riskTh && (await st()).pop === null, "重置后列回来,点页签条关闭面板");
 await page.click(`${cv} .ptabs .tab[data-v="status-log"]`);
 ok((await st()).tab.risk === "status-log" && (await text(`${cv} .tbl th`)).includes("票据编号"), "切换到风险状态变更记录");
+ok((await count(`${cv} [data-pop="cs:risk-log"]`)) === 1 && (await count(`${cv} .sf-act .btn:not(.icon)`)) === 2, "变更记录页签:查找、重置 | 列展示");
+await page.click(`${cv} .ptabs .tab[data-v="notification"]`);
+ok(!(await has(`${cv} [data-pop^="cs:"]`)) && !(await has(`${cv} .sf-act .sep`)), "追保通知页签前端没接列展示:不画竖线和齿轮");
 await page.click(`${cv} .ptabs .tab[data-v="monitor"]`);
 await page.click(`${cv} .tbl td .btn.dng`);
 ok((await st()).ovls.includes("confirm") && (await text(`${cv} .cfm-hd b`)).includes("强平卖出"), "危险行的「强平卖出」弹出确认框");
@@ -163,9 +175,23 @@ await page.click(`${cv} .cfm-ft .btn >> nth=0`);
 /* ---------- 列表页 → 抽屉 → 弹窗 ---------- */
 section("列表页:搜索展开、勾选、详情抽屉、补录弹窗");
 await goPage("list");
-ok((await st()).active === "共赢票据管理/订单管理" && (await count(`${cv} .sf .sf-i`)) === 5, "订单管理:搜索收起态 5 项");
+ok((await st()).active === "共赢票据管理/订单管理" && (await count(`${cv} .sf .sf-i`)) === 5 && (await page.$eval(`${cv} .sf-act`, (el) => el.style.gridColumn)) === "span 1", "订单管理:搜索收起态 5 项,按钮组占一列(数据导出改图标后,1440 宽一列放得下带齿轮的按钮组)");
+ok((await count(`${cv} .sf-act .sep`)) === 1 && (await count(`${cv} .sf-act [data-pop="cs:list-issue"]`)) === 1 && (await count(`${cv} .sf-act .sep ~ .btn.icon[title="数据导出"]`)) === 1 && !(await has(`${cv} .sf-act .sep ~ .btn:not(.icon, .txt)`)) && !(await has(`${cv} .tb-card .tb-bar`)), "按钮组:查找、重置 | 数据导出、列展示齿轮(竖线右边都是图标按钮);表格卡片没有工具条");
+await page.click(`${cv} [data-pop="cs:list-issue"]`);
+ok((await st()).pop === "cs:list-issue" && (await visible(`${cv} .cs-pop`)) && (await count(`${cv} .cs-pop .cs-g`)) === 2 && (await text(`${cv} .cs-pop .cs-g >> nth=0`)).includes("操作"), "点齿轮打开列展示面板:固定列一段、其余一段,操作列在固定段");
+const thAll = await count(`${cv} .tbl thead th`);
+await page.click(`${cv} .cs-pop [data-act="csvis"] >> nth=2`);
+ok((await count(`${cv} .tbl thead th`)) === thAll - 1 && (await st()).pop === "cs:list-issue" && (await count(`${cv} .cs-pop .cs-hd .ck.half`)) === 1, "勾掉一列:表头少一列、面板还开着、全选变半选");
+await page.click(`${cv} .cs-pop [data-act="csreset"]`);
+ok((await count(`${cv} .tbl thead th`)) === thAll && (await count(`${cv} .cs-pop .cs-hd .ck.on`)) === 1, "重置:列回来、全选恢复");
+await page.click(`${cv} .cs-pop [data-act="csall"]`);
+ok((await count(`${cv} .tbl thead th`)) === 1, "全选再点:数据列和操作列全部隐藏,只剩勾选列");
+await page.click(`${cv} .cs-pop [data-act="csall"]`);
+ok((await count(`${cv} .tbl thead th`)) === thAll, "再点全选:全部显示");
+await page.click(`${cv} .pg-list .tbl-wrap`);
+ok((await st()).pop === null && !(await has(`${cv} .cs-pop`)), "点面板外面关闭面板");
 await page.click(`${cv} [data-act="sf"]`);
-ok((await count(`${cv} .sf .sf-i`)) === 11, "展开后 11 项");
+ok((await count(`${cv} .sf .sf-i`)) === 11 && (await page.$eval(`${cv} .sf-act`, (el) => el.style.gridColumn)) === "span 1", "展开后 11 项,按钮组在第 4 行最后一格靠右(不再单独一行)");
 await page.click(`${cv} [data-act="rowsel"] >> nth=0`);
 ok((await st()).sel.length === 1 && (await visible(`${cv} .selbar`)), "勾选一行出现选择条");
 await page.click(`${cv} [data-act="allsel"]`);
@@ -188,9 +214,11 @@ await page.click(`${cv} .cfm-ft .btn >> nth=1`);
 await page.click(`${cv} .dw-hd [data-act="ovclose"]`);
 ok((await st()).ovls.length === 0, "关闭抽屉");
 await page.click('.bk-seg[data-seg="end"] button[data-v="sales"]');
-ok(!(await has(`${cv} [data-act="allsel"]`)) && (await count(`${cv} .sf .sf-i`)) === 5, "销售端订单管理:没有勾选列");
+ok(!(await has(`${cv} [data-act="allsel"]`)) && (await count(`${cv} .sf .sf-i`)) === 5, "销售端订单管理:没有勾选列,收起态同样 5 项");
+ok((await count(`${cv} [data-pop="cs:list-sales"]`)) === 1, "销售端订单管理也有列展示");
 await page.click('.bk-seg[data-seg="end"] button[data-v="trs"]');
 ok(!(await has(`${cv} .tbl td .btn.txt`)) && (await text(`${cv} .tbl thead`)).includes("用户ID"), "TRS 委托查询:没有操作列");
+ok(!(await has(`${cv} [data-pop^="cs:"]`)) && !(await has(`${cv} .sf-act .sep`)) && (await count(`${cv} .sf .sf-i`)) === 5, "TRS 委托查询前端没接列展示:按钮组不画竖线和齿轮,收起态仍露 5 项");
 await page.click('.bk-seg[data-seg="end"] button[data-v="issue"]');
 
 /* ---------- 直接打开的弹层页面 ---------- */
@@ -266,12 +294,14 @@ ok((await count(`${cv} .empty.box`)) === 5 && (await count(`${cv} .result`)) ===
 section("树形行:TRS 委托查询母单/子单、成交统计汇总/明细、持仓证券/通道");
 await goPage("list");
 await page.click('.bk-seg[data-seg="end"] button[data-v="trs"]');
-ok((await count(`${cv} .tbl tbody tr.p.has`)) === 2 && (await count(`${cv} .tbl tbody tr.s`)) === 2, "委托查询:2 个母单有子单,默认展开第一单 2 条子单");
-ok((await text(`${cv} .tbl tbody tr.s td[colspan="7"]`)).includes("通道") && (await count(`${cv} .tbl tbody tr.s.last`)) === 1, "子单行合并格写通道/交易渠道,最后一条带 last");
-const subCellH = await page.$eval(`${cv} .tbl tbody tr.s td[colspan="7"]`, (el) => el.getBoundingClientRect().height / (parseFloat(getComputedStyle(el.closest(".cv")).zoom) || 1));
-ok(Math.abs(subCellH - 36) < 1.5, `子单行 36px(实测 ${subCellH.toFixed(1)})`);
+ok((await count(`${cv} .tbl tbody tr.p.has`)) === 10 && (await count(`${cv} .tbl tbody tr.s`)) === 3, "委托查询:10 张母单都有子单(单通道单也补一条),默认展开第一单 1 条、第一张拆单 2 条");
+ok((await count(`${cv} .tbl tbody tr.p.has .cnt`)) === 10 && (await text(`${cv} .tbl tbody tr.p.has .cnt >> nth=1`)) === "2", "展开列带子单笔数,拆单计数 2");
+ok((await text(`${cv} .tbl tbody tr.s td[colspan="6"]`)).includes("子单 1") && (await count(`${cv} .tbl tbody tr.s.last`)) === 2, "子单行合并 6 列写「子单 N · 通道名」,每组最后一条带 last");
+const subCellH = await page.$eval(`${cv} .tbl tbody tr.s td[colspan="6"]`, (el) => el.getBoundingClientRect().height / (parseFloat(getComputedStyle(el.closest(".cv")).zoom) || 1));
+ok(Math.abs(subCellH - 40) < 1.5, `子单行 40px(实测 ${subCellH.toFixed(1)})`);
+ok((await count(`${cv} .tbl tbody tr.s td.c`)) > 0 && (await count(`${cv} .tbl tbody .st-stack`)) === 1, "子单行文字列居中;委托状态格叠撤单结果(1 行)");
 await page.click(`${cv} .tbl th .tgl.all`);
-ok((await count(`${cv} .tbl tbody tr.s`)) === 4 && (await has(`${cv} .tbl th .tgl.all.on`)), "表头全部展开:4 条子单,图标变主色");
+ok((await count(`${cv} .tbl tbody tr.s`)) === 12 && (await has(`${cv} .tbl th .tgl.all.on`)), "表头全部展开:12 条子单,图标变主色");
 await page.click(`${cv} .tbl th .tgl.all`);
 ok((await count(`${cv} .tbl tbody tr.s`)) === 0, "再点全部折叠");
 await page.click(`${cv} .tbl tbody tr.p.has >> nth=1`);
@@ -280,7 +310,7 @@ await page.click(`${cv} .tbl tbody tr.p.has .tgl.on`);
 ok((await count(`${cv} .tbl tbody tr.s`)) === 0, "点图标收起");
 await page.click('.bk-seg[data-seg="end"] button[data-v="issue"]');
 await goPage("tree");
-ok((await st()).active === "用户管理/委托查询" && (await count(`${cv} .tbl tbody tr.p.has`)) === 2 && (await count(`${cv} .tbl tbody tr.s`)) === 2, "树形行页默认变体是委托查询:和列表页 TRS 同一页,默认展开第一单");
+ok((await st()).active === "用户管理/委托查询" && (await count(`${cv} .tbl tbody tr.p.has`)) === 10 && (await count(`${cv} .tbl tbody tr.s`)) === 3, "树形行页默认变体是委托查询:和列表页 TRS 同一页,默认展开第一单和第一张拆单");
 await page.click('.bk-seg[data-seg="variant"] button[data-v="stats"]');
 ok((await st()).active === "风险监控/成交数据统计" && (await count(`${cv} .tbl`)) === 5, "成交数据统计:整体情况 + 按通道、按用户、按交易渠道、按股票四张表");
 ok((await count(`${cv} .tbl tbody tr.s`)) === 3 && (await text(`${cv} .tbl tbody tr.s .ind`)).includes("用户"), "按通道统计默认展开第一条汇总:3 条明细,带「用户」前缀");
@@ -397,7 +427,7 @@ ok((await cvTheme()) === "db", "再点回深色 B");
 await goPage("kit");
 ok((await text(`${cv} .kit-top h1`)).includes("深色皮肤 B 石墨灰") && (await text(`${cv} .kit-top`)).includes("#4A8CF7"), "组件总览标题跟随皮肤,主色是 B 的 #4A8CF7");
 await goPage("spec");
-ok((await count(".sp-theme")) === 2 && (await text(".sp-theme.on")).includes("石墨灰") && (await count(".sp-tbl.cmp tbody tr")) === 9 && (await count(".sp-pick")) === 8, "规范摘要「换肤」两张卡片、当前是 B、对照表 9 行;选型结果 8 项含深色皮肤 B、树形行 3、自动化配置 2");
+ok((await count(".sp-theme")) === 2 && (await text(".sp-theme.on")).includes("石墨灰") && (await count(".sp-tbl.cmp tbody tr")) === 9 && (await count(".sp-pick")) === 9 && (await text(".sp-picks")).includes("列展示按钮位置"), "规范摘要「换肤」两张卡片、当前是 B、对照表 9 行;选型结果 9 项含深色皮肤 B、树形行 3、列展示 2、自动化配置 2");
 ok((await text("#bkSpec h2 >> nth=3")).includes("石墨灰"), "规范摘要「颜色」标题写着当前皮肤");
 await page.click('.sp-theme[data-theme="f"] [data-theme-go]');
 ok((await S()).page === "home" && (await cvTheme()) === "f", "「用这套皮肤看首页」切到浅色并跳到首页");

+ 7 - 0
src/使用说明.txt

@@ -24,6 +24,7 @@
     菜单搜索、通知、用户菜单、搜索展开、勾选表格行、点「详情」打开抽屉、在抽屉里点「补录」叠开弹窗、
     切换页签、切换买卖方向和价格。登录页里可以换一张验证码、显示密码、记住我、忘记密码。
   - 上方可以切换看发行/销售/TRS 哪一端,以及页面的变体(登录/重置密码、404/401、几种确认框、自动化配置的查看/编辑)。
+  - 列表页、风险监控的搜索区按钮组末尾有个齿轮(列展示):点开面板可以勾掉列、点重置,勾掉的列当场从表里消失;点面板外面关闭。
   - A股自动化配置:点「编辑」进入编辑,可以拨开关、在数字框里直接输入(上一档的上限跟着变)、选报价方式、
     在对冲方案里先选下单类型再选方案、用 + / − 加减一档;点「保存」会按代码的规则校验,订单开关有变更时先弹确认框。
     风控、清算两张卡片各自编辑。上方「查看/编辑」按钮和画面同步,编辑中切回「查看」等于取消。
@@ -37,10 +38,16 @@
   - 树形行(2026-10-10 从 10 个版本里选定「版本 3 树线不铺底」):父子行在同一张表里时,最左一列放细箭头,子行 36px、次级字色、
     不铺底色,靠树线和缩进区分;表头同位置是全部展开/折叠。三个前端里只有 TRS 的三个页面是这种表,都在「树形行 · TRS 三个页面」
     里(委托查询母单→子单、成交统计汇总→明细、持仓证券→通道;委托查询和「列表页 · TRS」是同一页)。
+    委托查询 2026-10-16 已按前端实现回改:展开列带子单笔数、子单行 40px 铺浅底、每张母单都有子单(单通道单也补一条);另两页仍按版本 3。
   - 换肤:两套皮肤——浅色 F 晴空渐变(默认)、深色 B 石墨灰(10-08 从三版候选里选定)。
     顶栏右侧「皮肤」可以切着看每个页面;登录页右上角的月亮/太阳按钮、用户菜单里的「界面设置」在画面里也能切。
     「规范摘要 → 换肤」有两套的对照表。待确认:默认浅色还是跟随系统;切换后的选择记在账号还是本机。
   - 「界面尺寸」切换已去掉(密度已统一),「界面设置」收进用户菜单。
+  - 列展示(2026-10-18 从六版里选定「方案 2 搜索区·图标按钮」,方案稿在「列展示按钮位置·方案预览」):前端现状是表格卡片顶部一条 48px 工具条只放这一个按钮,
+    本稿改成放在搜索区按钮组末尾——「查找/重置」后一条竖线,再「数据导出」和「列展示」两个图标按钮(下载、齿轮,悬停提示按钮名;数据导出 10-19 由文字改为图标),「展开」仍在最末;
+    面板(显隐、拖动排序、固定列首/列尾、全选、重置)从齿轮下方弹出,表格卡片直接从表头开始。
+    竖线右边都是图标按钮后整组约 330px,1440 宽下一列放得下:收起态露 5 个条件,展开后按钮组在最后一行剩下的格子里(10-19 定,方案稿在「搜索区按钮组同行·方案预览」)。
+    前端接了列展示的是订单管理(发行、销售)、票据管理、风险监控、风险状态变更记录;TRS 委托查询没接,不画。
   - A股自动化配置(2026-10-14 从三版里选定「版本 2 分区网格」,同日前端实现后本稿按实现回改):交易卡片里报价一个面板、
     报价表撑满,4 类订单自动化排成 2×2 子面板,买入/卖出、强平/到期左右并排;查看态开关写成「已开启/已关闭」,
     编辑态才出现开关;表格整表居中(沿用原型)。待确认的只剩编辑态行高一条,见页面下方。

Some files were not shown because too many files changed in this diff