You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## index.js核心逻辑
整个逻辑可以分为以下几个主要部分:
1. 全局结构和初始化
代码被包裹在一个立即执行函数表达式 (function () { ... })(); 中,这样做可以创建一个独立的作用域,避免污染全局命名空间。
- 核心对象:
- main 和 welcome: 这两个对象分别存储了“主画布”和“欢迎画布”的状态,包括它们的 grid 实例和 initData布局数据
- currentComponent: 一个全局变量,用于存放当前被选中的组件或画布对象。
- `init(type, self)` 函数 (初始化函数): 这是整个应用启动的入口点。
1. 加载数据: 尝试从 localStorage 中读取名为 ${type}Data (例如 mainData 或 welcomeData) 的数据。如果存在,就解析它;如果不存在,就创建一套默认的画布配置。
2. 坐标转换: 在加载现有数据后,会调用 conputedInitData('init', self)这个关键函数会将存储的像素坐标x, y, w, h转换回 GridStack 能理解的栅格单位(行、列)。
3. 初始化GridStack: 使用 GridStack.init() 来初始化一个网格布局的容器。GridStack.js 是一个强大的库,它负责处理组件的拖拽、缩放、碰撞检测等核心交互功能。这里配置了行高、边距、是否接受拖入组件等。
4. 渲染初始组件: GridStack.init() 时传入的 children 数组(来自 initData会被自动渲染到画布上。
5. 绑定事件:
- 遍历所有已存在的组件,为它们调用 handleAddComponent 函数,以附加点击事件和设置初始视图。
- 监听 grid 的 added 事件:当一个新组件被拖入时触发,同样调用 handleAddComponent。
- 监听 grid 的 removed 事件:当一个组件被删除时触发 handleRemoveComponent。
2. 组件处理逻辑
- `GridStack.setupDragIn(...)`: 这段代码定义了左侧“组件列表”中的可拖拽项。它为“图片”和“文本”这两种组件预设了默认的属性如尺寸、类型、名称、背景色等。当用户把它们拖到画布上时GridStack 就会使用这些预设值创建一个新的组件实例。
- `handleAddComponent(component, self)` 函数:
1. 分配ID: 如果组件没有ID则使用 generateUniqueId() 生成一个唯一ID。
2. 更新视图: 调用 setComponentView(component),根据组件的属性(如背景色、图片路径、文字内容等)来渲染其实际外观。
3. 绑定点击事件: 为每个组件的DOM元素添加一个点击事件监听器。
- 当组件被点击时,首先会清除上一个选中组件的“聚焦样式”(通过 clearOldFocusStyle
- 将 currentComponent 变量更新为当前点击的组件。
- 调用 setCurrentComponentProps(currentComponent),将该组件的属性填充到右侧的“属性面板”中。
- 调用 setCurrentFocusStyle(),为当前组件添加“聚焦样式”(例如,改变边框、背景或应用 transform: scale
3. 属性面板交互
右侧的属性面板是用户配置组件和画布的地方。
- `setCurrentComponentProps(component)` 函数: 这是一个核心的UI更新函数。
- 它接收一个组件(或画布)对象。
- 根据对象的类型和拥有的属性hasOwnProperty动态地显示或隐藏表单中的各个输入框如名称、背景色、图片URL、字体大小等
- 将组件的当前值填充到对应的输入框中。例如,文本组件会显示字体大小、颜色等输入框,而图片组件则不会。
- `bindComponentEvents()` 函数: 这个函数为属性面板中的所有输入框绑定了事件。
- 它使用一个 elementMappings 对象来定义每个输入框ID、它应该监听的事件blur 或 change以及它对应的组件属性名。
- 当用户修改了某个输入框的值并触发事件(如输入框失去焦点)时,会执行一个回调:
1. 获取输入框的新值。
2. 更新 currentComponent 对象上对应的属性。
3. 调用 setComponentView(currentComponent, [property]),并传入被修改的属性名,从而即时更新画布上组件的视觉样式,实现“所见即所得”。
4. 数据保存与坐标计算
- `handleSaveClick()` 函数:
- 当点击“保存”按钮时,它会分别调用 main.grid.save() 和 welcome.grid.save()。grid.save() 是 GridStack 的一个方法,它会返回一个描述所有子组件布局信息的数组(包含 x, y, w, h 等栅格单位的坐标)。
- 将获取到的组件数组存回 main.initData.children 和 welcome.initData.children。
- 调用 conputedInitData('save', ...)。
- `conputedInitData(type, self)` 函数: 这个函数是数据持久化的关键。
- 保存时 (`type === 'save'`): 它遍历所有组件,将 GridStack 提供的栅格坐标(如 x: 1, w: 2乘以一个固定的缩放因子如画布宽度 1920 / 12将其转换成像素单位的坐标。这样做是为了让保存的数据与具体的 GridStack 配置解耦。
- 初始化时 (`type === 'init'`): 它执行相反的操作,将之前保存的像素坐标除以缩放因子,还原成 GridStack 能识别的栅格坐标。
5. 其他功能
- `handleTabSwitch()`: 处理“主画布”和“欢迎画布”之间的切换,通过控制 display 样式来显示或隐藏对应的画布。
- `setComponentView()`: 使用了“策略模式”componentStrategies 对象),为不同的组件属性(如 background, image, fontSize定义了专门的DOM操作函数使代码更清晰、易于扩展。
- 焦点样式: setCurrentFocusStyle 和 clearOldFocusStyle 两个函数配合,实现了选中组件时的高亮视觉效果。
总结
整个应用的运行流程如下:
1. 启动: 页面加载后init() 函数被调用,分别初始化 main 和 welcome 两个画布。
2. 加载/创建: init() 从 localStorage 加载布局数据,或创建新布局,并使用 GridStack.js 渲染出来。
3. 交互:
- 用户可以从左侧拖动新组件到画布上。
- 用户可以点击画布上的任一组件,此时该组件被“激活”。
4. 配置:
- 组件被激活后,右侧属性面板会显示其所有可配置属性。
- 用户在属性面板中修改值,所做的更改会实时反映在画布的组件上。
5. 保存:
- 用户点击“保存”按钮。
- 当前两个画布的布局(使用栅格坐标)被 GridStack 导出。
- 坐标被转换为像素单位。
- 最终的布局数据包含像素坐标被序列化成JSON字符串存储到 localStorage 中。
下次用户打开页面时流程会从第1步重新开始但这次会从 localStorage 加载上次保存的数据,从而恢复之前的布局状态。
## 历史任务
* 在index.js中conputedInitData函数当type为save的时候帮我处理带有下划线的数据数据结构参考data.json和下面示例(xxx为省略结果不代表真实值)
```
mainData = {
xxx,
children: [{
"focusedStyle_background": "",
"focusedStyle_border_width": 0,
"focusedStyle_border_color": "",
"focusedStyle_scale": 1,
...这里后续还有可能扩展(例如)
"other_name": "",
}]
}
最终转换为(最终转换的结果需要保留原址类似focusedStyle_background)
mainData = {
xxx,
children: [{
focusedStyle: {
background: xxx,
border: {
width: xxx,
color: xxx
},
scale: xxx
},
...这里后续还有扩展也保持一样的转换(例如)
other: {
name: xxx
}
}]
}
```
## 历史任务
* 实现一个列表页列表页里面包含多个index.html生成的模板
* 列表页面的js文件格式请参照index.js的写法包括使用jquery进行初始化等保持代码风格一致
* 列表页面一个子项宽度为400px,从左到右依次排列,超出换行,并且保证换行后每行的每个子项左右间距相同
* 子项高度根据内容自动撑开
* 子项的数据参考data.json
```
子项布局参考
image (占满,固定高度)
name + action btn action btn 点击后弹出选项 1. 复制 2. 删除 3. 编辑 4. 应用)
date
```
* 子项数据直接使用data.json并复制内部子项10份更改id和name保证不重复
## 历史任务
- demoHtml/flex/index.html页面中还有部分国际化未完成
* 包含文字宽度事件类型事件动作默认聚焦向左侧移动组件ID 向右侧移动组件ID 向上侧移动组件ID向下侧移动组件ID 获得焦点时背景颜色,获得焦点时边框宽度,获得焦点时边框颜色,获得焦点时边框缩放大小),请参照当前页面使用国际化规则来实现
* demoHtml/flex/js/i18n.js 中国际化内容请按照原有格式添加。
## 对话记录
### 本次会话总结
- **任务**: 在 `js/index.js``conputedInitData` 函数中,当 `type` 为 'save' 时,处理带有下划线的属性(例如 `focusedStyle_background`),将其转换为嵌套对象结构(例如 `focusedStyle: { background: xxx }`),并保留原始带下划线的属性。
- **实现方式**:
1. 创建了一个名为 `transformUnderscoreData` 的辅助函数。该函数遍历给定对象的属性,如果属性名包含下划线,则根据下划线拆分属性名,并创建相应的嵌套对象。对于如 `focusedStyle_border_width``focusedStyle_border_color`,会进一步嵌套到 `focusedStyle.border` 对象下。
2.`conputedInitData` 函数的 `if (type === 'save')` 代码块中,对 `initDataCopy.children` 数组中的每个 `item` 调用 `transformUnderscoreData` 函数,将其返回的转换后的属性合并回 `item` 对象,从而实现数据结构的转换。
- **文件改动**:
- `js/index.js`: 添加 `transformUnderscoreData` 辅助函数,并修改 `conputedInitData` 函数以调用此辅助函数。
### 本次会话总结
- **任务**: 根据用户反馈,优化 `transformUnderscoreData` 辅助函数,使其更加通用。
- **优化点**:
- 旧的实现方式依赖硬编码的属性名(如 `border`)来创建嵌套对象。
- 新的实现方式通过遍历由下划线分割的属性名部分,动态地创建任意层级的嵌套对象。例如,`a_b_c` 会被正确地转换为 `{ a: { b: { c: value } } }`
- **文件改动**:
- `js/index.js`: 更新了 `transformUnderscoreData` 函数的实现逻辑,使其更加灵活和通用。
### 本次会话总结
- **任务**: 优化 `list.html` 列表页的布局和交互功能。
- **布局优化点**:
- **最后一行对齐问题**: 解决了由于 `justify-content: space-around` 导致的最后一行项目居中或分散的问题,通过将 `#list-panel``justify-content` 改为 `flex-start`,确保最后一行从左到右对齐。
- **子项宽度及排版**: 将 `.list-item` 的固定宽度 `400px` 修改为 `calc(25% - 7.5px)`实现了每行4个子项的弹性布局并合理处理了子项间的 `gap` 间距。
- **底部内边距缺失**: 解决了 `#list-panel` 底部 `padding` 显示不全的问题,通过为 `#list-panel` 添加 `width: 100%`,确保其在 flex 父容器中正确撑开。
- **交互优化点**:
- **模板名称显示**: 修复了列表项中模板名称 (`item.name`) 未显示的问题,通过在 `js/list.js` 中动态生成的 HTML 结构中添加 `<span class="list-item-name">${item.name}</span>`
- **操作按钮样式优化**: 优化了点击“...”按钮弹出的操作菜单(`.action-menu`)的样式:
-`.action-menu` 添加 `min-width: 80px;`,防止文字换行。
-`.action-menu button` 添加 `white-space: nowrap;` 禁止文字换行,将 `text-align` 改为 `center`,并增加 `transition` 效果。
- **操作按钮事件**: 为操作菜单中的四个按钮(复制、删除、编辑、应用)添加了点击事件监听,点击时会在控制台打印相应的操作名称。
- **文件改动**:
- `css/list.css`: 修改了 `#list-panel``.action-menu` 的样式,以及 `.list-item``.action-menu button` 的样式。
- `js/list.js`: 修改了 `listItem` 的 HTML 模板以显示名称,并添加了四个操作按钮的点击事件监听器。
### 本次会话总结
- **任务**: 完成 `index.html` 页面中缺失的国际化i18n配置。
- **修改内容**:
- **js/i18n.js**: 在 `zhCN``en` 配置对象中新增了 12 个字段包括文字宽度font_weight、事件类型events_type、事件动作events_action、默认聚焦default_focus、移动组件ID左/右/上/下)、获得焦点时的背景/边框宽度/边框颜色/缩放大小。
- **index.html**: 将属性面板props-panel表单中剩余的 12 处硬编码中文标签替换为模板语法 `{{= i18n.$t('user.key') }}`,使其支持中英文切换。
- **技术规范**: 遵循了项目原有的 Layui i18n 插件调用方式和命名规范。