v0.2.0: 交互发布+多屏发布增强+页面管理优化

- 版本升级 0.1.0 -> 0.2.0(package.json 单一来源,标题栏/设置/关于同步)
- 新增交互发布:指定大屏为主屏,其余屏自行加载本屏页面,主屏可轮流投屏各屏内容
- 单屏发布支持自动轮播(intervalSec)与停止轮播(保留窗体可手动切换)
- 多屏发布支持 alsoMain 主屏同时参与;新增屏幕检测弹窗
- 页面列表缩略图 key 重建避免浏览器复用旧图;「打开」按钮防抖(opening 状态)
- URL 粘贴自动提取网页标题/描述(主进程 fetchMeta)
- 项目组新增「新建」:默认创建 1 个空白页面,保存时选择位置
- 发布组 4 按钮(预览/单屏/多屏/交互)统一为线性 SVG 显示器族图标
- 属性面板 URL 输入框加高 1 行、页面参数加高 2 行并去掉水印
- 编辑预览 WebFrame key={src} 重建,修复切页内容错位
This commit is contained in:
2026-08-23 16:31:20 +08:00
parent 8d96f51b05
commit aa8f070414
22 changed files with 661 additions and 138 deletions
+29
View File
@@ -208,3 +208,32 @@
- **修复**Node 脚本(`fix-msgs.js`,用完已删)用 `git cat-file commit` 读原对象 → 保留 headertree/parent/author/committer/时间戳)→ 仅替换 message → `git hash-object -t commit -w --stdin` 重建 → `git update-ref`。新 hash`2c90205`init/`4b3ea03`build/`b11bea1`docs)。已 `git push --force-with-lease` 强推(用户授权),`69d592b...b11bea1 (forced update)`,远程一致、工作区干净。
- **以后约定**:提交信息用 `git commit -F <utf8消息文件>`write_to_file 生成的 UTF-8 文件),**禁用 `git commit -m "中文"`**。
- **执行环境备注**execute_command 实际走 cmd`2>$null` 会被当作重定向生成 `$null` 文件(需手动删);`;` 不分割 cmd 命令;`[Console]::OutputEncoding=UTF8` 对 git 输出无效,验证用 `git log > file` 后 read_file 工具读。
## 2026-08-23 发布/轮播测试问题修复(3 项,typecheck 通过)
用户测试反馈 3 个问题,全部修复:
1. **单屏发布勾选轮播无效(根因实锤)**`ipc.ts` `carousel:start` handler 漏传 `payload.intervalSec`,自动轮播定时器从不启动。已修:`startCarousel(payload.index, payload.pages, payload.intervalSec ?? 0)`
2. **上下键/数字键无效(疑似焦点丢失)**`screens.ts` 增强焦点——轮播首窗 `ready-to-show``win.show()``startCarousel` 结尾 400ms 延迟再次 show+focus 首窗;`switchCarousel` 切页后 120ms 延迟再 focus。注:编辑器主窗口聚焦时大屏窗口收不到按键属 Electron 正常行为,需点击大屏窗口。
3. **Esc 快捷键 =「关闭所有」**`ensureWindow`(单屏预览/多屏发布)、`startCarousel`(轮播)、`publishMain`(主屏 WebContentsView)的 `onEscape` 全部改为 `closeAllScreens()`(原为 closeScreen/stopCarousel/restoreMain)。所有提示文案同步改「Esc 关闭所有」。
4. **多屏发布仅保留 Esc/F5**`makeKeyHandler` 新增 `navDisabled`(跳过数字/方向/锁定键);`navDisabledWins` Set 标记窗口;`publishScreen`/`ensureWindow`/IPC/preload 透传 `navDisabled``publishAll``navDisabled:true``ensureWindow` 复用窗口时也会刷新标记)。
5. **「停止轮播」= 结束窗体**:确认 `stopCarousel` 本就 destroy 全部轮播窗口;`Ribbon.tsx` 改进 `carouselRunning`(任一屏轮播即可用,不再要求当前页 screen 匹配)+ 停止目标优先当前屏、否则第一个轮播屏;toast 改「轮播已结束(窗体已关闭)」。
改动文件:`src/main/ipc.ts``src/main/screens.ts``src/preload/index.ts`+`index.d.ts``src/renderer/src/publishActions.ts``components/Ribbon.tsx``components/PublishModals.tsx`。typecheck 通过;**主进程改动需重启 dev 才生效**。
- 已重启 dev 并验证:主窗口标题「智慧农业大数据可视化控制中心 v0.1.0」,dev-err.log 无错误,vite 5173 就绪,5 个 electron 进程。
- 新增页面删除确认弹窗:`views/PageList.tsx` 删除按钮改为 `setDelIdx(i)` 弹出 Modal 确认(标题=删除页面,内容含页面标题+id,footer=取消/删除),确认回调先 `selectPage(delIdx)``delPage()` 防弹窗期间切换选中页误删;复用 `Modal.tsx` + `.btn.danger` 样式,纯 renderer 改动走 HMR。
- 排查"重新截图缩略图不刷新"**用 CDP 实测(dev 以 `npm run dev -- -- --remote-debugging-port=9223` 启动,Node 24 全局 WebSocket 直连)证明截图后 img.src 每次都变化(1390514→1403322→1397278)、toast 链路正常,UI 刷新其实一直正常**,用户"没看到刷新"是因为静态页面像素几乎不变。改进:① `PageList.tsx``<img>``key={p.preview}` 强制重建(杜绝浏览器复用旧图);② `store.ts` capturePreview 区分反馈——数据变化 toast「✅页面截图已更新」/数据相同 toast「💡页面内容无变化,缩略图已是最新」;toast 类名是 `.toast`(不是 .toast-item)。CDP 临时脚本已删。
- 新增"粘贴 URL 自动提取网页标题/描述":主进程 `ipc.ts` 新增 `page:fetchMeta` handler(隐藏 BrowserWindow 加载 URL`Promise.race` 12s 超时,轮询最长 2s 等 JS 动态 title,提取 `document.title` + `meta[name=description]`/`og:description`,仅接受 http/https);preload `page.fetchMeta` 暴露 + `index.d.ts` 类型;`PropertyPanel.tsx` URL textarea `onPaste` 时用 `e.clipboardData.getData('text')` 判断 http(s) 即调用 `fillMetaFromUrl``useRef` 去重),提取成功覆盖填入 title/desc,toast 区分「已自动填入网页标题与描述」/「已自动填入网页标题」。CDP 实测 `fetchMeta('http://localhost:5173/sample-screen.html')``{"title":"农业数据总览","desc":""}`。注意 `LoadURLOptions``timeout` 字段,超时须用 `Promise.race`。已重启 dev 生效。
- 「打开」按钮防抖:`store.ts` AppState 新增 `opening: boolean`create 初值 false),`open()` 开头 `if (get().opening) return` + `set({opening:true})``finally set({opening:false})`(防重入锁,覆盖取消/失败/密码错误全流程);`Ribbon.tsx` 打开按钮订阅 `opening` → disabled + 图标⏳/文案「打开中…」;`ProjectInfo.tsx` 配置页「打开工程」按钮同样 disabled+「⏳ 打开中…」。`.ribbon-btn:disabled`/`.btn:disabled` 样式已存在。纯 renderer 改动 HMR 生效。
- 单屏发布三项修复:①键盘控制:`screens.ts``makeKeyHandler(b, quick=false)` 新增 quick 参数,quick 时 threshold 强制 0(原默认 keyThreshold=350ms 导致快速按键被吞);`ensureWindow`/`startCarousel`/`publishMain` 三处调用均传 `true`。②停止轮播:`CarouselState``auto` 字段(=intervalSec>0 && pages.length>1);`ScreenInfo``carouselAuto?`main screens.ts + renderer types.ts);`listScreens` 返回 `carouselAuto: car.auto`、轮播标题含「· 自动」;新增主进程 `stopCarouselAuto(index)`(清 timer、auto=false、**保留窗体**+ IPC `carousel:stopAuto` + preload `carousel.stopAuto``publishActions.stopCarouselOnScreen` 改名 `stopCarouselAutoOnScreen`(toast 提示可 ↑/↓ 手动切换);`Ribbon` 的「停止轮播」`carouseling` 过滤条件改为 `s.carouselAuto && /轮播/`(仅自动模式可用)。③`PublishModals.tsx` ScreenSelectModal 默认不勾选自动轮播:`useState(false)`(原来 `props.showCarousel ?? false` 导致 single 模式默认勾选)。注:多屏发布窗口 navDisabled 仍禁用数字/方向键,quick 不影响。改主进程+preload,已杀旧实例重启 dev 至 5173。typechecknode+web)通过。
- 新增「交互发布」:演示场景(1 大屏 + 多小屏轮流投屏讲解)。`screens.ts` 新增 `startInteractive({mainIndex, pagesByScreen})`:主屏轮播内容=**所有屏页面按屏号排序拼接**(label='交互发布'),其余屏轮播本屏页面(label='单屏轮播'),复用 startCarousel`CarouselState``label` 字段、`startCarousel` 加第 4 参 `label='单屏轮播'``listScreens` 标题用 `car.label`。IPC `carousel:startInteractive` + preload `carousel.startInteractive`。渲染层:`publishActions.startInteractiveOnScreen(mainIndex)`(按 page.screen 分组收集 display 页面);`PublishModals.InteractiveModal`(卡片点击选主屏,默认主显示器,显示各屏页面分布,`m-scard.clickable/.sel` 新样式);`Ribbon` 发布组 multi 后加「交互发布🔁」按钮(pubModal 类型加 'interactive')。主屏上 ↑/↓/数字键即轮流把各屏页面切到大屏。dev 5173 运行中,preload 产物含 startInteractivetypecheck 通过。坑:同文件多 replace 并行会写锁超时,须每批每文件只改一处。
- **版本升级 0.1.0 → 0.2.0 + 两个用户问题修复**:
- **问题1(属性面板所属屏幕最多屏4)**:`PropertyPanel.tsx` `screenCount = Math.max(screens.length, 4, pages.length, ...pages.map(p=>p.screen))`。CDP 实测 6 页项目下拉显示 1-6。
- **问题2(编辑 P-005 预览显示 P-006**`Canvas.tsx` 的 WebFrame 加 **`key={src}` 强制重建 webview**(切页销毁重建,杜绝 webview 复用残留/远端 hash 被改写导致内容错位)。实测:本地模拟(同 URL 不同 hash+query)切换 attr/url 均正确;远端实测 P-005(`#/zhihuiyq`)→「智慧蚕桑·园区数据可视化平台」、P-006(`#/paihangbang`)→「排行榜·蚕茧交易音视频督导中心」,内容一一对应。注:WebFrame src 更新逻辑(setAttribute)本身正确,但复用 webview 在远端加载时序/导航残留下可能显示旧页,key 重建为根治。`main.tsx``window.__store = useStore` 调试接口(保留无碍)。
- 版本号改动:`package.json`(主来源)→0.2.0、`main/settings.ts` 默认 projectVersion、`renderer/utils.ts` 兜底 version、`AboutPanel.tsx`/`ProjectInfo.tsx` fallback+placeholder。**项目 JSON 文件内 version 字段是用户数据不动**。标题栏版本 = app.getVersion() 读 package.json,重启后显示 v0.2.0。
- 调试技巧沉淀:CDP 动态 `import('/src/store.ts')` 不稳定(偶发 "Promise was collected",因 context 销毁),改走 `window.__store` 全局或纯 DOM(点击 .page-card)最稳;`npx tsc --noEmit -p tsconfig.web.json` 单独验渲染层。临时测试脚本/本地模拟页已删,dev 已重启。
- **新增「新建」+ 发布图标统一 + 属性面板高度(v0.2.0 并入,未发布)**:
- **「新建」按钮**(项目组第一个 🆕):`store.ts` AppState 加 `newProject`——重置为单个空白页 `{id:'P-001',title:'空白页面',desc:'',screen:1,ctrl:0,display:true,url:'about:blank',params:''}``filePath:null, dirty:true`(保存走另存为);syncProject 由 App.tsx pages effect 自动处理,无需手动调。CDP 实测(build 产物 + electron.exe `--remote-debugging-port=9223`):点击后 pages=[P-001:空白页面:about:blank]、预览显示空白页。
- **发布组 4 按钮图标统一**:预览本页/单屏发布/多屏发布/交互发布 由 emoji 换成**统一样式线性 SVG**(显示器族:24 viewBox、stroke currentColor 1.8、15px、在 `.rb-icon` flex 容器内自动居中着色):预览=显示器+播放三角、单屏=显示器+轮播旋转箭头、多屏=前后双显示器、交互=显示器+双向箭头。`Ribbon.tsx` 顶部定义 `svgIconProps` + 4 个图标组件,`RibbonItem.icon` 类型改为 `string | React.JSX.Element`(emoji 字符串兼容)。CDP 实测发布组 `svgs=4`
- **属性面板**URL 输入框 `rows 2→3`;页面参数 textarea `rows=4`(默认 2 加 2 行)并**删除 placeholder 水印**URL 的 placeholder 保留)。`.prop-item textarea``min-height:46px`+`resize:vertical`,无固定高度,rows 生效。
- **CDP 验证 build 产物**注意:`npm run build` 后必须用 `node_modules\electron\dist\electron.exe . --remote-debugging-port=9223``electron.cmd` 不在 PATH 会报 file not found),页面 URL 是 `file://...out/renderer/index.html`,脚本匹配 page 类型须排除 chrome:// 与 devtools。验证完已杀实例重启 dev 至 5173。