GithubHelp home page GithubHelp logo

chill-schema-form's Introduction

👋 About me

  • 😀 Frontend developer, mainly focus on

🛠 Most Technology stack

TypeScript Vue Node

📊 statistics

📞 Contact

Gmail

chill-schema-form's People

Contributors

jackson-yyy avatar xuemanchi avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

chill-schema-form's Issues

里程碑/Milestone v1.0.0

基础设计

  • #2
  • #3
  • 全局注册机制
  • schema解析器
  • 数据校验器
  • 渲染层(formRender)设计

项目搭建

  • 基础搭建(打包、单测等)
  • 全局注册功能
  • schema解析器编写
  • 数据校验器编写
  • formRender编写

开发中问题汇总

  1. 为什么选择了pnpm,而不是yarn/npm
  • 下载速度快
  • 能避免幽灵依赖
  1. vue2/vue3同时打包问题
    利用软连接动态变更vue-render/node_modules/vue所指向的vue,
    同时vue-demi切换版本时,unlink软连接(这里因为vue-demi切换版本的脚本有bug,提了修改 pr ),
    这样处理完后,vue-demi直接指向的就不是软连接里的vue,而是当前node_modules下的vue,
    这样就能保证打包时加载到正确的vue版本了
  2. 怎么同时启动vue2和vue3的demo的?
  • 利用pnpm的readPackge hooks, 在本地强行给demo-vue2/3安装一个vue-demi
  • 在vite.config.ts里,取消@chill-schema-form/vue-render的预编译,项目中当做源码引入,将vue-demi当做external,用当前node_modules下的vue-demi作为源,这样就能保证vue-demi引用到特定的vue版本

schema、ui-schema

schema

schema保持了schema的所有字段,新增两个字段validators和errMsg,用于设置自定义校验和报错提示

export const schema:Schema = {
  type: 'object',
  properties: {
    switch: {
      type: 'boolean',
    },
    city: {
      type: 'string',
      enum: ['1', '2']
    },
    areas: {
      type: 'array',
      minItems: 2,
      items: {
        type: 'number'
      },
      errMsg: {
        minItems: '{title}最少选择两个'
      }
    },
    email: {
      type: 'string',
      pattern: '^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$',
      format: 'email',
      validators: [
        (val: string, formData) => val.length <= 10 && formData.test2.length > 2,
        (val: string) => val.length >= 5
      ],
      errMsg: {
        type: '类型错误',
        pattern: '{title}格式错误,应为邮件格式',
        format: '{title}格式错误,应为邮件格式',
        validators: [
          '{title}长度需要小于等于10,当前值为{value}',
          (title: string, value: string) => `${title}长度需要大于等于5,当前值为${value}`
        ]
      }
    },
    person: {
      type: 'object',
      properties: {
        name: {
          type: 'string',
          minLength: 5
        },
        phone: {
          type: 'string',
          format: 'phone'
        }
      }
    },
    types: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          count: {
            type: 'number'
          },
          name: {
            type: 'string'
          }
        }
      }
    },
    strategy: {
      type: 'object',
      oneOf: [
        {
          properties: {
            name: {
              type: 'string'
            },
            times: {
              type: 'number'
            }
          }
        },
        {
          properties: {
            name1: {
              type: 'string'
            },
            times1: {
              type: 'number'
            }
          }
        }
      ]
    }
  }
}

export type SchemaType = 'integer' | 'string' | 'array' | 'number' | 'boolean' | 'object'

interface NumberKeywords {
  minimum?: number
  maximum?: number
  exclusiveMinimum?: number
  exclusiveMaximum?: number
  multipleOf?: number
  format?: string
  enum?: number[]
}

interface StringKeywords {
  minLength?: number
  maxLength?: number
  pattern?: string
  format?: string
  enum?: string[]
}

interface ArrayKeywords {
  minItems: number
  maxItems: number
  uniqueItems: boolean
}

type ErrMsg = string | ((title: string, value: any) => string)

type Schema = (NumberKeywords | StringKeywords | ArrayKeywords) & {
  description?: string
  type?: SchemaType
  additionalProperties?: Boolean | Record<string, Schema>
  properties?: Record<string, Schema>
  items?: Schema | Schema[]
  $ref?: string
  required?: string[]
  anyOf?: Schema[]
  oneOf?: Schema[]
  allOf?: Schema[]
  not?: Schema
  
  /** new attrs */ 
  validators?: ((value: any, formData: Record<string, any>) => boolean)[]
  errMsg?: Partial<Record<keyof (NumberKeywords & StringKeywords & ArrayKeywords) | 'type' | 'required', ErrMsg> | {validators?: ErrMsg[] }>
}

ui-schema(方案1)

