停靠布局、背景图片、无边框窗体;运行区按比例缩小、可放大

- Dock:和 WinForms 一样按 Controls 倒序依次停靠,扣掉 Panel 边框、GroupBox 标题占的地方;
  改大小、显示隐藏、加入移出控件时重新布局;SuspendLayout / ResumeLayout / PerformLayout
- 设计器里每次修改后按同样的规则排好停靠的控件(designer/layout.ts);属性窗口加"停靠"
- BackgroundImage + BackgroundImageLayout(平铺、居中、拉伸、缩放):窗体、面板、图片框,
  设计器里从项目资源选;无边框窗体没有标题栏,大小不含边框
- 背景色沿用父控件:没设背景色的标签、面板、分组框等画父控件的背景色(不是背景图片),
  和 WinForms 一样;分组框标题处边框断开、透出背景;文本框的 BackColor 生效
- 图片框可以放子控件,把鼠标按下、松开发给程序
- 运行区:窗体比运行区大时整体按比例缩小并显示比例;"放大"占满整个网页(窗体在左、实训台在右),Esc 还原
- Paint 事件:VS 双击面板生成的空 panel1_Paint 能编译(平台由浏览器绘制,不触发)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-23 15:14:34 +08:00
1 parent 42e258ddcd
commit d60868d912
17 files changed
+600 -49

No files matched your search

+21 -1
View File
@@ -1,6 +1,6 @@
// 运行时图片:运行器发来的图片数据由 RunSession 保存成 blob 网址,控件属性里只带图片编号。
// 运行区 provide,ControlView inject;设计器预览没有运行时图片,取到空表。
import type { InjectionKey } from 'vue'
import type { CSSProperties, InjectionKey } from 'vue'
export const RUN_IMAGES: InjectionKey<ReadonlyMap<string, string>> = Symbol('runImages')
@@ -12,3 +12,23 @@ export interface ImageValue {
/** 设计器预览里的项目资源:直接给出素材网址(运行时用 key) */
url?: string
}
/** 背景图片的 CSS:BackgroundImageLayout 为 Tile(默认,平铺)、None(左上角一张)、Center、Stretch、Zoom */
export function backgroundCss(value: unknown, layout: unknown, images: ReadonlyMap<string, string>): CSSProperties {
const image = value as ImageValue | null | undefined
const url = image ? image.url ?? images.get(image.key) : undefined
if (!url) return {}
const base = { backgroundImage: `url("${url}")` }
switch (layout) {
case 'None':
return { ...base, backgroundRepeat: 'no-repeat', backgroundPosition: '0 0' }
case 'Center':
return { ...base, backgroundRepeat: 'no-repeat', backgroundPosition: 'center' }
case 'Stretch':
return { ...base, backgroundRepeat: 'no-repeat', backgroundSize: '100% 100%' }
case 'Zoom':
return { ...base, backgroundRepeat: 'no-repeat', backgroundPosition: 'center', backgroundSize: 'contain' }
default:
return { ...base, backgroundRepeat: 'repeat', backgroundPosition: '0 0' }
}
}