# 组件扩展
IDE表单开发页面组件可使用组件开发框架进行自定义开发。
# 流程说明
# 配置说明
层次结构如下
# 1.设计组件页面
主要分为三个大部分:组件选择区,表单编辑区,组件配置区
上传组件后,打开组件选择区的扩展组件界面,拖拽对应扩展组件到表单编辑区,添加成功后可看到扩展组件页面
# 2.属性面板(组件配置区)
主要分为两部分:通用组件配置区和扩展组件配置区
通用组件配置区
- 基础:进行基础组件配置,如绑定字段、标题展示
- 验证:设置组件绑定值的校验
- 高级:进行组件扩展配置,如事件绑定,配置扩展属性
- 样式:调整组件基本样式
扩展组件配置区
- 扩展:扩展组件私有配置项
可在config.js中extConfig下配置组件单独使用的参数
# 3.设置状态
在表单编辑区中,鼠标悬停在组件上,右上角会出现配置按钮,点击设置状态图标进行状态配置
# 4.PC端配置
视图默认展示绑定的数据,如需设置特定展示内容,如图片,需要在primeton-form-designer项目src/views/index/base.js中单独配置
# 5.移动端配置
移动端配置同PC端一致,不同点为开发文件为mobile.vue,并且默认使用vant ui
# 5.子表格配置
单独配置子表格时可以使用isInChildren进行判断
props: {
isInChildren:{
default:false,
},
}
# 6.组件数据
组件内部可直接使用参数如下
props: {
value:{
type: String | Number | Object | Array,
},
mode: { // 编辑态: edit, 查看态:view , 用于在子表格内切换状态
default: 'edit'
},
entityModel:{ // 如果组件绑定了一个实体, 则该属性会传入实体的数据
default:()=>{}
},
readonly: {
default:false
},
disabled: {
default:false
},
settings: {
type: Object,
default: () => {},
},
icon:'',
viewId: {},
viewCode: {
default: "view",
},
},
# 开发基础
# 1.创建开发项目
- build/mfp.config.js配置打包文件的名字
注意appName唯一不可重复
src/components 此文件下新建扩展组件文件夹
扩展组件文件夹下结构
- config.vue 属性配置页面
- config.js 组件模型。PC端和移动端均可使用,通过props:['activeData']使用
- pc.vue pc端渲染
- mobile.vue 移动端渲染
src/doc 组件说明文档,文档名同组件config.vue中tag。注意:请确保命名不会重复!
src/icons/svg 组件图标,图标名同组件config.vue中tagIcon。注意:请确保命名不会重复!
# 2.运行方法
- 安装依赖 npm install
- 运行项目 npm run dev
- 打包项目 npm run build:prod:zip
# 3.开发调试
在组件库上传扩展组件基础开发的zip文件,确保组件库存在扩展组件信息
修改webpack.dev.js中publicPath,publicPath与mfp.config.js中appName一致
- 使用代理(如nginx)本地扩展组件项目,代理地址为build/mfp.config.js中appName
location /pmSign{
proxy_pass http://localhost:8080;
}
- 修改build\plugins\components-plugin.js
window:
class ComponentsPlugin {
apply(compiler) {
compiler.hooks.emit.tap("Components Plugin", (compilation) => {
const outputPath = compiler.path || compilation.options.output.path;
const jsonFile = outputPath + "\\components.json";
const content = JSON.stringify(configs);
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath, { recursive: true });
}
fs.writeFileSync(jsonFile, content, {
encoding: "utf8",
flag: "w",
});
});
}
}
mac:
class ComponentsPlugin {
apply(compiler) {
compiler.hooks.emit.tap("Components Plugin", (compilation) => {
const outputPath = compiler.path || compilation.options.output.path;
const jsonFile = outputPath + "/components.json";
const content = JSON.stringify(configs);
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath, { recursive: true });
}
fs.writeFileSync(jsonFile, content, {
encoding: "utf8",
flag: "w",
});
});
}
}
- 打开调试开关,打开后需刷新网页
# 开发一个基础input组件
# 1.进行组件基本配置
pc.vue
<template>
<div>开发一个基础input组件</div>
</template>
<script>
export default {
name: "myInput",// 与config.js中tag一致
props: {
value:{
type: String | Number | Object | Array,
},
mode: { // 编辑态: edit, 查看态:view , 用于在子表格内切换状态
default: 'edit'
},
entityModel:{ // 如果组件绑定了一个实体, 则该属性会传入实体的数据
default:()=>{}
},
readonly: {
default:false
},
disabled: {
default:false
},
settings: {
type: Object,
default: () => {},
},
icon:'',
viewId: {},
viewCode: {
default: "view",
},
},
};
</script>
mobile.vue
<template>
<div>开发一个基础input组件(移动版)</div>
</template>
<script>
export default {
name: "myInput",// 与config.js中tag一致
props: {
value:{
type: String | Number | Object | Array,
},
mode: { // 编辑态: edit, 查看态:view , 用于在子表格内切换状态
default: 'edit'
},
entityModel:{ // 如果组件绑定了一个实体, 则该属性会传入实体的数据
default:()=>{}
},
readonly: {
default:false
},
disabled: {
default:false
},
settings: {
type: Object,
default: () => {},
},
icon:'',
viewId: {},
viewCode: {
default: "view",
},
activeData: {},// 扩展属性传值
},
};
</script>
config.vue
<template>
<div>
<el-form-item label="扩展信息配置">
<el-input v-model="activeData.extConfig.text"></el-input>
</el-form-item>
</div>
</template>
<script>
export default {
name:'myInput-config',
props:['activeData'],
data(){
return {
}
},
computed:{
},
watch:{
},
methods: {
},
}
</script>
config.js
module.exports = {
__config__: {
label: 'myInput',
tag: 'myInput',
tagIcon: 'myInput',
defaultValue: { value: null, code: '', type: 'const', prop: '' },
span: 24,
showLabel: true,
labelWidth: null,
layout: 'defaultItem', // defaultItem-默认布局,同系统中布局组件配置一致,colFormItem-Form布局,同系统中高级组件配置一致
required: true,
regList: [],
},
style: {width: '100%'},
tip: '',
disabled: false,
extConfig: {
text:''
// 所有扩展属性都写在这里
},
}
mfp.config.js
const devMode = process.env.NODE_ENV === 'development';
const appName = 'myExample';//微前端应用名称
module.exports = {
name: appName,
shared:{
vue: { singleton: true },
},
filename: `remoteEntry.js`,//微应用入口,不建议修改
exposes: { // 对外暴露的模块(页面)
'./mobile_entry': './src/mobile_config.js',// 配置模块
'./entry': './src/config.js',// 配置模块
'./store': './src/store/index.js', // 默认将子应用 store 导出,供base应用注册
},
remotes: {
base: 'base@/remoteEntry.js'
},
}
说明文档
组件图标
# 2.第一次打包
运行build:prod:zip,获得myExample.zip
# 3.上传组件
打包后的zip文件上传afcenter 的组件库
添加组件,测试组件是否添加成功
# 4.本地调试
webpack.dev.js
devServer: {
open: false,
hot: true,
port: 9006,
proxy: {
// 如果开发环境中有跨域问题,在这里配置代理
},
headers:{
'Access-Control-Allow-Origin':'*'
},
devMiddleware: {
index: true,
mimeTypes: { phtml: 'text/html' },
publicPath: '/myExample', //开发期增加上下文路径
serverSideRender: true,
writeToDisk: false,
},
client: {
logging: 'error',
overlay: {
errors: true,
warnings: false
}
},
// stats: "minimal",
},
nginx
location /myExample{
proxy_pass http://localhost:9006;
}
运行本地文件
打开本地调试,刷新页面,检查是否成功连接本地组件扩展项目
成功连接本地组件扩展项目后,可进行本地开发。本地项目更改保存后会实时更新到表单。
完整代码
// pc.vue
<template>
<div>
<el-input v-model="value" placeholder="请输入内容"></el-input>
</div>
</template>
<script>
export default {
name: "myInput",
props: {
value:{
type: String | Number | Object | Array,
},
mode: { // 编辑态: edit, 查看态:view , 用于在子表格内切换状态
default: 'edit'
},
entityModel:{ // 如果组件绑定了一个实体, 则该属性会传入实体的数据
default:()=>{}
},
readonly: {
default:false
},
disabled: {
default:false
},
settings: {
type: Object,
default: () => {},
},
icon:'',
viewId: {},
viewCode: {
default: "view",
},
},
};
</script>
// mobile.vue
<template>
<div>
<van-field v-model="value" label="" placeholder="请输入用户名(移动端)" />
</div>
</template>
<script>
export default {
name: "myInput",
props: {
value:{
type: String | Number | Object | Array,
},
mode: { // 编辑态: edit, 查看态:view , 用于在子表格内切换状态
default: 'edit'
},
entityModel:{ // 如果组件绑定了一个实体, 则该属性会传入实体的数据
default:()=>{}
},
readonly: {
default:false
},
disabled: {
default:false
},
settings: {
type: Object,
default: () => {},
},
icon:'',
viewId: {},
viewCode: {
default: "view",
},
},
};
</script>
// config.js
module.exports = {
__config__: {
label: 'myInput',
tag: 'myInput',
tagIcon: 'myInput',
defaultValue: { value: null, code: '', type: 'const', prop: '' },
span: 24,
showLabel: true,
labelWidth: null,
layout: 'colFormItem', // defaultItem-默认布局,同系统中布局组件配置一致,colFormItem-Form布局,同系统中高级组件配置一致
required: true,
regList: [],
// viewId: null, //视图配置项
// viewCode: 'view', //视图配置项
},
style: { width: '100%' },
tip: '',
disabled: false,
extConfig: {
// 所有扩展属性都写在这里
},
}
# 5.最终打包
运行build:prod:zip
# 6.上传调试后组件
上传最新的组件zip文件
# 7.验证组件
关掉本地调试,查看组件是否正常运行
重新上传后,组件未自动更新,请在扩展组件配置界面删除旧组件,重新添加组件
PC端
移动端
- 如修改版本后,组件未更新,请删除所有数据重新配置
# 扩展一个三方组件
以引用高德地图为例。
# 1.安装官方提供的loader
npm install @amap/amap-jsapi-loader --save
# 2.基本配置
需要在前端介质 app-config.json 中配置
"amap": {
"key": "",
"securityJsCode": "",
"webServerKey": ""
}
变量说明:
- key:高德地址开放平台的https://console.amap.com/ 中我的应用中配置 Web 端(JS API)的 key
- securityJsCode:我的应用中配置 Web 端(JS API)的安全密钥
- webServerKey:我的应用中配置 Web 服务的 Key
# 3.代码示例
<template>
<div class="amap">
<div id="container"></div>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
export default {
name: "amap",
data() {
return {
map: null
};
},
mounted() {
this.initAMap();
},
methods: {
initAMap() {
window._AMapSecurityConfig = {
securityJsCode: 'jscode' // 输入你的jscode
}
AMapLoader.load({
key: "xxx", //设置您的key
version: "2.0", // 高德地图版本
plugins: ["AMap.ToolBar", "AMap.Driving"], // 插件
AMapUI: {
// 高德地图UI组件库,可不写,内部提供了覆盖物标注点,以及部分模块的封装
version: "1.1",
plugins: []
},
Loca: {
// Loca版本(高性能地图数据可视化库) 可不写
version: "2.0"
}
})
.then(AMap => {
// container渲染的id
this.map = new AMap.Map("container", {
zoom: 5, // 当前缩放级别
zooms: [2, 22], // 缩放级别范围
center: [105.602725, 37.076636] // 中心点
});
})
.catch(e => {
console.log(e);
});
}
}
};
</script>
<style lang="scss" scoped>
.amap {
padding: 0px;
margin: 0px;
width: 100%;
height: 100%;
position: relative;
}
#container {
padding: 0px;
margin: 0px;
width: 100%;
height: 300px;
position: absolute;
}
</style>
# 3.页面展示
# 基于组件扩展
基于日历组件扩展一个可以添加日程的功能
效果如图:
# 创建一个添加日程的表单和视图
- 在表单设置处新建参数page_date,并赋给日期组件的默认值
- 在视图下,给视图设置新建参数view_date,并在动作设置处的添加新增按钮添加参数page_date
- 在视图的快速设置处,添加日期删选,默认值为参数view_date
- 新建一个全量视图(方便后续日历组件的单元格标记渲染)
# 日历组件+资源容器,资源容器引用日程的视图
新建表单,拖动一个日历组件和两个资源容器组件(样式隐藏全量视图);
资源容器分别绑定默认视图(field_23703160)和 全量视图(field_56598840)
新建变量v_date
给日历组件添加点击事件,代码如下:(点击日历,表格查询)
this.v_date = data.day
this.Api.getFrameAsync('field_23703160').then(el => {
el.Api.doQuery()
})
- 给默认视图传参,在扩展属性处添加view_date,如下图:
- 在表单设置处添加表单加载前事件(表单初始化时加载数据),代码如下
this.Api.getFrameAsync('field_23703160').then(el => {
el.Api.doQuery()
})
this.Api.getFrameAsync('field_56598840').then(el => {
el.Api.doQuery()
})
- 自定义渲染日历组件,在日历的自定义渲染模板处添加代码如下
<div slot-scope="{data}">
<p style="margin-bottom: 0;">{{ data.day.split('-')[2] }}</p>
<span v-if="(formData.field_56598840 ||[]).map(f => f.date).indexOf(data.day) !== -1" style="display: inline-block;width: 8px;height:8px;border-radius: 4px;background-color: #ccc;"></span>
</div>