From 7f7d3925b21428843bbb9d547d143b5d9b7fdec4 Mon Sep 17 00:00:00 2001 From: LoveChengke Date: Sat, 17 Oct 2026 14:29:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(island):=20=E5=A2=9E=E5=8A=A0=E5=B8=B8?= =?UTF-8?q?=E9=A9=BB=E5=86=85=E5=AE=B9=E5=B9=B6=E6=B2=BF=E7=94=A8=20ClassI?= =?UTF-8?q?sland=20=E5=B0=BA=E5=AF=B8=E4=B8=8E=E9=81=BF=E8=AE=A9=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- docs/dev/20261017-implementation.md | 20 + docs/reference.md | 7 + .../src/island/ComponentNode.vue | 43 ++- .../src/island/CustomIsland.vue | 353 ++++++++++++++---- .../src/island/custom-island.css | 123 ++++++ packages/desktop-client/src/main/island.ts | 157 +++++++- .../desktop-client/src/main/smoke-layout.ts | 2 + .../desktop-client/src/main/smoke-resident.ts | 285 ++++++++++++++ packages/desktop-client/src/preload/island.ts | 4 + .../views/settings/IslandSettingsView.vue | 123 +++++- packages/shared/src/island-layout.ts | 99 ++++- 12 files changed, 1092 insertions(+), 127 deletions(-) create mode 100644 packages/desktop-client/src/main/smoke-resident.ts diff --git a/README.md b/README.md index 2630402..de6c22a 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,11 @@ ClassHelper是给中小学班级用的一套信息管理系统。
- [x] 通知、新作业、叫人都会上岛:先是胶囊,点开是详情卡 - [x] 多条消息展开为竖排列表,按重要程度排序,放不下时提示去应用内看 -- [x] **上课时段不打扰**:普通通知暂存到下课再弹;紧急通知与叫人立刻弹 +- [x] **默认提醒模式上课时段不打扰**:普通通知暂存到下课再弹;紧急通知与叫人立刻弹 - [x] 尺寸、圆角、字号、主题色、停靠位置(顶部 / 底部 × 左 / 中 / 右)都可以调 - [x] 纯黑 / 毛玻璃 / 主题色渐变三种底 - [x] 触摸屏(希沃白板这类一体机)也能点开 +- [x] 可选常驻岛:自定义收起与无通知展开内容,收到消息显示铃铛,点击查看通知 ### 部署与运维 diff --git a/docs/dev/20261017-implementation.md b/docs/dev/20261017-implementation.md index f884474..cf7bc10 100644 --- a/docs/dev/20261017-implementation.md +++ b/docs/dev/20261017-implementation.md @@ -64,6 +64,26 @@ 本轮视觉回归通过 102/102,随后补充的岛宽修正与展开可见性检查通过后台 28/28,并通过二次启动。后台最终几何检查先等待原渲染器的形变兜底和尺寸防抖完成,避免在隐藏窗口的中间动画帧判断最终尺寸。客户端构建、类型检查、lint、改动文件格式检查及文档严格构建均通过。 +## 可选常驻岛 + +本机 `islandLayout.residentEnabled` 默认关闭,仅在 custom 模式生效。开启后,现有方案 rows 作为收起内容,新增 expandedRows 保存没有通知时点击展开的内容;两套内容在同一编辑器中切换编辑,随方案复制、导入导出和本机配置一起保存。旧配置自动补齐展开布局,不改变原来的常驻选择。 + +按本次产品要求,常驻模式中普通、紧急和叫人消息都先提示右侧铃铛及去重后的数量,点击常驻内容、铃铛或按 Enter/空格才展开原通知列表;上课时允许主动查看。没有消息时展开自定义内容,收起、处理完成及通知撤回后保留常驻岛。经典模式与关闭常驻的自定义模式继续使用原提醒规则。 + +铃铛固定占位,避免收消息时内容横移。普通组件默认继承行的垂直居中对齐;原先无条件 align-self:stretch 会使不同字号文字从顶部排版,现只在父容器有宽度且明确配置时应用覆盖对齐。时钟采用等宽数字,分隔符保持固定线高,展开页标题与收起按钮在同一基准线上。设置页重新获得焦点时读回本机配置,避免编辑器内改常驻选项后主设置页显示旧值。 + +新增隔离检查覆盖旧配置兼容、空闲常驻、铃铛与文本对齐、无消息展开/收起、普通与紧急消息点击展示、同 id 去重、课堂中查看、键盘展开、处理完成后的窗口几何、空闲展开时新消息提示、两套内容独立编辑和关闭常驻恢复经典模式。截图留档于 `.cache/resident-island-shots/`。 + +### 再次核对 ClassIsland 尺寸与避让设置 + +2026-10-17 重新从 GitHub master 读取 [经典主题](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/XamlThemes/ClassicTheme/Styles.axaml)、[Fluent 主题](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/XamlThemes/FluentTheme/Styles.axaml)、[默认设置](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/Models/Settings.cs)、[窗口设置页](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/Views/SettingPages/WindowSettingsPage.axaml)、[外观设置页](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/Views/SettingPages/AppearanceSettingsPage.axaml)及 [行样式](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/Controls/MainWindowLine.axaml)。主题行高为 40、内容左右留白 12;四档字体为 14/16/18/20,时钟用强调字号 18;默认圆角 8、背景不透明度 0.5、缩放 1、行距 5。共享 CLASSISLAND_ISLAND_METRICS 作为唯一尺寸与默认值表,常驻行移除上下 12px 留白。缩放、圆角与背景透明度范围也与上游一致。 + +原生窗口复用现有光标轮询,在常驻背景移入时降低到 5% 不透明度并开启点击穿透,移出恢复;支持反转及关闭。保留独立的右侧交互入口:无消息时是小展开箭头,有消息时显示铃铛与数量。它不触发避让,避免和此前点击查看通知的要求冲突;编辑、展开和触笔/触摸后 1 秒内暂停避让。这是为本产品保留交互入口的明确适配,上游原始主界面默认整体穿透。未引入 RawInput/.NET 依赖,轮询方式对应上游默认 UseRawInput=false。窗口隐藏或淡化不清空消息队列。 + +补充后台检查实测常驻行 40px、原生 setOpacity 的 5% 比例、点击穿透状态、移出恢复、反转/关闭淡化、右侧入口与触摸宽限期。源码缓存和截图分别留在 `.cache/classisland-current/`、`.cache/resident-island-ci-shots/`。 + +最终构建视觉验收 118/118,随后后台重启检查 43/43、单实例锁和配置句柄释放通过;全仓类型、lint、构建与产物凭据门禁、格式及文档严格检查通过。最终截图为 `.cache/resident-island-ci-final-shots/`,验收日志为 `.cache/resident-island-ci-visual-final.log`。本次不修改业务数据库、不打包或推送发布。 + ## 十项自检 1. 第三周周三第二节:ClassPlan 的规则与 ClassPlanEntry 的 timeLayoutItemId → `resolveActiveClassPlan` → `loadTermContext/resolveCurrentWeek` → `projectPlan` → `/schedules/active-week` → `refreshIslandSchedule` 的整周 IndexedDB 缓存 → `recomputeClassState` 发送当天课程 → `ComponentNode.vue` 课程表组件。 diff --git a/docs/reference.md b/docs/reference.md index e00503f..944e837 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -1583,6 +1583,13 @@ makensis 临时目录、ClassIsland 路径与日志编码等)。这里只列 天气组件本轮不做:没有可离线使用的数据源,验收不依赖外网。 自定义组件栏位于灵动岛内部,提醒内容也在同一个岛体内展开,不再嵌套另一张带底色的岛卡片。 +可在「设置 → 灵动岛」选择常驻岛:收起内容与无通知展开内容分别编辑,并随方案一起导入导出。 +常驻模式的新消息(含紧急消息)先亮铃铛和数量,点击岛体或铃铛查看原通知列表;上课时也可主动查看。 +没有待处理消息时点击岛体,展示自定义的空闲展开页;处理完消息后回到常驻内容。关闭常驻后使用原提醒规则。 +铃铛在右侧预留固定空间,收到消息时内容不横移;默认组件按垂直中心对齐,时间使用等宽数字。 +常驻外观重新按 ClassIsland 源码对齐:单行基准高 40 px、左右留白 12 px、四档字号 14/16/18/20、时钟使用强调字号 18 px;默认缩放 1×、圆角 8 px、背景不透明度 0.5、行间距 5 px。 +窗口设置沿用「指针移入淡化」与「反转指针移入淡化」;普通背景淡化至 5% 并穿透点击,移出恢复。铃铛/展开按钮、编辑模式、已经展开的内容及触摸操作短暂宽限期保持可交互。 +界面缩放范围 0.75–2.5×,圆角 0–20 px,背景不透明度 0–1;整体外观可一键恢复 ClassIsland 默认值。 编辑器左侧按用途添加组件、中央预览岛内排列、右侧只显示当前对象的设置;方案操作收在顶部菜单。 主设置页选择经典或自定义灵动岛,自定义模式的组件尺寸、配色在编辑器中调整,主设置页保留位置与提醒选项。 迁移保留旧 Schedule 作为审计来源;新门面只写新模型。重复占用同一时间点的旧行无法同时成为一节课, diff --git a/packages/desktop-client/src/island/ComponentNode.vue b/packages/desktop-client/src/island/ComponentNode.vue index 22aed04..a76652b 100644 --- a/packages/desktop-client/src/island/ComponentNode.vue +++ b/packages/desktop-client/src/island/ComponentNode.vue @@ -17,6 +17,11 @@ const props = defineProps<{ parentHasWidth?: boolean; }>(); const emit = defineEmits<{ select: [id: string]; drag: [event: PointerEvent, id: string] }>(); +function select(event: MouseEvent) { + if (!props.editing) return; + event.stopPropagation(); + emit('select', props.node.id); +} const container = computed(() => ['group', 'stack', 'slide', 'rolling'].includes(props.node.kind)); const current = computed(() => { const d = new Date(props.now), @@ -101,7 +106,7 @@ const style = computed(() => { width: n.advanced.width ? `${n.advanced.width}px` : undefined, maxWidth: n.advanced.maxWidth ? `${n.advanced.maxWidth}px` : undefined, alignSelf: !props.parentHasWidth - ? 'stretch' + ? undefined : ({ stretch: 'stretch', start: 'flex-start', center: 'center', end: 'flex-end' } as const)[ n.advanced.align ], @@ -114,8 +119,8 @@ const style = computed(() => { '--emphasized': override ? `${a.emphasizedFontSize}px` : undefined, '--large': override ? `${a.largeFontSize}px` : undefined, '--rolling-duration': `${Math.max(1, 200 / n.settings.speed)}s`, - flexDirection: n.settings.direction === 'vertical' ? 'column' : 'row', - gap: `${n.settings.gap}px`, + flexDirection: container.value ? (n.settings.direction === 'vertical' ? 'column' : 'row') : undefined, + gap: container.value ? `${n.settings.gap}px` : undefined, display: container.value ? 'flex' : undefined, }; }); @@ -127,7 +132,7 @@ const style = computed(() => { :class="[{ selected: editing && selectedId === node.id, container, 'node-editing': editing }, node.kind]" :style="style" :data-node="node.id" - @click.stop="editing && emit('select', node.id)" + @click="select" > @@ -584,7 +715,7 @@ onUnmounted(() => { class="wide-button" :disabled="activeRow.main" @click=" - scheme.rows.forEach((r) => (r.main = r.id === activeRow.id)); + rows.forEach((r) => (r.main = r.id === activeRow.id)); save(); " > @@ -593,14 +724,12 @@ onUnmounted(() => {

提醒优先显示在主要行内;若该行禁用提醒,则显示在下一条允许提醒的行内。

- + +

当前方案

@@ -641,20 +810,38 @@ onUnmounted(() => {

多行布局