# 小屏组件开发手册
## 1. 组件创建标准化流程 (CI/CD)
**非常重要:** 创建新组件时,**必须**首先使用脚手架命令生成基础模板,严禁手动创建文件夹。
### 1.1 自动化创建命令
在项目根目录下运行以下命令:
```bash
npm run ioc create [ComponentName]
```
- `[ComponentName]`: 组件的英文名称,必须使用 **大驼峰命名法** (PascalCase),例如 `ProductList` 或 `UserInfo`。
- 此命令会自动生成标准的目录结构、基础配置文件 (`base.js`, `config.js`) 以及必要的插件引用。
### 1.2 开发步骤
1. **运行命令**:`npm run ioc create MyNewComponent`
2. **验证生成**:检查 `components/card_components/MyNewComponent` 目录是否生成。
3. **AI 辅助生成**:使用本文档底部的 **自动化生成 Prompt**,结合具体业务需求,让 AI 填充和完善组件的 `config.js` 配置、`mock/data.js` 数据以及 `index.vue` 业务逻辑。
---
## 2. 组件核心目录结构
```text
ComponentName/ # 组件根目录
├── css/ # 样式目录
│ └── index.scss # 样式源文件(逻辑样式分离)
├── js/ # 逻辑配置目录
│ ├── base.js # 基础信息(自动生成,勿改 module_name)
│ └── config.js # 低码配置(属性面板、交互事件)
├── mock/ # 数据模拟目录
│ └── data.js # 组件Mock数据
├── plugin/ # 插件目录 (自动生成)
│ ├── eventgenerate/ # 事件生成机制
│ └── boxoptions/ # 通用容器配置
├── index.js # 注册入口(自动生成)
└── index.vue # 组件Vue主文件
```
---
## 3. 配置文件详解 (js/config.js)
`config.js` 定义了组件在低码设计器中的外观和行为。主要包含 `options` (属性设置) 和 `interaction` (交互设置)。
### 3.1 常用配置控件类型 (Configuration Types)
在 `configuration` -> `options` 数组中定义属性。以下是常用控件类型参考(具体以平台支持为准):
| 类型 (type) | 描述 | 示例代码 |
| :-------------- | :---------------- | :-------------------------------------------------------------------------------------------------------- |
| **text** | 普通文本输入框 | `{ name: 'title', displayName: '标题', type: 'text', value: '默认值' }` |
| **boolean** | 开关 (True/False) | `{ name: 'showIcon', displayName: '显示图标', type: 'boolean', value: true }` |
| **color** | 颜色选择器 | `{ name: 'bgColor', displayName: '背景色', type: 'color', value: '#ffffff' }` |
| **select** | 下拉选择框 | `{ name: 'align', type: 'select', options: [{name: '左', value: 'left'}, {name: '右', value: 'right'}] }` |
| **number** | 数字输入框 | `{ name: 'limit', displayName: '数量限制', type: 'number', value: 10 }` |
| **image** | 图片URL输入 | `{ name: 'bgImg', displayName: '背景图', type: 'image', value: '' }` |
| **uploadimage** | 图片上传控件 | `{ name: 'bgurl', displayName: '背景图片', type: 'uploadimage', value: '' }` |
| **boxmodel** | 盒子模型(边距) | 引用插件 `boxOptions`,通常作为通用配置引入。 |
| **array** | 数组/列表配置 | 用于配置列表项,需配合 `template` 和 `dynamic: true` 使用。 |
**uploadimage 图片上传配置示例(参考 Lunbo 组件):**
```javascript
{
displayName: '轮播图配置',
name: 'lunboSetting',
type: 'array',
dynamic: true,
tip: '轮播图配置',
value: [
{
displayName: '轮播图一',
value: [
{
name: 'imgOptions',
displayName: '图片设置',
value: [
{
name: 'bgurl',
displayName: '选中',
value: '',
type: 'uploadimage', // 图片上传控件
tip: '目前支持jpg、png、svg格式'
},
{
displayName: '图片高度',
name: 'height',
value: '15px',
type: 'text'
}
]
}
]
}
],
template: { /* ... 与 value 结构一致 */ }
}
```
**uploadimage 图片路径处理(prefix 方法):**
使用 `uploadimage` 类型时,需要在组件中添加 `prefix` 计算属性来处理图片路径:
```javascript
computed: {
// 图片地址处理
imgurl(index) {
// 优先使用全局配置的图片源
if (Config && Config.envconfig && Config.envconfig.imgsrc) {
return Config.envconfig.imgsrc + this.lunboSetting[index].imgOptions.bgurl || '';
}
// 使用 prefix 拼接路径
return this.prefix + this.lunboSetting[index].imgOptions.bgurl || '';
},
// 图片路径前缀(根据环境区分)
prefix() {
const origin = location.origin;
let path = location.pathname;
if (path.indexOf('/') === 0) {
path = path.substring(1);
}
const basePath = '/' + path.split('/')[0];
if (!this.isTest()) {
return origin + basePath + '/'; // 正式地址
} else {
return 'http://218.4.136.120:8990/smallscreen-demo/'; // 测试地址
}
}
},
methods: {
isTest() {
let href = location.href;
if (href.startsWith('https:') || href.startsWith('http://app.epoint.com.cn')) {
return false;
}
if (href.includes('smallscreen-demo') || href.startsWith('http://localhost')) {
return true;
}
return false;
}
}
```
**数组配置示例 (Array Type):**
```javascript
{
name: 'tabList',
displayName: 'Tab列表',
type: 'array',
dynamic: true, // 允许动态增删
value: [],
template: {
name: 'tabItem',
displayName: 'Tab项',
value: [
{ name: 'label', displayName: '名称', type: 'text', value: 'Tab1' },
{ name: 'id', displayName: '值', type: 'text', value: '1' }
]
}
}
```
### 3.2 配置项在组件中的使用 (Configuration Usage)
**参考来源:** [关联配置项与样式处理](https://app.epoint.com.cn/h5/ecode/#/docdetail/development/a1-b1-c1-h6Ql) | [参考示例:GoodsSimilar/index.vue]
在 `index.vue` 中,配置数据通过 `props.config.options` 传入。为了主数据的健壮性并适配不同平台,**必须**使用 `computed` 属性对配置进行二次处理,严禁直接在模板中使用 `config.options`。
**核心原则:**
1. **防御性编程**:假定配置项可能为空,始终提供默认值。
2. **单位标准化**:设计器中输入的数值通常不带单位,需统一转换为 `px`。
3. **样式封装**:将样式逻辑封装在 `computed` 中,模板中仅绑定结果。
**参考代码 (基于 GoodsSimilar/index.vue 最佳实践):**
```javascript
computed: {
// 1. 获取基础配置对象(赋予空对象默认值,防止报错)
layoutConfig() {
// 使用可选链 (?.) 安全访问顶层配置
const config = this.config?.options?.layoutConfig || {};
return {
// 基本类型直接取值,并设默认值
itemsPerRow: config.itemsPerRow || 3,
cardSpacing: config.cardSpacing || 10,
// 逻辑处理
showTitle: config.showTitle !== false
};
},
// 2. 样式对象处理 (Style Processing)
titleStyle() {
// 获取嵌套的样式配置
const style = this.config?.options?.cardStyle?.titleStyle || {};
// 返回 Vue 标准样式对象
return {
// 自动补齐单位
fontSize: `${style.fontSize || 14}px`,
color: style.color || '#333333',
// 特殊样式处理
fontWeight: style.bold ? 'bold' : 'normal',
// 透传 Webkit 属性
WebkitLineClamp: style.maxLines || 2
};
},
// 3. 容器样式处理 (Box Model)
boxOptions() {
const { boxOptions } = this.config.options;
const boxmodel = boxOptions.boxmodel || {};
// 遍历处理所有数值类型的边距
Object.keys(boxmodel).forEach((key) => {
if (!isNaN(Number(boxmodel[key]))) {
boxmodel[key] = boxmodel[key] + 'px';
}
});
return boxOptions;
}
}
```
**补充:样式兼容性处理 (Style Compatibility)**
虽然 Vue 支持绑定 Style 对象,但在某些特定低码运行环境(如部分小程序转化场景)中,可能需要将样式对象扁平化为字符串。若遇到样式不生效的情况,可参考以下工具函数进行转换:
```javascript
// 将样式对象转换为 CSS 字符串 (如: "color:red;font-size:14px")
function styleObj2styleStr(styleObj) {
let str = "";
for (let key in styleObj) {
str += ";" + key + ":" + styleObj[key];
}
// 去除首部分号,并将驼峰转为短横线 (camelToKebab)
return str
.replace(";", "")
.replace(/([A-Z])/g, "-$1")
.toLowerCase();
}
```
---
## 4. 事件系统详解 (Event System)
组件必须通过 `plugin/eventgenerate` 提供的机制与外部交互。所有事件定义在 `js/config.js` 的 `interaction.value` 中。
### 4.1 内置标准事件
以下事件建议在所有组件中标准化实现:
| 事件名 (name) | 显示名 (displayName) | 触发时机 | 参数规范 |
| :------------ | :------------------- | :------------------------------ | :--------------------------------- |
| **onMounted** | 加载完成 | 组件 `mounted` 生命周期钩子中 | 通常传 '自定义参数' 或组件初始状态 |
| **onClick** | 点击事件 | 组件被点击时 (通常在容器根元素) | 传组件的完整数据对象 `myData` |
| **onChange** | 值变动 | 组件内部状态/输入值改变时 | 传改变后的新值 |
### 4.2 自定义事件开发流程
1. **定义 (config.js)**:
在 `interaction` -> `event` 数组中添加自定义事件配置。
```javascript
{
name: 'onDeliveryClick', // 事件唯一标识
displayName: '配送地址点击', // 设计器显示名称
type: 'array',
dynamic: true,
value: [],
template: {
name: 'action',
displayName: '动作',
type: 'text',
value: 'console.log("配送点击", e)'
}
}
```
2. **触发 (index.vue)**:
在业务逻辑中调用 `this.eventGenerate`。
```javascript
methods: {
handleDelivery() {
// 参数1:事件名(必须与config.js中name一致)
// 参数2:传递给低码平台的参数对象
this.eventGenerate('onDeliveryClick', {
type: 'delivery',
addressId: this.address.id
});
}
}
```
---
## 5. 核心开发规范
### 5.1 index.vue 规范
- **根类名**: 必须包含 `epoint-component`。
- **Mixin**: 必须混入 `import eventMixin from './plugin/eventgenerate'`.
- **Props**: 必须接收 `config` (配置), `data` (数据), `cdata` (父子联动)。
- **Computed**:
- `boxOptions`: 处理容器样式的单位(如将 `10` 转为 `10px`)。
- `myData`: 统一处理 Mock 数据和父级联动数据 (`pdata`) 的优先级。
- **Hooks**: 必须实现 `_getConfig` 和 `_getMockData` 静态方法。
### 5.2 样式规范
- 推荐使用 **SCSS**。
- 由于组件可能运行在小程序环境,**严禁**使用 `document`, `window` 等浏览器特有对象操作 DOM(但在 `mounted` 中仅用于 H5 环境的逻辑除外)。
- 样式文件建议通过 `@import './css/index.scss';` 引入,保持 `vue` 文件整洁。
---
## 6. 自动化组件生成提示词 (Prompt)
**使用说明:**
1. 先运行 `npm run ioc create [ComponentName]` 生成基础模板。
2. 复制以下 Prompt,替换 `[组件中文名]` 和 `[组件需求描述]`。
3. 发送给 AI 助手,让其生成具体的业务逻辑代码。
### 自动化生成 Prompt 模板
````markdown
请你作为一个资深的前端开发专家,基于 M8 低码开发平台的规范,为我完善一个刚刚通过脚手架生成的 Vue 组件。
### 任务信息
**组件名称(目录名)**: [ComponentName] (请确保与 npm run ioc create 的名称一致)
**组件中文名称**: [组件中文名]
**功能描述**: [详细描述组件的功能、UI 布局、交互逻辑]
### 已有环境
我已经通过 `npm run ioc create [ComponentName]` 生成了基础目录结构。请你在此基础上,重写或完善具体的逻辑代码。
### 核心文件修改要求
**1. js/config.js (配置定义)**:
- 保持 `boxOptions` 不变。
- 根据功能描述,添加 `options` 配置项(如:标题文字(text)、颜色配置(color)、显示开关(boolean)、列表配置(array)等)。
- 在 `interaction` 中定义标准事件 (`onMounted`, `onClick`) 和业务自定义事件 (如 `onItemClick`, `onTabChange`)。
**2. mock/data.js (模拟数据)**:
- 构造真实感的 Mock 数据,数据结构需与 UI 展示一一对应。
- **必须**遵循以下格式,`data` 外层数组容器勿删除:
```javascript
export default {
data: [
// 这一层勿删,组件通过 props.data 接收此数组
{
name: "立项",
status: "已完成",
},
{
name: "土地手续",
status: "未完成",
},
// ... 更多数据项
],
};
```
````
**3. index.vue (核心逻辑)**:
- 完善 `<template>`:绑定 `config` 中的样式配置,渲染 `myData` 中的数据。
- 完善 `<script>`:
- 确保引用了 `./js/config` 和 `./mock/data`。
- **重要**: 完善 `myData` 计算属性,处理 `pdata` (父级联动) 和 `mockData` 的优先级。
- **重要**: 实现 `boxOptions` 计算属性,遍历 `boxmodel` 为数值添加 `px` 单位。
- 实现交互方法,通过 `this.eventGenerate('事件名', 参数)` 触发配置中定义的事件。
- 完善 `<style>`:编写精美的 SCSS 样式,注意响应式布局。
### 请输出代码
请依次输出以下文件的完整代码(覆盖原有内容):
1. `[ComponentName]/js/config.js`
2. `[ComponentName]/mock/data.js`
3. `[ComponentName]/css/index.scss`
4. `[ComponentName]/index.vue`
```
```