export const common: UiSchema = {
  // *表示后面的按照schema顺序渲染
  order: ['switch', 'person', '*'],
  properties: {
    switch: {
      widget: 'switch',
      title: '是否打开'
    },
    city: {
      widget: 'select',
      title: '城市',
      attrs: {
        options: [
          {
            label: 'SHANGHAI',
            value: '1'
          }, 
          {
            label: 'GUANGDONG',
            value: '2'
          }
        ]
      }
    },
    areas: {
      widget: 'checkbox',
      title: '区域',
      attrs: {
        class: 'area-checkbox',
        style: {
          margin: '0 2px'
        },
        options: [
          {
            label: '区域1',
            value: 1
          }, 
          {
            label: '区域2',
            value: 2
          }
        ]
      }
    },
    email: {
      widget: 'input',
      title: '邮箱',
      on: {
        change: console.log
      }
    },
    person: {
      properties: {
        name: {
          widget: 'input',
          title: '姓名'
        },
        phone: {
          widget: 'input',
          title: '手机号',
          hidden (formData) {
            return !!formData.person.name
          }
        }
      }
    },
    types: {
      items: {
        properties: {
          count: {
            widget: 'input'
          },
          name: {
            widget: 'input'
          }
        }
      }
    },
    strategy: {
      properties (formData) {
        if(formData.switch) {
          return {
            name: {
              widget: 'input'
            },
            time: {
              widget: 'input'
            }
          }
        } else {
          return {
            name1: {
              widget: 'input'
            },
            time: {
              widget1: 'input'
            }
          }
        }
      }
    }
  }
}

type Component = ''

// 如果是函数,会做成响应式的效果,类似于computed
type functionalAttr<T> = T | ((formData: Record<string, any>, val: any) => T)

export interface UiSchema {

  // 渲染组件
  widget?: functionalAttr<string | Component>

  // 表单项标题
  title?: functionalAttr<string>

  // 描述
  description?: functionalAttr<string>

  // 是否隐藏
  hidden?: functionalAttr<boolean>

  // 组件attrs
  attrs?: functionalAttr<Record<string, any> | {
    style?: CSSStyleDeclaration
    class?: string | string[]
  }>

  // 事件回调
  on?: Record<string, (...args: any[]) => void>

  // 对象子属性
  properties?: functionalAttr<Record<string, UiSchema>>

  // 数组子属性
  items?: functionalAttr<UiSchema | UiSchema[]>

  // 渲染顺序,默认按照Object.keys的顺序
  order?: string[]
}

ui-schema(方案2)

type functionalAttr<T> = T | ((formData: Record<string, any>, val: any) => T)
type Component = ''

function formItem (label: string, ...children: UiSchema[]): UiSchema {
  return {
    widget: 'form-item',
    attrs: {
      label
    },
    children
  }
}

function formItemGroup (label: string, ...children: UiSchema[]): UiSchema {
  return {
    widget: 'form-item-group',
    attrs: {
      label
    },
    children
  }
}

export const common: UiSchema = {
  widget: 'form',
  children: [
    formItem('是否打开', {
      filed: 'switch',
      widget: 'switch'
    }),
    formItem('城市', {
      filed: 'city',
      widget: 'select',
      attrs: {
        options: [
          {
            label: 'SHANGHAI',
            value: '1'
          }, 
          {
            label: 'GUANGDONG',
            value: '2'
          }
        ]
      }
    }),
    formItem('区域', {
      filed: 'areas',
      widget: 'checkbox',
      attrs: {
        class: 'area-checkbox',
        style: {
          margin: '0 2px'
        },
        options: [
          {
            label: '区域1',
            value: 1
          },
          {
            label: '区域2',
            value: 2
          }
        ]
      }
    }),
    formItem('邮箱', {
      filed: 'email',
      widget: 'input',
      on: {
        change: console.log
      }
    },
    {
      widget: 'button',
      children: ['测试邮件'],
      on: {
        click: console.log
      }
    }),
    formItemGroup(
      '负责人', 
      formItem('姓名', {
        widget: 'input',
        filed: 'name'
      }),
      formItem('手机号', {
        widget: 'input',
        filed: 'phone',
        hidden (formData) {
          return !!formData.person.name
        }
      })
    ),
    formItem('类型', {
      widget: 'additional-list',
      filed: 'types',
      children: [
        {
          filed: 'count',
          widget: 'input'
        },
        {
          filed: 'name',
          widget: 'input'
        }
      ]
    })
    
  ]
}

interface UiSchema {
  widget?: functionalAttr<string | Component>

  filed?: string

  // 是否隐藏
  hidden?: functionalAttr<boolean>

  // 组件attrs
  attrs?: functionalAttr<Record<string, any> | {
    style?: CSSStyleDeclaration
    class?: string | string[]
  }>

  // 事件回调
  on?: Record<string, (...args: any[]) => void>

  children?: functionalAttr<(UiSchema | string)[]>
}

开发中问题汇总

  1. 为什么选择了pnpm,而不是yarn/npm
  2. vue2/vue3同时打包问题
  3. 怎么同时启动vue2和vue3的demo的?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.