背景
基于之前组件库的基础上,其实,我们所有的schema配置都与ui层面没有关系,并且发现ant和element框架api大同小异,对于基于element开发出来的schema,初步核对了一下,ant上也是可行的;如下配置,完全与ui无关,与element和ant api初步核对确实可行:
const config = reactive<formConfig>({
// colNum: 6,
labelWidth: '100px',
disabled: false,
loading: false,
isExport: true,
columns: [
{
label: '业务域',
prop: 'businessDomain',
type: 'select',
required: false,
disabled: false,
options: {
type: 'api',
getData: () => {
return new Promise(async(resolve) => {
const api: MonitoringPlatform = new MonitoringPlatform()
const res = await api.monitoringPlatformConfigQueryBusinessDomain()
if (res?.success) {
resolve(res?.data.map((item: string) => ({
label: item,
value: item,
})) ?? [])
} else {
resolve([])
}
})
},
},
change: async(e: any) => {
const options = await getBusinessScenarioOptions(e.value)
// 清除业务场景&问题场景表单值
form.value.businessScenario = ''
form.value.errorType = ''
// 修改业务场景options / 启禁用
const businessScenarioItem = config.columns[getColumnIndex(config.columns, 'businessScenario')] as selectProps
businessScenarioItem.options = options
businessScenarioItem.disabled = !e.value
// 清空问题场景 option / 启禁用
const errorTypeItem = config.columns[getColumnIndex(config.columns, 'errorType')] as selectProps
errorTypeItem.options = []
errorTypeItem.disabled = !form.value.businessScenario
},
placeholder: '请选择',
},
{
label: '业务场景',
prop: 'businessScenario',
type: 'select',
required: false,
disabled: true,
options: [],
change: async(e: any) => {
// 清除问题场景表单值
form.value.errorType = ''
const options = await getErrorTypeOptions(e.value)
// 清空问题场景options
const errorTypeItem = config.columns[getColumnIndex(config.columns, 'errorType')] as selectProps
errorTypeItem.options = options
errorTypeItem.disabled = !e.value
},
placeholder: '请先选择业务域',
},
{
label: '问题场景',
prop: 'errorType',
type: 'select',
required: false,
disabled: true,
options: [],
placeholder: '请先选择业务场景',
},
{
label: '预警对象',
prop: 'alertTarget',
type: 'input',
placeholder: '请输入',
},
{
label: '重要程度',
prop: 'importance',
type: 'select',
multiple: true,
options: {
type: 'dic',
key: 'ReconciliationWarningImportantEnum',
},
labelKey: 'desc',
valueKey: 'name',
placeholder: '请选择',
},
{
label: '配置人',
prop: 'createUserName',
type: 'input',
placeholder: '请输入',
},
],
})
目标
近期:基础组件表单及表格组件通过npm包发布之后,不管是基于elementplus项目还是基础ant的项目,npm下来之后可以直接使用,并且分别兼容各自ui框架的原生api
最终:分别形成一套vue2、vue3、react技术栈的基础组件,类似于ant Design vue 和 ant Design react形式;eg:nowayForm-vue、nowayForm-vue3、nowayForm-react
现在正在实施完成nowayForm-vue3
实施
思路:通过一个全局变量的方式来动态确认使用elementplus组件还是ant组件,转换器代码如下:
/*
* @Author: 陈宇环
* @Date: 2023-06-05 15:12:47
* @LastEditTime: 2023-06-14 10:30:49
* @LastEditors: 陈宇环
* @Description:
*/
// 自定义动态组件
export class CustomDynamicComponent {
// 定义this属性的类型
[x: string]: JSX.Element | ((type: string) => JSX.Element)
// 默认element-plus样式,在window下面进行注册
static language = window.uiLanguage || 'ele'
// element-plus对比基准
static eleLanguage = 'ele'
// ant-design-vue对比基准
static antLanguage = 'ant'
// 多种ui定义字典值
static dicts: {
[x in 'ele' | 'ant']: () => {
[x in string]: JSX.Element
}
} = {
ant: () => {
return {
row: <a-row/>,
col: <a-col/>,
form: <a-form/>,
formItem: <a-form-item/>,
button: <a-button/>,
table: <a-table/>,
tableColumn: <a-table-column/>,
radio: <a-radio/>,
pagination: <a-pagination/>,
input: <a-input/>,
password: <a-input-password/>,
textarea: <a-textarea/>,
number: <a-input-number/>,
radioGroup: <a-radio-group/>,
radioButton: <a-radio-button/>,
select: <a-select/>,
selectOption: <a-select-option/>,
switch: <a-switch/>,
cascader: <a-cascader/>,
checkBox: <a-checkbox/>,
checkBoxGroup: <a-checkbox-group/>,
datePicker: <a-date-picker/>,
checkBoxButton: <div/>,
popconfirm: <a-popconfirm/>,
}
},
ele: () => {
return {
row: <el-row />,
col: <el-col />,
form: <el-form />,
formItem: <el-form-item />,
button: <el-button />,
table: <el-table />,
tableColumn: <el-table-column />,
radio: <el-radio />,
pagination: <el-pagination />,
input: <el-input />,
password: <el-input />,
textarea: <el-input />,
number: <el-input-number />,
radioGroup: <el-radio-group />,
radioButton: <el-radio-button />,
select: <el-select />,
selectOption: <el-option />,
switch: <el-switch />,
cascader: <el-cascader />,
checkBox: <el-checkbox />,
checkBoxGroup: <el-checkbox-group />,
datePicker: <el-date-picker />,
checkBoxButton: <el-checkbox-button />,
popconfirm: <el-popconfirm/>,
}
},
}
constructor() {
// 首字母大写函数
function ucFirst(str:string):string {
const str1 = str[0].toUpperCase() + str.slice(1)
return str1
}
const components:string[] = ['row', 'col', 'form', 'formItem', 'button', 'table', 'tableColumn', 'radio', 'pagination', 'input', 'password', 'textarea', 'number', 'radioGroup', 'radioButton', 'select', 'selectOption', 'switch', 'cascader', 'checkBox', 'checkBoxGroup', 'datePicker', 'checkBoxButton', 'popconfirm']
components.forEach((item) => {
// 根据组件名称自动注册组件
this['dynamic' + ucFirst(item)] = this.getComponent(item)
})
}
// 获取动态组件函数
getComponent(type: string): JSX.Element {
const methodMap = CustomDynamicComponent.dicts[window?.uiLanguage ?? 'ele']
// 判断是否有ui主题组件库
if (methodMap) {
// 判断是否有定义的组件类型,没有返回div
return methodMap()[type] || <div />
}
return <div />
}
}
在window中定义全局变量
window.uiLanguage = 'ele'
使用
这里以input为例:
import { CustomDynamicComponent } from '@/components/CustomDynamicComponent'
const { dynamicInput } = new CustomDynamicComponent()
....
<dynamicInput
class="input"
type='text'
model-value={props.modelValue}
placeholder={props.config.placeholder || `请输入${props.config.label}`}
disabled={!!props.config.disabled}
/** ant-design-vue && ele 统一封装 - start */
clearable={props.config.clearable !== false} // ele 特有属性
allowClear={props.config.allowClear ?? props.config.clearable !== false} // ant-design-vue特有属性
/** ant-design-vue && ele 统一封装 - end */
onInput={updateValue}
{...props.config.nativeProps}
/>
缺点
需要使用项目全局引入ui框架(或者我们组件暴露出去必须要引入的组件)
存在小部分冗余代码
源码及实现浅析
https://juejin.cn/post/7246190449311940669
作者:快落的小海疼