# widget开发

这里介绍一个从搭建微前端到完成widget开发案例的大概流程。

# 开发前提

本项目的运行依赖afcenter-ui和portal-ui项目+widget-ui项目(部分组件使用到components项目),开发过程中确保项目都在运行。

# 创建微前端项目

1.安装primeton命令

npm install primeton-cli -g

2.创建微前端项目

primeton create-m-app 指令参考:

primeton create-m-app [moduleName] [debugPort]

创建微前端项目myApp,调试端口:8888

primeton create-m-app myApp 8888

运行命令后,在当前工作目录生成微前端项目结构:

# 定义参数

在微前端项目myApp中新建配置文件,eg: src\utils\widgetSetting.js

:如果开发过程中,涉及修改参数,需要重新拉取widget

export const dropConfig = {
  my_widget: { // 与widget绑定资源code一致
    code: 'my_widget',
    showStyle: 'list',
    eventLists: [], // 根据所需(0个或多个),自定义参数,
    width: 60, // widget的初始宽度,非必填(屏幕总宽分为120个单元,默认值40,60就是一半)
    height: 10, // widget的初始高度,非必填(屏幕总高分为20个单元,默认值8)
    global: [{ // 头部的横向菜单
        label: '清空',
        eventCode: 'add',
        component: 'widget_button'
    }],
    self:[{ // 头部的纵向菜单,按钮以及弹框组件
            label: '新增', // 按钮名称
            eventCode: 'add',
            icon: 'el-icon-plus',
            isDialog: true,
            component: 'widget_form' //自定义事件对应的组件;需在 /views/settingComponents/目录下写的组件name一致
        }]
  }
}

# 暴露模块

  1. 暴露配置文件
  2. 并新建配置文件中出现的组件,在build/mfp.config.js中对外暴露页面(开发过程中修改此配置文件,需要重启项目)
exposes: {
    ...,
    // 配置文件
    './settingConfig': './src/utils/widgetSetting.js',
    // widget主页面
    './my_widget': './src/views/my_widget/index.vue',
    // 横向布局组件
    './widget_button': './src/views/my_widget/widget_button.vue',
    // 弹框组件
    './widget_form': './src/views/my_widget/widget_form.vue'
}

# 配置代理

添加微前端的代理

// webpack代理
'/myApp': {
	target: 'http://localhost:8888/',
},

// nginx代理
location /myApp{
    proxy_pass http://localhost:8888/myApp;
}

# 配置页面

在运行项目的<应用管理>页面里注册资源

  • 配置微前端模块(添加并保存)

  • 添加分组

  • 添加页面

  • 页面授权(勾选中,并保存)

  • 添加门户

  • 门户授权

  • 添加widget

  • widget授权(一般添加后会自动授权,如果没有你想要的角色,可自行授权,同门户)

  • 进入门户,并拖拽出widget

# 开发调试

页面大概拆分为主页面、横向菜单和纵向菜单

要想实现数据持久化,有两种途径,一种是直接调用自备的接口;另一种是通过widget的公共方法实现数据保存。下面的场景将详细介绍后者的实现。

# 主页面实现

页面实现

在页面中,获取到的props参数只有配置中的自定义参数(eventLists: [])

<div class="memo_main">
    <el-row v-for="(event, index) in $attrs.eventLists" :key="event.id">
        <el-divider v-if="index"></el-divider>
        <el-col class="memo_item">
            <div class="memo_title">{{ event.title }}</div>
            <div class="memo_action">
                <el-button type="text" @click="remove(event)">移除</el-button>
            </div>
        </el-col>
        <el-col class="memo_item">
            <div class="mr-20">{{ event.updateTime }}</div>
            <div>{{ event.content }}</div>
        </el-col>
    </el-row>
</div>

数据持久化

实现数据持久化,通过触发updateItem方法,(这里的cloneDeep引入自lodash)

remove(event) {
    this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    })
    .then(async () => {
        let lists = cloneDeep(this.$attrs.eventLists)
        lists = lists.filter(i => i.id !== event.id)
        this.$emit('updateItem',{eventLists: lists})
    })
    .catch(() => {
    })
}

# 横向菜单(global配置)

页面头部的横向菜单,涉及global配置,整个模块是一个slot,可以在对应的组件中实现所需功能。

页面实现和数据持久化

实现数据持久化,可通过更新configOption数据实现保存

<template>
  <div>
    <el-button @click="clearAll">清空</el-button>
  </div>
</template>
<script>
export default {
  name: 'myWidgetButton',
  props: {
    configOption: {},
  },
  methods:{
    clearAll() {
      const data = {
        ...this.configOption,
        eventLists: [],
        doSave: true
      }
      this.$emit('update:configOption', data)
    }
  }
}
</script>

# 纵向菜单(self配置)

页面头部的纵向菜单,点击打开弹框。涉及self配置,例如<新增>

在self配置项的label展示为下拉菜单,菜单点击事件已在公共方法里处理过,弹框组件为配置项的component值

弹框页面实现如下

其中页面props参数:dialogVisible(弹框是否展示)、configOption(是整个widget的模型数据)

<div>
    <el-dialog title="添加备忘录" :visible.sync="dialogVisible" width="800px" append-to-body :before-close="handleClose">
        <el-form :ref="formRef" :model="form" :rules="validateRules" label-width="100px">
            <el-row>
                <el-col :span="22">
                    <el-form-item label="标题" prop="title">
                        <el-input v-model="form.title" />
                    </el-form-item>
                </el-col>
                <el-col :span="22">
                    <el-form-item label="内容" prop="content">
                        <el-input v-model="form.content" type="textarea" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <span slot="footer">
            <el-button @click="handleClose">取 消</el-button>
            <el-button type="primary" @click="submitForm">确 定</el-button>
        </span>
    </el-dialog>
</div>

弹框数据持久化如下

发公共方法,需要修改弹框的dialogVisible = false,这样在$emit('configOption')时会触发数据保存和更新

submitForm(){
	this.$refs[this.formRef].validate(async valid => {
        if (valid) {
            let data = cloneDeep(this.configOption)
            data.dialogVisible = false
            const time = new Date()
            const updateTime = time.getFullYear() + '/' + time.getMonth() + '/' + time.getDate()
            data.eventLists.push({id: `t_${Math.random()*(100000)}`,...this.form, updateTime})
            this.$emit('update:configOption', data)
        }
    })
}

# 编译部署

  • 开发完成后,打包部署。在项目根目录下,执行命令
npm run build:prod
  • 将打包生成的dist文件放置在widget-ui包同级目录下

  • 添加代理,启动项目

上次更新: 2023/7/20下午12:25:28