# 扩展示例
提示
以继承基础widget方式开发,具体如何开发可以访问开发widget一节
# 目标
- 基于基础
textarea
扩展自定义属性,这里增加字数统计及最大字符长度 - 只允许输入0-9的数字
- 隐藏设置表单时帮助信息字段,减少配置项
- 开发自定义规则,实现异步校验,只能输入非0开始的数字字符串(其实可以用内置的正则实现,这里为了讲解异步规则开发)
# 预览
- 完整示例 codeSandbox (opens new window)
- 在线预览 (opens new window),添加 myTextarea,设计视图添加 异步校验 规则,预览视图输入非数字或 0 开始的数字字符串试试
# 项目结构
├── App.vue // 项目入口,这里注册 目标1 及 目标4 的规则绑定
├── rules
│ ├── MyAsyncRule.js // 目标4: 自定义规则
│ └── index.js
└── widgets
├── index.js // 多个widget统一导出为widget组
└── myTextarea // 目标1:自定义widget
├── Schema.js // 目标1: 基于基础textarea扩展自定义属性,并申明字数统计及最大字符长度
├── Setting.vue // 所有目标的配置,会修改new Schema实例
├── View.vue // 所有目标的最终展现,目标2的逻辑在此
└── index.js // widget入口文件
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
接下来分别看下每个目标对应的文件
# App.vue
项目入口文件,对应具体业务场景页面。因为Epage基于dom节点渲染,所以可以在任何基于浏览器dom的项目实例化。
提示
需要注意的是需要提前安装依赖并注册到vue上
npm i vue@2 vuex iview epage-iview -S
# 如果创建设计器需要在安装拖拽依赖,只是渲染的话可以不用安装
npm i vuedraggable -S
1
2
3
2
3
<template>
<div id="app">
<div ref='epage' />
</div>
</template>
<script>
import { render } from 'epage-core'
import Epage from 'epage'
import { entry } from 'epage-iview'
import widgets from './widgets'
import * as myRules from './rules'
const { Rule, helper } = Epage
// 目标4:将开发好的自定义规则注册到Epage全局
Rule.set(myRules)
// 目标4:为 myTextarea 绑定自定义规则
helper.setValidators(widgets, { myTextarea: ['myAsync'] })
export default {
mounted () {
this.renderPage()
},
methods: {
renderPage () {
const el = this.$refs.epage
new Epage({
el,
pc: {
Render: render.VueRender,
component: entry,
widgets
}
})
}
}
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# MyAsyncRule.js
自定义规则,如何更好的开发规则,请访问开发规则
// 目标4:自定义异步规则,内部setTimeout可以为实际fetch请求等
export default class MyAsyncRule {
static get type () {
return 'myAsync'
}
static get name () {
return '异步校验'
}
constructor (rule = {}) {
const defaultRule = {
type: 'string',
asyncValidator: null,
message: ''
}
this.origin = Object.assign({}, defaultRule, rule)
this.rule = {
type: 'string',
trigger: 'blur',
asyncValidator: (rule, value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (/^[1-9]+\d*$/g.test(value)) {
resolve()
} else {
reject('请输入非0开始的数字字符串')
}
}, 1000)
})
},
message: '请输入非0开始的数字字符串'
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Schema.js
自定义widget的Schema
声明,如何扩展或开发Schema
请访问开发wedget#Schema开发
import { textareaWidget } from 'epage-iview'
// 目标1:基于基础textarea扩展
export default class MyTextareaSchema extends textareaWidget.Schema {
constructor (props) {
super()
// 目标1:自定义属性,是否显示剩余字数及最大长度
this.option.wordLimit = false
this.option.maxlength = 1000
this.create(props)
}
}
// 静态配置,同类widget公有
Object.assign(MyTextareaSchema, {
title: 'myTextarea',
widget: 'myTextarea',
icon: 'compose',
type: 'string',
// 目标4:申明widget绑定的校验自定义异步规则
validators: ['myAsync'],
logic: {
value: ['=', '!=', '<>', '><'],
event: []
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Setting.vue
自定义widget的配置表单,核心功能为修改Schema
实例。如何扩展或开发Setting
请访问开发wedget#Setting开发
<template lang="pug">
setting-form(:store='store')
//- 目标3:隐藏默认的help配置
span(slot='help')
FormItem(v-if='!$slots.rows' label='高度')
InputNumber(
type='text'
size='small'
v-bind:min='1'
placeholder='请输入高度'
v-model='selectedSchema.option.rows')
template(v-else)
slot(name='rows')
//- 目标1:自定义属性配置
FormItem(label='显示字数')
i-switch(v-model='selectedSchema.option.wordLimit')
span(slot='open') 是
span(slot='close') 否
//- 目标1:自定义属性配置
FormItem(label='最大长度')
InputNumber(v-model='selectedSchema.option.maxlength' size='small' v-bind:step='1' v-bind:min='0')
</template>
<script>
import { setting } from 'epage-iview'
const { SettingForm, settingExtend } = setting
// const SettingForm = textareaWidget.Setting
export default {
components: { SettingForm },
extends: settingExtend,
methods: {}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# View.vue
自定义widget的最终视图展示,根据schema字段信息渲染不同视图逻辑。如何扩展或开发View
请访问开发wedget#View开发
<template lang="pug">
.ep-widget.ep-widget-mytextarea
template(v-if='mode === "display"')
span {{model[schema.key]}}
template(v-else)
Input(
v-if='schema.key'
type='textarea'
v-bind:rows='schema.option.rows'
v-bind:placeholder='schema.placeholder'
v-bind:disabled='schema.disabled'
v-bind:size='schema.size || rootSchema.size'
v-model.trim='model[schema.key]'
@on-enter="event('on-enter', ...arguments)"
@click.native="event('on-click', ...arguments)"
@on-change="event('on-change', ...arguments)"
@on-keyup="event('on-up', ...arguments)"
@on-focus="event('on-focus', ...arguments)"
@on-blur="event('on-blur', ...arguments)"
@on-keydown="onKeydown(...arguments)"
@on-keypress="event('on-keypress', ...arguments)"
)
//- 目标1:自定义属性,剩余字数
span.word-count(
v-if='schema.option.wordLimit && schema.option.maxlength'
) {{schema.option.maxlength - (model[schema.key] || '').length}}
</template>
<script>
import { viewExtend } from 'epage-iview'
export default {
extends: viewExtend,
methods: {
// 目标2:校验输入规则,这里组允许数字、左右方向键及删除,且超出后不可输入
onKeydown (e) {
console.log(this.model[this.schema.key])
this.event('on-keydown', ...arguments)
// 只能输入0-9的数字
const isNumCode = e.keyCode >= 48 && e.keyCode <= 57
const isFnCode = [
8, // BackSpace
37, // Left Arrow
39, // Right Arrow
].indexOf(e.keyCode) > -1
const isExceeds = this.model[this.schema.key].length >= this.schema.option.maxlength
if (isExceeds) {
if (!isFnCode) e.preventDefault()
} else {
if (!(isNumCode || isFnCode)) {
e.preventDefault()
}
}
}
}
}
</script>
<style lang='less'>
/* 目标1:自定义规则剩余字数样式*/
.ep-widget-mytextarea{
.word-count{
position: absolute;
right: 6px;
bottom: 6px;
height: 16px;
line-height: 16px;
user-select: none;
color:#999;
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
提示
看完了本节,开始计划的4个目标是否都明白了呢?