feat: 优化模型参数页底部保存栏悬浮效果
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
# Param Save Bar Fixed Bottom Backend Implementation Plan
|
||||||
|
|
||||||
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** 评估并确认模型参数页底部保存栏悬浮需求是否需要后端配合,并输出后端执行结论。
|
||||||
|
|
||||||
|
**Architecture:** 本次需求仅涉及前端页面布局与样式调整,参数查询和保存接口、返回结构、DTO 以及持久化逻辑均不发生变化。后端实施计划以确认边界和回归验证要点为主,不引入代码修改。
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Spring Boot 3, 若依后端接口, 现有模型参数保存 API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 确认需求边界
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Review: `docs/plans/2026-03-16-param-save-bar-fixed-bottom-design.md`
|
||||||
|
- Review: `ccdi-project`
|
||||||
|
- Review: `ccdi-info-collection`
|
||||||
|
|
||||||
|
**Step 1: 阅读设计文档并确认影响面**
|
||||||
|
|
||||||
|
检查设计文档中关于“仅修改前端布局”的描述,确认没有新增字段、接口或保存时机变更。
|
||||||
|
|
||||||
|
**Step 2: 对照现有接口职责**
|
||||||
|
|
||||||
|
确认 `listAllParams` 和 `saveAllParams` 相关接口仍可满足前端使用,不需要新增响应字段或调整 DTO。
|
||||||
|
|
||||||
|
**Step 3: 记录实施结论**
|
||||||
|
|
||||||
|
结论应明确写为:后端无需改动,仅需配合前端回归验证现有保存流程。
|
||||||
|
|
||||||
|
### Task 2: 回归验证清单
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Review: `docs/plans/2026-03-16-param-save-bar-fixed-bottom-design.md`
|
||||||
|
|
||||||
|
**Step 1: 验证参数查询接口**
|
||||||
|
|
||||||
|
确保页面布局调整后,前端仍以原方式调用查询接口,接口入参与返回结构保持不变。
|
||||||
|
|
||||||
|
**Step 2: 验证批量保存接口**
|
||||||
|
|
||||||
|
确保前端仅改变按钮展示位置,不改变 `saveDTO` 结构与保存触发方式。
|
||||||
|
|
||||||
|
**Step 3: 记录无需联调变更**
|
||||||
|
|
||||||
|
在实施说明中注明:后端只需参与已有保存链路的回归确认,不需要代码发布。
|
||||||
72
docs/plans/2026-03-16-param-save-bar-fixed-bottom-design.md
Normal file
72
docs/plans/2026-03-16-param-save-bar-fixed-bottom-design.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 模型参数页底部保存栏悬浮设计
|
||||||
|
|
||||||
|
**背景**
|
||||||
|
|
||||||
|
当前“模型参数修改”相关页面中的“保存所有修改”按钮位于普通文档流末尾。参数卡片较多时,用户需要滚动到页面底部才能看到保存入口,保存操作不够直观,也容易在编辑过程中误以为没有统一保存入口。
|
||||||
|
|
||||||
|
**目标**
|
||||||
|
|
||||||
|
将“保存所有修改”区域调整为在页面滚动过程中始终保持可见的底部操作栏,覆盖以下页面:
|
||||||
|
|
||||||
|
- 项目详情中的参数配置页 `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||||
|
- 全局模型参数管理页 `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||||
|
|
||||||
|
**约束**
|
||||||
|
|
||||||
|
- 不修改参数加载、修改标记、批量保存等现有业务逻辑。
|
||||||
|
- 保持两个页面交互一致,避免同类页面体验割裂。
|
||||||
|
- 适配当前若依布局中 `AppMain` 作为滚动容器的结构。
|
||||||
|
- 兼顾桌面端与移动端显示,避免按钮区压住主要内容。
|
||||||
|
|
||||||
|
**方案选择**
|
||||||
|
|
||||||
|
## 方案一:使用 `position: sticky; bottom: 0`
|
||||||
|
|
||||||
|
将保存栏保留在页面内容流中,但通过 `sticky` 吸附在滚动容器底部。
|
||||||
|
|
||||||
|
优点:
|
||||||
|
|
||||||
|
- 与当前若依布局兼容性最好。
|
||||||
|
- 不需要手动计算侧边栏宽度、主内容区宽度和响应式偏移。
|
||||||
|
- 实际用户体验上可达到“滚动时始终可见”的效果。
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
|
||||||
|
- 严格意义上是吸附在内容滚动容器底部,而不是直接固定到 `window`。
|
||||||
|
|
||||||
|
## 方案二:使用 `position: fixed`
|
||||||
|
|
||||||
|
将保存栏直接固定到浏览器视口底部。
|
||||||
|
|
||||||
|
优点:
|
||||||
|
|
||||||
|
- 语义上最接近“固定在浏览器底部”。
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
|
||||||
|
- 需要额外处理左侧菜单宽度、固定头部、移动端安全区域和页面宽度同步。
|
||||||
|
- 在若依当前布局下更容易出现遮挡或错位。
|
||||||
|
|
||||||
|
**结论**
|
||||||
|
|
||||||
|
采用方案一。对于当前项目的页面结构,这种实现可以稳定地实现底部悬浮效果,同时将实现复杂度和回归风险控制在最低。
|
||||||
|
|
||||||
|
**界面设计**
|
||||||
|
|
||||||
|
- 保留“保存所有修改”按钮和“已修改 X 个参数”文案。
|
||||||
|
- 将按钮区改造成统一的底部操作栏。
|
||||||
|
- 操作栏使用白底、顶部边框和轻阴影,与卡片区分层。
|
||||||
|
- 桌面端使用左右分布或同一行布局。
|
||||||
|
- 移动端允许换行,确保按钮点击区域充足。
|
||||||
|
|
||||||
|
**内容区域处理**
|
||||||
|
|
||||||
|
- 页面主体增加底部留白,避免最后一张模型卡片或表格内容在滚动时被吸附栏遮挡。
|
||||||
|
- 空状态页面不显示底部保存栏。
|
||||||
|
|
||||||
|
**测试与验证**
|
||||||
|
|
||||||
|
- 验证项目详情参数配置页在多组参数卡片下滚动时保存栏持续可见。
|
||||||
|
- 验证全局模型参数页在多组参数卡片下滚动时保存栏持续可见。
|
||||||
|
- 验证移动端宽度下保存按钮与“已修改 X 个参数”文案不重叠。
|
||||||
|
- 验证未修改参数、已修改参数、保存中三种状态下按钮区表现正常。
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Param Save Bar Fixed Bottom Frontend Implementation Plan
|
||||||
|
|
||||||
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** 将模型参数页的“保存所有修改”区域调整为滚动时始终可见的底部吸附操作栏,并统一项目详情页与全局模型参数页的展示效果。
|
||||||
|
|
||||||
|
**Architecture:** 在两个现有 Vue 页面中复用同一套样式策略:保留现有保存逻辑,仅通过 `position: sticky; bottom: 0` 将按钮区吸附到滚动容器底部,同时补充页面底部留白和移动端布局适配。这样可以与若依当前的 `AppMain` 滚动容器兼容,避免 `fixed` 带来的侧边栏遮挡问题。
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 2, Element UI, SCSS, 若依前端布局
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 为项目详情参数页补充底部吸附样式
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||||
|
|
||||||
|
**Step 1: 写出预期行为检查点**
|
||||||
|
|
||||||
|
确认以下目标行为:
|
||||||
|
|
||||||
|
- 页面滚动时“保存所有修改”始终可见
|
||||||
|
- 最后一张模型卡片不会被保存栏遮挡
|
||||||
|
- 已修改计数文案继续正常展示
|
||||||
|
|
||||||
|
**Step 2: 调整保存栏结构样式**
|
||||||
|
|
||||||
|
将 `button-section` 改为底部吸附操作栏,补充:
|
||||||
|
|
||||||
|
- `position: sticky`
|
||||||
|
- `bottom: 0`
|
||||||
|
- `z-index`
|
||||||
|
- 顶部边框与阴影
|
||||||
|
- 桌面端横向排列
|
||||||
|
|
||||||
|
**Step 3: 补充容器底部留白**
|
||||||
|
|
||||||
|
在页面主容器增加足够的 `padding-bottom`,避免内容被悬浮栏遮挡。
|
||||||
|
|
||||||
|
**Step 4: 增加移动端适配**
|
||||||
|
|
||||||
|
在媒体查询中让保存栏换行显示,并让按钮在窄屏下占满宽度。
|
||||||
|
|
||||||
|
### Task 2: 为全局模型参数页应用同样的吸附方案
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||||
|
|
||||||
|
**Step 1: 复用项目详情页的布局样式策略**
|
||||||
|
|
||||||
|
保持保存栏的结构与交互一致,避免两个模型参数页体验不一致。
|
||||||
|
|
||||||
|
**Step 2: 校正全局页容器留白**
|
||||||
|
|
||||||
|
结合页面标题区和卡片区,补足底部滚动空间,确保最后一组参数完整可见。
|
||||||
|
|
||||||
|
**Step 3: 保持空状态不显示保存栏**
|
||||||
|
|
||||||
|
确认 `modelGroups.length === 0` 时仍仅展示空状态,不渲染底部操作栏。
|
||||||
|
|
||||||
|
### Task 3: 进行前端回归验证
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Review: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||||
|
- Review: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||||
|
|
||||||
|
**Step 1: 执行构建验证**
|
||||||
|
|
||||||
|
Run: `npm run build:prod`
|
||||||
|
|
||||||
|
Expected: 前端生产构建成功,样式改动未引入编译错误。
|
||||||
|
|
||||||
|
**Step 2: 手工检查两个页面**
|
||||||
|
|
||||||
|
验证点:
|
||||||
|
|
||||||
|
- 滚动时保存栏持续可见
|
||||||
|
- 修改参数后计数正常变化
|
||||||
|
- 点击保存后逻辑不受影响
|
||||||
|
- 窄屏下按钮区不重叠、不溢出
|
||||||
|
|
||||||
|
**Step 3: 记录验证结果**
|
||||||
|
|
||||||
|
在交付说明中明确区分已执行的构建验证与建议的页面人工回归项。
|
||||||
@@ -200,6 +200,7 @@ export default {
|
|||||||
.model-cards-container {
|
.model-cards-container {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
padding-bottom: 92px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-card {
|
.model-card {
|
||||||
@@ -229,15 +230,50 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button-section {
|
.button-section {
|
||||||
padding: 15px;
|
position: sticky;
|
||||||
background: #fff;
|
bottom: 0;
|
||||||
border-radius: 4px;
|
z-index: 20;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px 16px;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
border-radius: 12px 12px 0 0;
|
||||||
|
box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.08);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.modified-tip {
|
.modified-tip {
|
||||||
margin-left: 15px;
|
|
||||||
color: #909399;
|
color: #909399;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.param-config-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-cards-container {
|
||||||
|
padding-bottom: 104px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-section {
|
||||||
|
padding: 14px 16px;
|
||||||
|
padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ export default {
|
|||||||
.model-cards-container {
|
.model-cards-container {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
padding-bottom: 92px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-card {
|
.model-card {
|
||||||
@@ -233,15 +234,50 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button-section {
|
.button-section {
|
||||||
padding: 15px;
|
position: sticky;
|
||||||
background: #fff;
|
bottom: 0;
|
||||||
border-radius: 4px;
|
z-index: 20;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px 16px;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
border-radius: 12px 12px 0 0;
|
||||||
|
box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.08);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.modified-tip {
|
.modified-tip {
|
||||||
margin-left: 15px;
|
|
||||||
color: #909399;
|
color: #909399;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.param-config-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-cards-container {
|
||||||
|
padding-bottom: 104px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-section {
|
||||||
|
padding: 14px 16px;
|
||||||
|
padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user