# 扩展示例

提示

以继承基础widget方式开发,具体如何开发可以访问开发widget一节

# 目标

  1. 基于基础 textarea 扩展自定义属性,这里增加字数统计及最大字符长度
  2. 只允许输入0-9的数字
  3. 隐藏设置表单时帮助信息字段,减少配置项
  4. 开发自定义规则,实现异步校验,只能输入非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

接下来分别看下每个目标对应的文件

# App.vue

项目入口文件,对应具体业务场景页面。因为Epage基于dom节点渲染,所以可以在任何基于浏览器dom的项目实例化。

提示

需要注意的是需要提前安装依赖并注册到vue上

npm i vue@2 vuex iview epage-iview -S
# 如果创建设计器需要在安装拖拽依赖,只是渲染的话可以不用安装
npm i vuedraggable -S
1
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

# 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

# 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

# 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

# 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

提示

看完了本节,开始计划的4个目标是否都明白了呢?

更新: 3/22/2021, 6:03:54 PM