el-form-renderer-vue3 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vite/deps_temp_ce3ed5bf/package.json +3 -0
- package/.vscode/extensions.json +3 -0
- package/LICENSE +661 -0
- package/README.en.md +36 -0
- package/README.md +252 -0
- package/index.html +13 -0
- package/package.json +34 -0
- package/public/vite.svg +1 -0
- package/src/App.vue +59 -0
- package/src/ChildCom.vue +24 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/femessage/components/render-form-group.vue +48 -0
- package/src/components/femessage/components/render-form-item.vue +328 -0
- package/src/components/femessage/el-form-renderer.vue +284 -0
- package/src/components/femessage/util/CustomComponent.js +28 -0
- package/src/components/femessage/util/VNode.js +9 -0
- package/src/components/femessage/util/enable-when.js +26 -0
- package/src/components/femessage/util/transform-content.js +48 -0
- package/src/components/femessage/util/utils.js +127 -0
- package/src/el-form-renderer.md +220 -0
- package/src/main.js +15 -0
- package/src/router/index.js +160 -0
- package/src/style.css +89 -0
- package/src/view/AboutView.vue +40 -0
- package/src/view/HomeView.vue +134 -0
- package/src/view/MyInput.vue +46 -0
- package/src/view/checkboxGroup.vue +29 -0
- package/src/view/content.vue +176 -0
- package/src/view/deprecated.vue +37 -0
- package/src/view/disabled.vue +104 -0
- package/src/view/el.vue +24 -0
- package/src/view/format.vue +63 -0
- package/src/view/getcomponent.vue +51 -0
- package/src/view/getform.vue +50 -0
- package/src/view/hidden.vue +51 -0
- package/src/view/label.vue +25 -0
- package/src/view/next.vue +55 -0
- package/src/view/picker.vue +27 -0
- package/src/view/radioGroup.vue +38 -0
- package/src/view/readonly.vue +144 -0
- package/src/view/remote.vue +115 -0
- package/src/view/rules.vue +46 -0
- package/src/view/rulesPlus.vue +34 -0
- package/src/view/setoptios.vue +50 -0
- package/src/view/slot.vue +37 -0
- package/src/view/testAttrs.vue +17 -0
- package/src/view/update.vue +64 -0
- package/src/view/vmodel.vue +137 -0
- package/src/your-component.vue +55 -0
- package/vite.config.js +7 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import _kebabcase from "lodash.kebabcase";
|
|
2
|
+
export default function transformContent(content) {
|
|
3
|
+
return content.map(({ ...item }) => {
|
|
4
|
+
if (item.type === "group") {
|
|
5
|
+
item.items = transformContent(item.items);
|
|
6
|
+
} else {
|
|
7
|
+
removeDollarInKey(item);
|
|
8
|
+
setItemId(item);
|
|
9
|
+
extractRulesFromComponent(item);
|
|
10
|
+
// 有些旧写法是 checkboxGroup & radioGroup
|
|
11
|
+
// 转换字符串string为kebab case.(foo-bar)
|
|
12
|
+
item.type = _kebabcase(item.type);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return item;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
// 兼容旧写法:$id、$name
|
|
19
|
+
function removeDollarInKey(item) {
|
|
20
|
+
Object.keys(item)
|
|
21
|
+
// 首先检查属性名是否以"$"开头,然后检查去掉"$"后的属性名是否不在对象item中。
|
|
22
|
+
.filter((k) => k.startsWith("$") && !(k.slice(1) in item))
|
|
23
|
+
// 将对象item中的k属性的值赋给item[k.slice(1)],然后删除原来的k属性,以实现将"$"去除的操作。
|
|
24
|
+
.forEach((k) => ((item[k.slice(1)] = item[k]), delete item[k]));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 为一个对象 item 设置一个 id 属性,但只有在 item 的 id 属性不存在时才会执行。
|
|
28
|
+
function setItemId(item) {
|
|
29
|
+
if (item.id) return;
|
|
30
|
+
// name 是符合表单项直觉的命名; prop 是为了与 element 的 table 的 columns 匹配
|
|
31
|
+
item.id = item.name || item.prop;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 其作用是从给定的 item 参数中提取规则并将其添加到 item.rules 属性中
|
|
35
|
+
export function extractRulesFromComponent(item) {
|
|
36
|
+
// 是否覆盖自定义组件内置的校验规则;(overrideRules:true不校验组件内规则)
|
|
37
|
+
if (item.overrideRules) return;
|
|
38
|
+
const { component } = item;
|
|
39
|
+
|
|
40
|
+
// 使用全局注册的组件暂时无法处理(处理自定义组件内的rules)
|
|
41
|
+
if (!component || typeof component === "string") return;
|
|
42
|
+
console.log(component);
|
|
43
|
+
const { rules = [] } = component;
|
|
44
|
+
item.rules = [
|
|
45
|
+
...(item.rules || []),
|
|
46
|
+
...(typeof rules === "function" ? rules(item) : rules),
|
|
47
|
+
];
|
|
48
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import _isplainobject from "lodash.isplainobject";
|
|
2
|
+
// var pairs = [
|
|
3
|
+
// ["a", 1],
|
|
4
|
+
// ["b", 2],
|
|
5
|
+
// ["c", 3],
|
|
6
|
+
// ];==》{ 'a': 1, 'b': 2, 'c': 3 }
|
|
7
|
+
import _frompairs from "lodash.frompairs";
|
|
8
|
+
// collect 函数接受两个参数,content 和 key,其中 content 是一个包含嵌套数据的数组,key 是要收集的键的名称。(key:options| defult)
|
|
9
|
+
export function collect(content, key) {
|
|
10
|
+
return _frompairs(
|
|
11
|
+
content
|
|
12
|
+
|
|
13
|
+
// 使用 map 函数对 content 数组进行映射操作。对每个数组中的元素(item)进行处理,创建一个新的对象,该对象包含三个属性:
|
|
14
|
+
.map((item) => ({
|
|
15
|
+
id: item.id,
|
|
16
|
+
type: item.type,
|
|
17
|
+
value: item.type === "group" ? collect(item.items, key) : item[key],
|
|
18
|
+
}))
|
|
19
|
+
// 函数对上一步生成的对象数组进行过滤。只保留那些满足以下条件的对象:(带有optios数据的)
|
|
20
|
+
.filter(
|
|
21
|
+
({ type, value }) =>
|
|
22
|
+
value !== undefined || (type === "group" && Object.keys(value).length)
|
|
23
|
+
)
|
|
24
|
+
// 返回 [select(id),optons]
|
|
25
|
+
.map(({ id, value }) => [id, value])
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 根据 content 中的 outputFormat 来处理 value;
|
|
30
|
+
* 如果 outputFormat 处理后的值是对象类型,会覆盖(Object.assign)到 value 上
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// value 是待处理的值,content 是一个数组,strict 是一个可选的参数,默认为 false。(当 strict 为 true 时,只返回设置的表单项的值, 过滤掉冗余字段, )
|
|
34
|
+
export function transformOutputValue(value, content, { strict = false } = {}) {
|
|
35
|
+
// 参数的值创建一个新的对象 newVal,如果 strict 为 true,则创建一个空对象,否则创建一个与输入 value 一样的对象的副本。
|
|
36
|
+
const newVal = strict ? {} : { ...value };
|
|
37
|
+
|
|
38
|
+
Object.keys(value).forEach((id) => {
|
|
39
|
+
// 找出表单项
|
|
40
|
+
const item = content.value.find((item) => item.id === id);
|
|
41
|
+
if (!item) return;
|
|
42
|
+
// 除去多选
|
|
43
|
+
if (item.type !== "group") {
|
|
44
|
+
// 用于处理输出值,参数为对应组件返回值
|
|
45
|
+
// 如果处理后的值是对象类型,会覆盖(Object.assign)到整个表单的值上
|
|
46
|
+
if (item.outputFormat) {
|
|
47
|
+
const v = item.outputFormat(value[id]);
|
|
48
|
+
// REVIEW: 仅根据 format 后的类型来判断赋值形式,有些隐晦
|
|
49
|
+
// (boolean): 如果 value 为一个普通对象,那么返回 true,否则返回 false(({ 'x': 0, 'y': 0 }); (Object.create(null));)
|
|
50
|
+
if (_isplainobject(v)) Object.assign(newVal, v);
|
|
51
|
+
else newVal[id] = v;
|
|
52
|
+
} else {
|
|
53
|
+
// 如果 item 没有 outputFormat 属性,直接将 value[id] 赋值给 newVal[id]。
|
|
54
|
+
newVal[id] = value[id];
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
// 多选递归处理
|
|
58
|
+
newVal[id] = transformOutputValue(value[id], item.items, { strict });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return newVal;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 根据 content 中的 inputFormat 来处理 value
|
|
67
|
+
* inputFormat 接受的是当前层级的 value
|
|
68
|
+
* 复杂点在于,不管传入的 value 是否包含某表单项的 key,所有使用了 inputFormat 的项都有可能在这次 update 中被更新
|
|
69
|
+
*/
|
|
70
|
+
export function transformInputValue(value, content) {
|
|
71
|
+
// 首先,创建了一个名为 newVal 的新对象,它是 value 的副本,以便在不修改原始数据的情况下进行操作。
|
|
72
|
+
const newVal = { ...value };
|
|
73
|
+
const processItem = (item) => {
|
|
74
|
+
const { id } = item;
|
|
75
|
+
if (item.inputFormat) {
|
|
76
|
+
// 对于每个 item,它检查是否存在 inputFormat 属性。如果存在,它将调用 item.inputFormat(value) 来处理 value,
|
|
77
|
+
// 然后将处理后的结果赋值给 newVal 的相应属性(根据 id 来确定属性名),但仅在处理后的值不为 undefined 时才会进行赋值。
|
|
78
|
+
const v = item.inputFormat(value);
|
|
79
|
+
if (v !== undefined) newVal[id] = v;
|
|
80
|
+
} else if (id in value) {
|
|
81
|
+
// 如果 item 没有 inputFormat 属性,它会检查是否 value 中存在与 item 的 id 相匹配的属性。如果存在,它会将该属性的值赋值给 newVal 的相应属性。
|
|
82
|
+
if (item.type !== "group") {
|
|
83
|
+
newVal[id] = value[id];
|
|
84
|
+
} else {
|
|
85
|
+
// 如果 item 的类型是 "group",则会递归调用 transformInputValue 函数来处理嵌套的对象。
|
|
86
|
+
newVal[id] = tran;
|
|
87
|
+
transformInputValue(value[id], item.items);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
// 判断响应式数据的类型
|
|
92
|
+
const itemsToProcess = content.value || content;
|
|
93
|
+
itemsToProcess.forEach(processItem);
|
|
94
|
+
return newVal;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 对 group checkbox-group初始化值修正默认为空数组
|
|
98
|
+
export function correctValue(value, content) {
|
|
99
|
+
content.forEach(({ type, id, items }) => {
|
|
100
|
+
switch (type) {
|
|
101
|
+
case "group":
|
|
102
|
+
if (!(id in value)) value[id] = {};
|
|
103
|
+
correctValue(value[id], items);
|
|
104
|
+
break;
|
|
105
|
+
case "checkbox-group":
|
|
106
|
+
if (!(id in value)) value[id] = [];
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 递归合并 oldV & newV,策略如下:
|
|
113
|
+
* 1. 如果该项的 type 不是 GROUP,直接覆盖合并到 oldV
|
|
114
|
+
* 2. 如果是,则递归执行步骤 1
|
|
115
|
+
*/
|
|
116
|
+
export function mergeValue(oldV, newV, content) {
|
|
117
|
+
// 遍历 newV 对象的所有属性
|
|
118
|
+
Object.keys(newV).forEach((k) => {
|
|
119
|
+
// 对于每个属性 k,首先尝试在 content 数组中查找具有相同 id 值的项,如果找不到则使用一个空对象。
|
|
120
|
+
const item = content.value.find((item) => item.id === k) || {};
|
|
121
|
+
// 如果不是 "group" 类型, 就直接将 newV 中的属性值覆盖到 oldV 中的对应属性上,实现合并。
|
|
122
|
+
if (item.type !== "group") oldV[k] = newV[k];
|
|
123
|
+
// 如果项的类型是 "group",则递归调用这个函数 mergeValue,以进一步合并 oldV[k] 和 newV[k],并传入该项的子项数组 item.items 作为 content 参数。
|
|
124
|
+
else mergeValue(oldV[k], newV[k], item.items);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
export function noop() {}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
## 遗留问题 :带解决
|
|
2
|
+
|
|
3
|
+
select 为 multiple 多选时 必须初始化空数组(在 elementplus v-model 初始化 updateValue 时 为空数组会触发校验)
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
export default {
|
|
9
|
+
// ...
|
|
10
|
+
props: {
|
|
11
|
+
/**
|
|
12
|
+
* support all el-form's props
|
|
13
|
+
* @see: https://element.eleme.io/#/zh-CN/component/form#form-attributes
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 表单项的配置数组,每个表单项代表一个原子表单项
|
|
18
|
+
* the form config's array, each item represents a form-item
|
|
19
|
+
*/
|
|
20
|
+
content: {
|
|
21
|
+
type: Array, // type:Content[], check Content's definition below
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* disable all form-items
|
|
27
|
+
*/
|
|
28
|
+
disabled: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 表单项的typescript定义
|
|
37
|
+
* 支持所有el-form-item's props。表单项组件本身的props定义在el上
|
|
38
|
+
* definition of form-item written in typescript.
|
|
39
|
+
* support all el-form-item's props. The component's props need to be set at prop el
|
|
40
|
+
*/
|
|
41
|
+
interface Content {
|
|
42
|
+
// 每一个原子都存在 id,用于存储该原子的值,不能重复
|
|
43
|
+
// key of form-item value in form value. Must be unique
|
|
44
|
+
id: string
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 可以是element提供的所有表单组件类型,如传入'input',则渲染出'el-input'
|
|
48
|
+
* 当type="group"时,可以创造复杂对象类型的表单数据,配合items使用。此时getFormValue()返回的是对象类型的数据,对象的每个属性对应items里的每一项
|
|
49
|
+
* support all element's form component, e.g., type 'input' will render as 'el-input'.
|
|
50
|
+
* you can create nested form value with type 'group' and use items to define that form value's shape. The type of this form value will be 'object'
|
|
51
|
+
*/
|
|
52
|
+
type: string
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 当type="group"时使用
|
|
56
|
+
* items内依然遵循同一层级的id不重复的原则
|
|
57
|
+
* using with type 'group'
|
|
58
|
+
* the `id` in each item of items must be unique
|
|
59
|
+
*/
|
|
60
|
+
items: Content[]
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 默认值
|
|
64
|
+
* FIXME: 别用关键字做 key
|
|
65
|
+
*/
|
|
66
|
+
default?: any
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 当 type === 'input' 时展示文本值
|
|
70
|
+
* 当 type === 'select' 时展示对应 label
|
|
71
|
+
* 对于其他组件等同于 disabled = true
|
|
72
|
+
*/
|
|
73
|
+
readonly = false
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated
|
|
77
|
+
*/
|
|
78
|
+
enableWhen?: object | string
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 传入一个方法,并返回 boolean,返回 true 时则隐藏该表单项
|
|
82
|
+
* formValue 为当前 form 的值,item 为当前表单项的定义
|
|
83
|
+
* hide the form-item when return true
|
|
84
|
+
* formValue is same as what getFormValue returns, and item is the config of this form-item
|
|
85
|
+
*/
|
|
86
|
+
hidden?: (formValue: Object, item: Content) => boolean
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 具有选择功能的原子表单可用此定义可选项
|
|
90
|
+
* use with type: select, radio-group, radio-button, checkbox-group, checkbox-button
|
|
91
|
+
*/
|
|
92
|
+
options?: {label: string; value?: any}[]
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 配置remote.url,即可远程配置组件的某个prop!
|
|
96
|
+
* remote接受以下属性:
|
|
97
|
+
* url: 远程接口的地址
|
|
98
|
+
* prop: 要注入的 prop 的名称,默认为 options
|
|
99
|
+
* request: 可选,请求方法
|
|
100
|
+
* dataPath: 可选,data在响应体中的路径
|
|
101
|
+
* onResponse: 可选,处理请求回来的数据
|
|
102
|
+
* onError: 可选,处理请求出错的情况
|
|
103
|
+
* 另外,针对 select、radio-group、checkbox-group,远程数据能自动映射成 el-option 选项!以下属性仅在此情况使用
|
|
104
|
+
* label: 可选,可直接配置远程数据中用作 label 的key
|
|
105
|
+
* value: 可选,可直接配置远程数据中用作 value 的key
|
|
106
|
+
* @see https://zhuanlan.zhihu.com/p/97827063
|
|
107
|
+
*
|
|
108
|
+
* use remote to set one prop! remote accept following props:
|
|
109
|
+
* url: remote api address
|
|
110
|
+
* prop: prop name that data inject
|
|
111
|
+
* request: optional, customize how to get your options
|
|
112
|
+
* dataPath: optional, data's path in response
|
|
113
|
+
* onResponse: optional, deal with your response
|
|
114
|
+
* onError: optional, deal with request error
|
|
115
|
+
* and, we treat select、radio-group、checkbox-group specially and the resp will be map as an el-option's group! following props only suitable for this case
|
|
116
|
+
* label: optional, label key in resp
|
|
117
|
+
* value: optional, value key in resp
|
|
118
|
+
*/
|
|
119
|
+
remote?: {
|
|
120
|
+
url: string
|
|
121
|
+
request = () => this.$axios.get(url).then(resp => resp.data)
|
|
122
|
+
prop = 'options'
|
|
123
|
+
dataPath = ''
|
|
124
|
+
onResponse = resp => {
|
|
125
|
+
if (dataPath) resp = _get(resp, dataPath)
|
|
126
|
+
switch (this.data.type) {
|
|
127
|
+
case 'select':
|
|
128
|
+
case 'checkbox-group':
|
|
129
|
+
case 'radio-group':
|
|
130
|
+
return resp.map(item => ({
|
|
131
|
+
label: item[label],
|
|
132
|
+
value: item[value]
|
|
133
|
+
}))
|
|
134
|
+
default:
|
|
135
|
+
return resp
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
onError = error => console.error(error.message)
|
|
139
|
+
label = 'label'
|
|
140
|
+
value = 'value'
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
attrs?: object // html attributes
|
|
144
|
+
/**
|
|
145
|
+
* 用于定义具体原子表单(如el-input)的属性,比如定义el-input的placeholder
|
|
146
|
+
* use to define props of the actual component of this form-item, such as placeholder of el-input
|
|
147
|
+
*/
|
|
148
|
+
el?: object
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 使用自定义组件
|
|
152
|
+
* component适用于渲染局部注册组件和自定义组件,而type适用于带el-前缀的全局组件
|
|
153
|
+
* custom component
|
|
154
|
+
* use it when element's form components are not enough
|
|
155
|
+
*/
|
|
156
|
+
component?: Vue
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 是否覆盖自定义组件内置的校验规则
|
|
160
|
+
* `true` 为覆盖, 默认为 `false`
|
|
161
|
+
* whether to override the validation rules written in custom components
|
|
162
|
+
* `true` to override, default `false`
|
|
163
|
+
*/
|
|
164
|
+
overrideRules: boolean
|
|
165
|
+
|
|
166
|
+
label?: string //set el-form-item's label
|
|
167
|
+
trim = true // trim value at change event
|
|
168
|
+
|
|
169
|
+
// 用于处理输入值,输入的值包括:1. default;2. v-model;3. updateForm。参数为整个表单的值对象或 updateForm 传入的对象
|
|
170
|
+
// 如果 inputFormat 返回 undefined,则不会更新此表单项
|
|
171
|
+
// obj is param you passed to updateForm. You can use this function to hijack this process and customize the form value
|
|
172
|
+
inputFormat?: (obj: any) => any
|
|
173
|
+
|
|
174
|
+
// 用于处理输出值,参数为对应组件返回值
|
|
175
|
+
// 如果处理后的值是对象类型,会覆盖(Object.assign)到整个表单的值上
|
|
176
|
+
// used to hijack the getFormValue's process and customize the return value
|
|
177
|
+
outputFormat?: (val: any) => any
|
|
178
|
+
|
|
179
|
+
// set el-form-item's rules
|
|
180
|
+
rules?: object
|
|
181
|
+
|
|
182
|
+
// @deprecated
|
|
183
|
+
atChange?: (id: string, value: any) => void
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 监听表单项发出的事件
|
|
187
|
+
* listen to any events emitted by component of form item
|
|
188
|
+
* @param {any[]} args - what that event emits
|
|
189
|
+
* @param {function} updateForm - same as methods.updateForm
|
|
190
|
+
*/
|
|
191
|
+
on?: {
|
|
192
|
+
[eventName: string]: (args: any[], updateForm: function) => void
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* a tour of typescript
|
|
198
|
+
*/
|
|
199
|
+
interface obj {
|
|
200
|
+
a: string // type string
|
|
201
|
+
b?: string // type string, optional
|
|
202
|
+
c = true // type boolean, optional, default true
|
|
203
|
+
d: string[] // type array, each item must be string
|
|
204
|
+
e: any // could be any valid js type
|
|
205
|
+
f: (a: number) => void // type function, which receives a param 'a' as number and return nothing
|
|
206
|
+
h: Vue // instance of Vue
|
|
207
|
+
i: {[a: string]: number} // type object, whose key is type string, and value is type number
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Methods
|
|
212
|
+
|
|
213
|
+
support all [el-form's methods](https://element.eleme.io/#/zh-CN/component/form#form-methods)
|
|
214
|
+
|
|
215
|
+
## Slots
|
|
216
|
+
|
|
217
|
+
| Slot | Description |
|
|
218
|
+
| -------- | ------------------------------------------- |
|
|
219
|
+
| default | insert at bottom |
|
|
220
|
+
| id:hello | insert before form-item whose id is 'hello' |
|
package/src/main.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// main
|
|
2
|
+
import { createApp } from "vue";
|
|
3
|
+
import ElementPlus from "element-plus";
|
|
4
|
+
import "element-plus/dist/index.css";
|
|
5
|
+
import App from "./App.vue";
|
|
6
|
+
import router from "./router/index";
|
|
7
|
+
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
|
8
|
+
const app = createApp(App);
|
|
9
|
+
|
|
10
|
+
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
|
11
|
+
app.component(key, component)
|
|
12
|
+
}
|
|
13
|
+
app.use(router);
|
|
14
|
+
app.use(ElementPlus);
|
|
15
|
+
app.mount("#app");
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createRouter, createWebHistory } from "vue-router";
|
|
2
|
+
import HomeView from "../view/HomeView.vue";
|
|
3
|
+
import AboutView from "../view/AboutView.vue";
|
|
4
|
+
import checkboxGroup from "../view/checkboxGroup.vue";
|
|
5
|
+
import content from "../view/content.vue";
|
|
6
|
+
import setoptios from "../view/setoptios.vue";
|
|
7
|
+
import picker from "../view/picker.vue";
|
|
8
|
+
import format from "../view/format.vue";
|
|
9
|
+
import update from "../view/update.vue";
|
|
10
|
+
import slot from "../view/slot.vue";
|
|
11
|
+
import hidden from "../view/hidden.vue";
|
|
12
|
+
import disabled from "../view/disabled.vue";
|
|
13
|
+
import vmodel from "../view/vmodel.vue";
|
|
14
|
+
import rules from "../view/rules.vue";
|
|
15
|
+
import next from "../view/next.vue";
|
|
16
|
+
import readonly from "../view/readonly.vue";
|
|
17
|
+
import label from "../view/label.vue";
|
|
18
|
+
import getform from "../view/getform.vue";
|
|
19
|
+
import getcomponent from "../view/getcomponent.vue";
|
|
20
|
+
import deprecated from "../view/deprecated.vue";
|
|
21
|
+
import el from "../view/el.vue";
|
|
22
|
+
import radioGroup from "../view/radioGroup.vue";
|
|
23
|
+
import remote from "../view/remote.vue";
|
|
24
|
+
import rulesPlus from "../view/rulesPlus.vue";
|
|
25
|
+
import MyInput from "../view/MyInput.vue";
|
|
26
|
+
import testAttrs from "../view/testAttrs.vue";
|
|
27
|
+
const routes = [
|
|
28
|
+
{
|
|
29
|
+
path: "/",
|
|
30
|
+
name: "home",
|
|
31
|
+
component: HomeView,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: "/about",
|
|
35
|
+
name: "about",
|
|
36
|
+
component: AboutView,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
path: "/checkboxGroup",
|
|
40
|
+
name: "checkboxGroup",
|
|
41
|
+
component: checkboxGroup,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
path: "/content",
|
|
45
|
+
name: "content",
|
|
46
|
+
component: content,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
path: "/setoptios",
|
|
50
|
+
name: "setoptios",
|
|
51
|
+
component: setoptios,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
path: "/picker",
|
|
55
|
+
name: "picker",
|
|
56
|
+
component: picker,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
path: "/format",
|
|
60
|
+
name: "format",
|
|
61
|
+
component: format,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
path: "/update",
|
|
65
|
+
name: "update",
|
|
66
|
+
component: update,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
path: "/slot",
|
|
70
|
+
name: "slot",
|
|
71
|
+
component: slot,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
path: "/hidden",
|
|
75
|
+
name: "hidden",
|
|
76
|
+
component: hidden,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
path: "/disabled",
|
|
80
|
+
name: "disabled",
|
|
81
|
+
component: disabled,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
path: "/vmodel",
|
|
85
|
+
name: "vmodel",
|
|
86
|
+
component: vmodel,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
path: "/rules",
|
|
90
|
+
name: "rules",
|
|
91
|
+
component: rules,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
path: "/next",
|
|
95
|
+
name: "next",
|
|
96
|
+
component: next,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
path: "/readonly",
|
|
100
|
+
name: "readonly",
|
|
101
|
+
component: readonly,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
path: "/label",
|
|
105
|
+
name: "label",
|
|
106
|
+
component: label,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
path: "/getform",
|
|
110
|
+
name: "getform",
|
|
111
|
+
component: getform,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
path: "/getcomponent",
|
|
115
|
+
name: "getcomponent",
|
|
116
|
+
component: getcomponent,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
path: "/deprecated",
|
|
120
|
+
name: "deprecated",
|
|
121
|
+
component: deprecated,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
path: "/el",
|
|
125
|
+
name: "el",
|
|
126
|
+
component: el,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
path: "/radioGroup",
|
|
130
|
+
name: "radioGroup",
|
|
131
|
+
component: radioGroup,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
path: "/remote",
|
|
135
|
+
name: "remote",
|
|
136
|
+
component: remote,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
path: "/rulesPlus",
|
|
140
|
+
name: "rulesPlus",
|
|
141
|
+
component: rulesPlus,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
path: "/MyInput",
|
|
145
|
+
name: "MyInput",
|
|
146
|
+
component: MyInput,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
path: "/testAttrs",
|
|
150
|
+
name: "testAttrs",
|
|
151
|
+
component: testAttrs,
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
const router = createRouter({
|
|
156
|
+
history: createWebHistory(),
|
|
157
|
+
routes,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
export default router;
|
package/src/style.css
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
-webkit-text-size-adjust: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
a {
|
|
18
|
+
font-weight: 500;
|
|
19
|
+
color: #646cff;
|
|
20
|
+
text-decoration: inherit;
|
|
21
|
+
}
|
|
22
|
+
a:hover {
|
|
23
|
+
color: #535bf2;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
a {
|
|
27
|
+
font-weight: 500;
|
|
28
|
+
color: #646cff;
|
|
29
|
+
text-decoration: inherit;
|
|
30
|
+
}
|
|
31
|
+
a:hover {
|
|
32
|
+
color: #535bf2;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
margin: 0;
|
|
37
|
+
display: flex;
|
|
38
|
+
place-items: center;
|
|
39
|
+
min-width: 320px;
|
|
40
|
+
min-height: 100vh;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
h1 {
|
|
44
|
+
font-size: 3.2em;
|
|
45
|
+
line-height: 1.1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
button {
|
|
49
|
+
border-radius: 8px;
|
|
50
|
+
border: 1px solid transparent;
|
|
51
|
+
padding: 0.6em 1.2em;
|
|
52
|
+
font-size: 1em;
|
|
53
|
+
font-weight: 500;
|
|
54
|
+
font-family: inherit;
|
|
55
|
+
background-color: #1a1a1a;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
transition: border-color 0.25s;
|
|
58
|
+
}
|
|
59
|
+
button:hover {
|
|
60
|
+
border-color: #646cff;
|
|
61
|
+
}
|
|
62
|
+
button:focus,
|
|
63
|
+
button:focus-visible {
|
|
64
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.card {
|
|
68
|
+
padding: 2em;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#app {
|
|
72
|
+
max-width: 1280px;
|
|
73
|
+
margin: 0 auto;
|
|
74
|
+
padding: 2rem;
|
|
75
|
+
text-align: center;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@media (prefers-color-scheme: light) {
|
|
79
|
+
:root {
|
|
80
|
+
color: #213547;
|
|
81
|
+
background-color: #ffffff;
|
|
82
|
+
}
|
|
83
|
+
a:hover {
|
|
84
|
+
color: #747bff;
|
|
85
|
+
}
|
|
86
|
+
button {
|
|
87
|
+
background-color: #f9f9f9;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="box">监听组件发出的任何事件</div>
|
|
3
|
+
|
|
4
|
+
<el-form-renderer label-width="100px" :content="content" ref="ruleForm">
|
|
5
|
+
</el-form-renderer>
|
|
6
|
+
</template>
|
|
7
|
+
<script setup>
|
|
8
|
+
import { reactive } from "vue";
|
|
9
|
+
import elFormRenderer from "../components/femessage/el-form-renderer.vue";
|
|
10
|
+
const content = reactive([
|
|
11
|
+
{
|
|
12
|
+
label: "fullName",
|
|
13
|
+
type: "input",
|
|
14
|
+
id: "fullName",
|
|
15
|
+
on: {
|
|
16
|
+
blur: ([event], updateForm) => {
|
|
17
|
+
updateForm({ display: "blur: " + event });
|
|
18
|
+
},
|
|
19
|
+
focus: ([event], updateForm) => {
|
|
20
|
+
updateForm({ display: "focus: " + event });
|
|
21
|
+
},
|
|
22
|
+
input: ([value], updateForm) => {
|
|
23
|
+
updateForm({ display: "input: " + value });
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
label: "display",
|
|
29
|
+
type: "input",
|
|
30
|
+
id: "display",
|
|
31
|
+
el: { readonly: true },
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
</script>
|
|
35
|
+
<style scoped>
|
|
36
|
+
.box {
|
|
37
|
+
margin: 20px 0;
|
|
38
|
+
font-size: 20px;
|
|
39
|
+
}
|
|
40
|
+
</style>
|