npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

vue-element-customize

v1.1.11

Published

vue element-ui customize template

Downloads

36

Readme

功能列表

组件

CustomizeTable, CustomizePagination, CustomizeSearchBar, CustomizeSwitch, CustomizeForm, CustomizeDialogForm, CustomizePage, CustomizeFormBuilder

辅助函数

isEmpty, clean, setValue, deepClone, formatDate, sleep, deepMerge,

自定义样式

select, input, el-radio-group, el-checkbox-group [round]属性
input [no-border]属性

全量引入

import Customize from 'vue-element-customize';
Vue.use(Customize);

传入配置项

Vue.use(Customize, {
  CustomizeTable: {},
  CustomizePagination: {},
  CustomizeSearchBar: {},
  CustomizeSwitch: {},
  CustomizeForm: {},
  CustomizeDialogForm: {},
  CustomizeFormBuilder: {},
});

按需引入

组件引入

import { CustomizeTable, CustomizePagination, CustomizeSearchBar, CustomizeSwitch, CustomizeForm, CustomizeDialogForm, CustomizePage, CustomizeFormBuilder } from 'vue-element-customize';
Vue.use(CustomizeTable, {});
Vue.use(CustomizePagination, {});
Vue.use(CustomizeSearchBar, {});
Vue.use(CustomizeSwitch, {});
Vue.use(CustomizeForm, {});
Vue.use(CustomizeDialogForm, {});
Vue.use(CustomizePage);
Vue.use(CustomizeFormBuilder);

辅助函数引入

辅助函数全量引入
import { CustomizeUtils } from 'vue-element-customize';
Vue.use(CustomizeUtils);
辅助函数按需引入
import { isEmpty, clean, setValue, deepClone, formatDate, sleep, deepMerge } from 'vue-element-customize';
Vue.prototype._isEmpty = isEmpty;
Vue.prototype._clean = clean;
Vue.prototype._setValue = setValue;
Vue.prototype._deepClone = deepClone;
Vue.prototype._formatDate = formatDate;
Vue.prototype._sleep = sleep;
Vue.prototype._deepMerge = deepMerge;
自定义样式引入
import 'vue-element-customize/lib/styles/index.scss'

项目级配置

CustomizeTable配置

stripe: Boolean,  // true
border: Boolean,  // false
size: String,  // medium
headerCellStyle: Object,  // { backgroundColor: '#f5f7fa', color: '#909399',  borderBottom: 0,  height: '50px', },
cellStyle: Object,  // { borderBottom: 0 },
className: String,
align: String,

CustomizePagination配置

pageSizes: Array,  // [10,20,50,100]
layout: String,  // 'total, sizes, prev, pager, next, jumper'
align: String,  // right
className: String,
pageSize: Number,  // 10

CustomizeSearchBar配置

size: String,  // medium
className: String,  //''
blockButtons: Boolean,  // false
showResetButton: Boolean,  // true
openEnterSearch: Boolean,  // false
searchButtonName: String,  // 查询
optionAllName: String,  // 全部

CustomizeSwitch配置

defaultColor: Array,  // 默认的[打开背景色, 关闭背景色]
className: String,

CustomizeForm配置

size: String,  // medium
className: String,  //''
showSubmitButton: Boolean,  // true
showResetButton: Boolean,  // true
submitButtonName: String,  // 提交
resetButtonName: String,  // 重置
openEnterSubmit: Boolean,  // false
optionAllName: String,  // 全部
labelWidth: String,  // 120px
clearable: Boolean,  // true
hideOptionAll: Boolean,  // true

CustomizeDialogForm配置

form: Object,  // 弹窗内form的配置,同CustomizeForm
size: String,  // medium
closeButtonShow: [Boolean, String],  // true
closeButtonName: String,  // 关闭
className: String,  // dialog自定义class

CustomizeTable组件

组件配置

props
data: Array,  // 渲染的数据

column: Array,  // 表格配置
  {
    prop: String,  // 渲染数据的key值
    label: String, // key值对应的表头名
    width: String, //宽度
    sortable: Function,  //排序
    formatter: Function,  //(row, column, cellValue, index) {}
    slot: String,  // 传入的slot名
  }

size: String,
type: String,  // 'index'
headerCellStyle: Function,  // ({row, column, rowIndex, columnIndex}) {} '表头样式'
cellStyle: Object,
sortable: Boolean,
stripe: Boolean,
border: Boolean,
className: String,
maxHeight: [String, Number],
align: String,  // 'center'
emit
@sortChange  //排序
@selectionChange
ref
ref: 'table'
slot
slot before // 默认slot 前
slot after  // 默认slot 最后

example

<customize-table
  :data="tableData"
  :column="tableColumn"
  v-loading="tableLoading"
>
  <template #slotName>
    <el-table-column label="slot-slotName" align="center">
      <template #default="{ row }">
        <span>{{ _isEmpty(row.status) }}</span>
      </template>
    </el-table-column>
  </template>
  <template #after>
    <el-table-column label="操作" align="center">
      <template #default="{ row }">
        <el-button type="primary" size="mini" @click="handleEdit(row.id)">编辑</el-button>
      </template>
    </el-table-column>
  </template>
</customize-table>

data() {
  return {
    tableLoading: false,
    tableData: [],
    tableColumn: [
      // { type: 'index', index: (index) => (this.current - 1) * this.size + index + 1 },
      { label: '名称', prop: 'name' },
      { label: '属性', prop: 'aaa', width: '100px', w: '100px' },
      { label: '状态', prop: 'status', formatter(row, column, cellValue, index) { 
        const Enum = {
          0: '暂停',
          1: '启用',
        }
        return Enum[cellValue] || '--';
      }},
      { slot: 'slotName' }
    ],
  }
},

CustomizePagination组件

组件配置

props
current.sync: [Number, String],  // 当前页
size.sync: [Number, String],  // 当前每页个数
total: [Number, String],  // 总个数

pageSizes: Array,  // 每页显示个数选择器的选项设置
layout: String,  // 组件布局 'sizes, prev, pager, next, jumper, ->, total, slot',
align: String,  // 组件位置 text-align
className: String,
emit
@change

example

<customize-pagination
  :current.sync="current"
  :total="total"
  :size.sync="size"
  @change="handleChangePage"
  style="padding-bottom: 30px;"
></customize-pagination>

data() {
  return {
    current: 1,
    total: 0,
    size: 10,
  }
},

methods: {
  handleChangePage({ current, size }) {
    this.fetchData();
  },
},

CustomizeSearchBar组件

组件配置

props
loading.sync: false,  // 搜索按钮loading状态

setting: [{
  type: String,  // input/select
  label: String // 表单label名
  prop: String,  // 提交的字段名 必填
  placeholder: String,  // 默认为请输入/选择 label 
  default: Any, // 默认值
  width: String, // style中width
  rules: Array,  // 替换默认rule
  required: Boolean, // true/false
  msg: String, // 错误提示信息
  prefixIcon: String, // type input时,输入框icon
  className: String,  // type input时,el-form-item的class

  slot: String,  // slot  #slot="{ form }"

  enum: Array | Object,  // 直接传入枚举或数组  label value
  enumProp: String,  // select时,enum中传入的枚举属性值
  hideOptionAll: true, // select时,隐藏‘全部’选项传入true
  optionAllName: String,  // select,全部显示label名,默认全部
}],

enum: {
  [enumProp]: [{ label: '', prop: '' }]
}

validate: Boolean,  // 开启表单验证 配合rules
inlineButtons: Boolean  // 按钮不换行
size: String,  // 表单size
className: String,  // form自定义class
blockButtons: Boolean,  // 是否将按钮组换行展示,默认false
showResetButton: Boolean,  // 是否展示重置按钮,默认true
openEnterSearch: Boolean,  // 是否开启input的回车搜索,默认false
searchButtonName: String,  // 查询按钮文案
full-width: Boolean,  // inline模式铺满宽度,输入框宽度自适应
inline: Boolean,  // inline 默认true
margin: String,  // el-form-item margin-right '10px'
marginLeft: String,  // el-form-item margin-right设为0,margin-left为该px
showSearchButton: Boolean,  // 是否显示搜索按钮 默认true
immediate: Boolean,  // select/date,是否值改变时立即触发搜索,默认false
optionAllName: String,  // select全部选项的名称,默认全部
emit
@submit  // emit所有表单参数
slot
#after="form" 
ref
ref: form

example

<customize-search-bar
  :loading.sync="searchBarLoading"
  @submit="handleSubmitSearch"
  :setting="searchBarSetting"
  :Enum="searchBarEnum"
></customize-search-bar>

data() {
  return {
    searchBarLoading: false,
    searchBarSetting: [
      { type: 'input', label: '名称', prop: 'name' },
      { type: 'select', label: '分类', prop: 'type', enumProp: 'allList' },
      { slot: 'type', prop: 'type' },
    ],
    searchBarEnum: {
      allList: [],
    },
  }
},

methods: {
  fetchData() {
    this.searchBarLoading = false;
  },
  handleSubmitSearch(searchParams) {
    this.current = 1;
    this.fetchParams = { ...searchParams };
    this.fetchData();
  },
  async searchBarInit() {
    const { data } = await GetList({
      pageNo:1,
      paeSize: 100,
    });
    let allList = data.itemInfos || [];
    allList = allList.map(n => ({
      label: n.name,
      value: n.id,
    }))
    this.$set(this.searchBarEnum, 'allList', allList);
  },
},

slot example

<template #type="{ form }">
  <el-form-item label="分类">
    <el-select v-model="form.type" placeholder="请选择分类" @change="handleChange">
      <el-option label="全部" value=""></el-option>
      <el-option
        v-for="item in allList"
        :label="item.name"
        :value="item.id"
        :key="item.id"
      ></el-option>
    </el-select>
  </el-form-item>
</template>

CustomizeSwitch组件

组件配置

props
type: String,  // inside/right
mode: String,  // 同type

value/v-model: [Number, String, Boolean],

values: Array,  // [打开时value, 关闭时value]

text: Array,  // [打开时文案, 关闭时文案]
icon: Array,  // [打开时图标, 关闭时图标] 开启后覆盖text
color: Array,  // [打开时背景色, 关闭时背景色]
disabled: Boolean,  // 默认false,
width: Number,  // element中宽度,默认40
name: String,
validateEvent: Boolean,  // 改变后是否触发表单验证
activeTextColor: String,  // type为inside时文字颜色
className: String,
size: String,  // medium/small/mini
emit
@change
ref
ref: switch

example 双向绑定

<customize-switch
  v-model="switchValue"
  :text="['开启', '关闭']"
  :values="[1, 0]"
></customize-switch>

data() {
  return {
    switchValue: 1,
  }
},

example 非双向绑定

<customize-switch
  :value="switchValue"
  :disabled="switchDisabled"
  :text="['开启', '关闭']"
  :values="[1, 0]"
  @change="handleSwitchChange"
></customize-switch>

data() {
  return {
    switchValue: 1,
    switchDisabled: false,
  }
},

methods: {
  async handleSwitchChange(value) {
    try {
      this.switchDisabled = true;
      await this._sleep(1);
      this.switchValue = value;
      this.$message.success('操作成功');
    } catch (error) {
      
    }
    this.switchDisabled = false;
  },
},

example type="inside"

<customize-switch
  :value="switchValue"
  :disabled="switchDisabled"
  :text="['开启', '关闭']"
  :values="[1, 0]"
  @change="handleSwitchChange"
  type='inside'
  :width="54"
  :color="['#67C23A', '#909399']"
  active-text-color="#fff"
  size="small"
></customize-switch>

CustomizeForm组件

组件配置

props
loading.sync: false,  // 搜索按钮loading状态

setting: [{
  type: String,  // input/textarea/inputNumber/select/text/date/datepicker/switch/image/object/array/
  label: String // 表单label名
  prop: String,  // 提交的字段名 必填
  placeholder: String,  // 默认为请输入/选择 label 
  default: Any, // 默认值
  width: String, // style中width
  rules: Array,  // 替换默认rule
  required: Boolean, // true/false
  msg: String, // 错误提示信息
  validType: String,  // rules type, url/email/date
  prefixIcon: String, // type input时,输入框icon
  className: String,  // type input时,el-form-item的class
  labelWidth: String,  // label-width

  slot: String,  // slot  #slot="{ form }"
  slotInside: String,  // 插入至el-form-item内

  enum: Array | Object,  // 直接传入枚举或数组  label value
  enumProp: String,  // select时,enum中传入的枚举属性值
  enumKey: Array,  // select时,enum的key值,默认['label', 'value']
  hideOptionAll: true, // select时,隐藏‘全部’选项传入true
  optionAllName: String,  // select,全部显示label名,默认全部
  clearable: Boolean,  // date时,el-date-picker的 clearable,默认true
  disabled: Boolean,  // disabled状态,默认false
  dataType: Number/Boolean,  // 数据类型,默认String
  inputNumber: Boolean,  // true且type number时显示el-input-number

  visible: Array | Function,  // [属性名, 属性值] 设置后 当某属性为该值时,渲染该条。为function时return true时显示
}],

enum: {
  [enumProp]: [{ label: '', prop: '' }]
}

validate: Boolean,  // 开启表单验证 配合rules
size: String,  // 表单size
className: String,  // form自定义class
showSubmitButton: Boolean,  // 是否展示提交按钮,默认true
showResetButton: Boolean,  // 是否展示重置按钮,默认true
submitButtonName: String,  // 提交按钮文案,默认提交
resetButtonName: String,  // 重置按钮文案,默认重置
openEnterSearch: Boolean,  // 是否开启input的回车搜索,默认false
margin: String,  // el-form-item margin-bottom
immediate: Boolean,  // select/date,是否值改变时立即触发搜索,默认false
optionAllName: String,  // 下拉选择全部的名称,默认全部
labelWidth: String,  // el-form label-width,默认120px
itemWidth: String,  // form-item的宽度,默认auto
clearable: Boolean,  // el-date-picker clearable,默认true
hideOptionAll: Boolean, // select时,隐藏‘全部’选项传入true
hideOperate: Boolean,  // 隐藏操作el-form-item
cleanProps: Boolean,  // submit时仅传出setting中有的属性,删除其它属性
fn: Array,  // 传入的函数 配合image中fnName
emit
@submit  // emit所有表单参数
slot
#button-before
#button
slot-scope
#after="form" 
ref
ref: form

example

<template>
  <customize-form
    :loading.sync="formLoading"
    @submit="handleSubmit"
    :setting="formSetting"
    :Enum="formEnum"
    validate
  ></customize-form>
</template>

<script>
export default {
  data() {
    return {
      formLoading: false,
      formSetting: [
        { type: 'input', label: '名称', prop: 'name', disabled: true },
        { type: 'select', label: '分类', prop: 'type', enumProp: 'allList', enumKey: ['label', 'value'], default: '' },
        { slot: 'slotName', prop: 'slotName' },
        { type: 'text', label: '啊啊', default: 'text' },
        { type: 'radio', label: '单选', prop: 'radio', enum: {
          1: 'a',
          2: 'b',
        } },
        { type: 'checkbox', label: '多选', prop: 'checkbox', enum: [
          { label: 'aa', value: 11 },
          { label: 'bb', value: 22 },
          { label: 'cc', value: 33 },
          { label: 'dd', value: 44 },
          { label: 'ee', value: 55 },
        ], default: [] },
        { type: 'switch', label: '开关', prop: 'switch', text: ['开启', '关闭'], values: [1, 0], default: 0 },
        { type: 'switch', label: '开关', prop: 'switch', text: ['开启', '关闭'], values: [1, 0], default: 0 },
        { type: 'object', label: '对象', prop: 'obj', default: {}, children: [
          { type: 'input', label: '名称', prop: 'name', required: true },
          { type: 'select', label: '分类', prop: 'type', enum: { 1: 'a', 2: 'b' }, default: '1' },
        ] },
        { type: 'array', label: '数组', prop: 'arr', len: 3, default: [], children: [
          { type: 'input', label: '名称', prop: 'name' },
          { type: 'input', label: '名称2', prop: 'name2', required: true },
          { type: 'select', label: '分类', prop: 'type', enum: { 1: 'a', 2: 'b' }, default: '1' },
        ] },
        { type: 'image', label: '图片', prop: 'image', fn: this.uploadFn },
      ],
      formEnum: {
        allList: [],
      },
    }
  },

  methods: {
    async handleSubmit(form) {
      console.log(form)
      /* 提交表单 */
      await this._sleep(1);

      this.$message.success('提交成功');
      this.formLoading = false;
    },

    async uploadFn() {
      const url = '';
      await this._sleep(1);

      return Promise.resolve(url);
    },

    async formInit() {
      const data = {
        itemInfos: [
          { name: 'a', id: 1 },
          { name: 'b', id: 2 },
          { name: 'c', id: 3 },
        ],
      };
      await this._sleep(1);

      let allList = data.itemInfos || [];
      allList = allList.map(n => ({
        label: n.name,
        value: n.id,
      }))
      this.$set(this.formEnum, 'allList', allList);
    },
  },

  created() {
    this.formInit();
  },
}
</script>

slot example

<template #slotName="{ form }">
  <el-form-item label="分类">
    <el-select v-model="form.type" placeholder="请选择分类" @change="handleChange">
      <el-option label="全部" value=""></el-option>
      <el-option
        v-for="item in allList"
        :label="item.name"
        :value="item.id"
        :key="item.id"
      ></el-option>
    </el-select>
  </el-form-item>
</template>

CustomizeDialogForm组件

组件配置

props
visible.sync: Boolean,  // 弹窗是否显示
editData: Any,  // 以编辑进入时回显数据
title: [String, Array],  // 弹窗标题,为数组时[新建title, 编辑title]
top: String,  // el-dialog top
width: String,  // el-dialog width
size: String,  // medium
closeButtonShow:  [Boolean, String],  // 是否展示关闭弹窗按钮,默认展示。传入String时控制slot(button-before, button)
closeButtonName: String,  // 关闭弹窗按钮名称,默认关闭
className: String,  // dialog自定义class
其他prop同CustomizeForm
emit
@submit form
slot
同CustomizeForm
#button-before
#button
slot-scope
同CustomizeForm
#after="form" 
ref
ref: dialog

example

<customize-dialog-form
  title='123'
  :visible.sync="dialogVisible" 
  @submit="handleSubmitDialog"
  :loading.sync="submitLoading"
  :setting="formSetting"
  :Enum="formEnum"
  validate  
></customize-dialog-form>

<script>
export default {
  /* 作为单独组件引入时 */
  // props: {
  //   visible: Boolean,
  // },

  data() {
    return { 
      submitLoading: false,
      formSetting: [
        { type: 'input', label: '名称', prop: 'name', default: '1', required: true },
        { type: 'select', label: '分类', prop: 'type', enumProp: 'allList' },
        { slot: 'type', prop: 'type' },
      ],
      formEnum: {
        allList: [],
      },
  
      editData: null,

      dialogVisible: false,
    }
  },

  computed: {
    /* 作为单独组件引入时,外部 :visible.sync */
    // dialogVisible: {
    //   get() {
    //     return this.visible;
    //   },
    //   set(val) {
    //     this.$emit('update:visible', val);
    //   },
    // },
  },

  methods: {
    async handleSubmitDialog(form) {
      console.log(form);
      try {
        /* 提交表单 */
        await this._sleep(1)
        
        this.$message.success('提交成功');
        this.dialogVisible = false;
      } catch (error) {
        console.log('submitDialogError', error);
      }
      this.submitLoading = false;
    },

    handleCreate() {
      // 以创建进入
      this.editData = null 
      this.dialogVisible = true;    
    },
    handleEdit() {
      // 以编辑进入 同步数据
      this.editData = {
        name: 'name',
        type: 2,
      }
      this.dialogVisible = true;    
    },
    async handleEditAsync() {   
      // 以编辑进入 异步数据
      this.editData = null
      this.dialogVisible = true;
      await this._sleep(1);
      this.editData = { 
        name: 'name2',
        type: 3,
      }
    }, 
    
    async formInit() {
      const data = {
        itemInfos: [
          {name:'a',id:1},
          {name:'b',id:2},
          {name:'c',id:3},
        ]
      } 
      this._sleep(2)
      let allList = data.itemInfos || [];  
      allList = allList.map(n => ({ 
        label: n.name, 
        value: n.id,
      }))
      this.$set(this.formEnum, 'allList', allList); 
    },
  }
}
</script>

CustomizePage组件

example

<template>
  <customize-page
    :pageSetting="pageSetting"
    :searchBarProps="searchBarProps"
    :dialogProps="dialogProps"
    :Enum="Enum"
    :tableProps="tableProps"
    :tableDataFn="tableDataFn"
    :editDataFn="editDataFn"
    :submitFn="submitFn"
  >
    <template #slotName>
      <el-table-column label="slot" prop="slotName">
        <template #default="{ row }">
          {{ row.id }}
        </template>
      </el-table-column>
    </template>

    <template #tableButton="{ id }">
      <el-button type="danger" size="mini" @click="handleDelete(id)">删除</el-button>
    </template>
  </customize-page>
</template>

<script>
export default {
  data() {
    return {
      pageSetting: [
        { label: 'ID', prop: 'id', width: '100px' },
        { type: 'input', label: '名称', prop: 'name', required: true, msg: '321' },
        { type: 'select', label: '分类', prop: 'type', enumProp: 'allSelectList', enumKey: ['label', 'value'], default: '', rules: [
          { required: true, message: 'message' }
        ], formatter: (row, column, cellValue, index) => (this.Enum.allSelectList.find(n => n.value == cellValue) || {})['label'] || '' },
        { type: 'text', label: '啊啊', default: 'text', prop: 'text' },
        { type: 'radio', label: '单选', prop: 'radio', enum: {
          1: 'a',
          2: 'b',
        }, disabled: true },
        { type: 'checkbox', label: '多选', prop: 'checkbox', enum: [
          { label: 'aa', value: 11 },
          { label: 'bb', value: 22 },
          { label: 'cc', value: 33 },
          { label: 'dd', value: 44 },
          { label: 'ee', value: 55 },
        ], default: [] },
        { type: 'switch', label: '开关', prop: 'switch', text: ['开启', '关闭'], values: [1, 0], default: 0 },
        { slot: 'slotName', prop: 'slotName' },
      ],

      Enum: {
        allSelectList: [],
      },
      
      searchBarProps: ['name', 'type'],
      dialogProps: ['name', 'type', 'text', 'radio', 'checkbox', 'switch'],
      tableProps: ['id', 'name', 'type', 'slotName'],
    }
  },

  methods: {
    async tableDataFn(form) {
      console.log(form);

      /* 获取列表 */
      const data = [
        { id: '1', name: 'aaa', type: 1 },
        { id: '2', name: 'bbb', type: 2 },
        { id: '3', name: 'ccc', type: 3 },
        { id: '4', name: 'ddd', type: 1 },
      ];
      const total = 100;
      await this._sleep(1);

      return Promise.resolve({ data, total });
    },
    async editDataFn(id) {
      /* 获取详情 */
      const data = {
        id,
        name: '1',
        type: 2,
        checkbox: [22, 33],
        switch: 1,
      }
      this._sleep(1);

      return Promise.resolve(data);
    },
    async submitFn(form) {
      console.log(form);
      /* 提交表单 */
      await this._sleep(1);

      return Promise.resolve();
    },
    
    async init() {
      const allSelectList = [
        { label: 'a', value: 1 },
        { label: 'b', value: 2 },
        { label: 'c', value: 3 },
      ];
      await this._sleep(1);
      this.$set(this.Enum, 'allSelectList', allSelectList);
    },

    handleDelete(id) {
      this.$alert(`删除 ${id}`);
    },
  },

  created() {
    this.init();
  },

}
</script>

<style>
html, body {
  margin: 0;
}
</style>

CustomizeFormBuilder组件

组件配置

props
debug: Boolean,  // 实时显示formSetting数据
operate: Array,  // 操作区展示的按钮 ['import', 'clear', 'preview', 'schema', 'code']
event
getSchema,  // 获取当前配置转码的schema
setSchema,  // 设置schema字符串并重新渲染
slot
#operate-title 操作区域标题名
#operate-before 操作区域前置按钮
#operate-after 操作区域后置按钮

example

<template>
  <customize-form-builder ref="formBuilder"></customize-form-builder>
</template>

<script>
export default {
  methods: {
    getSchema() {
      const result = this.$refs.formBuilder.getSchema();
      console.log(result)
    },
    setSchema() {
      const schemaStr = `{"$schema":"http://json-schema.org/draft-04/schema#","type":"object","properties":{"input_1588506741033":{"type":"input","label":"单行文本","prop":"input_1588506741033","key":1588506741033}}}`
      this.$refs.formBuilder.setSchema(schemaStr);
    },
  },

}
</script>

辅助函数

_isEmpty

判断是否不为空
@return { Boolean }

_clean

删除对象中值为空的属性
@param { Object }
@return { Object }

_setValue

值不为空直接返回,为空返回默认值
@param { Any, Any } 需判断的值,默认值
@return { Any }

_formatDate

时间格式化
@params { [String, Number, Date], String } 时间,格式(默认 yyyy-MM-dd hh:mm:ss)
@return { String }

_deepClone

深拷贝
@params { Object }
@return { Object }

_sleep

延迟几秒后继续执行
@params { Number } 延迟秒数
@return { Promise } resolve

_deepMerge

对象深度合并
@params { Object, Object } 延迟秒数
@return { Object }
_deepMerge(a, b, [..option])
_deepMerge.all([a, b, c], [..option])

常用增删改查页面完整事例(无弹窗)

<template>
  <div class="home">
    <customize-search-bar
      :loading.sync="searchBarLoading"
      @submit="handleSubmitSearch"
      :setting="searchBarSetting"
    ></customize-search-bar>

    <customize-table
      :data="tableData"
      :column="tableColumn"
      v-loading="tableLoading"
    >
      <template #slotName>
        <el-table-column label="slot-slotName" align="center">
          <template #default="{ row }">
            <span>_isEmpty  {{ _isEmpty(row.status) }}</span>
          </template>
        </el-table-column>
      </template>
      <template #after>
        <el-table-column label="操作" align="center">
          <template #default="{ row }">
            <el-button type="primary" size="mini" @click="handleEdit(row.id)">编辑</el-button>
          </template>
        </el-table-column>
      </template>
    </customize-table>

    <customize-pagination
      :current.sync="current"
      :total="total"
      :size.sync="size"
      @change="handleChangePage"
      style="padding-bottom: 30px;"
    ></customize-pagination>
  </div>
</template>

<script>
export default {
  name: 'home',
  data() {
    return {
      /* 列表请求参数 */
      fetchParams: {},

      /* 搜索相关 */
      searchBarLoading: false,
      searchBarSetting: [
        { type: 'input', label: '名称', prop: 'name' },
        { type: 'select', label: '分类', prop: 'type', enum: {
          0: '暂停',
          1: '启用',
        } },
        { slot: 'type', prop: 'type' },
      ],

      /* 表格相关 */
      tableLoading: false,
      tableData: [],
      tableColumn: [
        // { type: 'index', index: (index) => (this.current - 1) * this.size + index + 1 },
        { label: '名称', prop: 'name' },
        { label: '属性', prop: 'aaa', width: '100px', w: '100px' },
        { label: '状态', prop: 'status', formatter(row, column, cellValue, index) { 
          const Enum = {
            0: '暂停',
            1: '启用',
          }
          return Enum[cellValue] || '--';
        }},
        { slot: 'slotName' }
      ],

      /* 分页相关 */
      current: 1,
      total: 0,
      size: 10,
    }
  },

  methods: {
    /* 请求数据 */
    async fetchData() {
      this.tableLoading = true;
      try {
        const response = await new Promise(resolve => {
          setTimeout(_ => {
            console.log(this._clean({
              ...this.fetchParams,
              page: this.current,
              size: this.size,
            }))
            const response = {
              data: [
                { name: 'a', aaa: 2, status: 0 },
                { name: 'b', aaa: 3, status: null },
                { name: 'c', aaa: 4, status: 1 },
                { name: 'd', aaa: 5, status: 0 },
              ],
              total: 100,
            }
            resolve(response);
          }, 1111)
        })

        // const response = await GetList(this._clean({
        //   ...this.fetchParams,
        //   page: this.current,
        //   size: this.size,
        // }))
        this.tableData = response.data || [];
        this.total = response.total || 0;
      } catch (error) {
        this.tableData = [];
        this.total = 0;
      }
      this.tableLoading = false;
      this.searchBarLoading = false;
    },

    /* 搜索相关 */
    handleSubmitSearch(searchParams) {
      this.current = 1;
      this.fetchParams = searchParams;
      this.fetchData();
    },

    /* 分页相关 */
    handleChangePage() {
      this.fetchData();
    },
  },

  created() {
    this.fetchData();
  },
}
</script>

常用增删改查页面完整事例(带弹窗)

<template>
  <div class="home">
    <customize-search-bar
      :loading.sync="searchBarLoading"
      @submit="handleSubmitSearch"
      :setting="searchBarSetting"
    >
      <template #button>
        <el-button type="primary" @click="handleAdd">新建</el-button>
      </template>
    </customize-search-bar>

    <customize-table
      :data="tableData"
      :column="tableColumn"
      v-loading="tableLoading"
    >
      <template #slotName>
        <el-table-column label="slot-slotName" align="center">
          <template #default="{ row }">
            <span>_isEmpty  {{ _isEmpty(row.status) }}</span>
          </template>
        </el-table-column>
      </template>
      <template #after>
        <el-table-column label="操作" align="center">
          <template #default="{ row }">
            <el-button type="primary" size="mini" @click="handleEdit(row.id)">编辑</el-button>
          </template>
        </el-table-column>
      </template>
    </customize-table>

    <customize-pagination
      :current.sync="current"
      :total="total"
      :size.sync="size"
      @change="handleChangePage"
      style="padding-bottom: 30px;"
    ></customize-pagination>

    <dialog-create
      :editId="editId" 
      :visible.sync="dialogVisible" 
      @submit="handleSubmitDialog"
    ></dialog-create>
  </div>
</template>

<script>
import DialogCreate from './DialogCreate';

export default {
  components: {
    DialogCreate,
  },

  data() {
    return {
      /* 列表请求参数 */
      fetchParams: {},

      /* 搜索相关 */
      searchBarLoading: false,
      searchBarSetting: [
        { type: 'input', label: '名称', prop: 'name' },
        { type: 'select', label: '分类', prop: 'type', enum: {
          0: '暂停',
          1: '启用',
        } },
        { slot: 'type', prop: 'type' },
      ],

      /* 表格相关 */
      tableLoading: false,
      tableData: [],
      tableColumn: [
        // { type: 'index', index: (index) => (this.current - 1) * this.size + index + 1 },
        { label: '名称', prop: 'name' },
        { label: '属性', prop: 'aaa', width: '100px', w: '100px' },
        { label: '状态', prop: 'status', formatter(row, column, cellValue, index) { 
          const Enum = {
            0: '暂停',
            1: '启用',
          }
          return Enum[cellValue] || '--';
        }},
        { slot: 'slotName' }
      ],

      /* 分页相关 */
      current: 1,
      total: 0,
      size: 10,

      /* 弹窗相关 */
      dialogVisible: false,
      editId: null,
    }
  },

  methods: {
    /* 请求数据 */
    async fetchData() {
      this.tableLoading = true;
      try {
        const response = await new Promise(resolve => {
          setTimeout(_ => {
            
            const response = {
              data: [
                { id: 1, name: 'a', aaa: 2, status: 0 },
                { id: 2, name: 'b', aaa: 3, status: null },
                { id: 3, name: 'c', aaa: 4, status: 1 },
                { id: 4, name: 'd', aaa: 5, status: 0 },
              ],
              total: 100,
            }
            resolve(response);
          }, 222)

          // const response = await GetList(this._clean({
          //   ...this.fetchParams,
          //   page: this.current,
          //   size: this.size,
          // }))
          this.tableData = response.data || [];
          this.total = response.total || 0;
        })
      } catch (error) {
        this.tableData = [];
        this.total = 0;
      }
      this.tableLoading = false;
      this.searchBarLoading = false;
    },

    /* 搜索相关 */
    handleSubmitSearch(searchParams) {
      this.current = 1;
      this.fetchParams = searchParams;
      this.fetchData();
    },

    /* 分页相关 */
    handleChangePage() {
      this.fetchData();
    },

    /* 弹窗相关 */
    handleAdd() {
      this.editId = null;
      this.dialogVisible = true;
    },
    handleEdit(id) {
      this.editId = id;
      this.dialogVisible = true;
    },
    handleSubmitDialog() {
      console.log('handleDialog');
      this.fetchData();
    },
  },

  created() {
    this.fetchData();
  },
}
</script>

<template>
  <el-dialog
    title="title"
    top="15vh"
    width="600px"
    :visible.sync="dialogVisible"
    @close="handleClosed"
  >
    <el-form :model="form" :rules="rules" ref="form" label-width="180px" size="small">
      <el-form-item label="名称:" prop="name">
        <el-input v-model="form.name"></el-input>
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="handleSubmit" :loading="loading">提交</el-button>
        <el-button @click="handleCancel">取消</el-button>
      </el-form-item>
    </el-form>
  </el-dialog>
</template>

<script>
const DEFAULT_FORM = () => ({
  name: '',
});

export default {
  props: {
    visible: Boolean,
    editId: {},
  },

  data() {
    return {
      dialogVisible: false,

      form: DEFAULT_FORM(),
      rules: {
        name: { required: true, message: '请输入名称' },
      },
      loading: false,
    }
  },

  methods: {
    handleClosed() {
      // reset here
      this.$refs.form.resetFields();
      this.form = DEFAULT_FORM();
      this.$emit('update:visible', false);
    },

    async handleSubmit() {
      console.log(this.form);
      try {
        await this.$refs.form.validate();

        await this.$confirm(`是否确定提交`, "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        });

        this.loading = true;
        let params = this._deepClone(this.form);
        if (!!this.editId) {
          params.id = this.editId;
          await Update(params);
        }else{
          await Save(params);
        }

        this.$message.success(`${!!this.editId ? '编辑' : '新增'}成功`);
        this.dialogVisible = false;
        this.$emit('submit');
      } catch (error) {
        console.log(error);
      }
      this.loading = false;
    },
    handleCancel() {
      this.dialogVisible = false;
    },
  },

  watch: {
    visible: {
      handler(newVal, oldVal) {
        this.dialogVisible = newVal;
      },
      immediate: true,
    }
  },

}
</script>

<style lang='scss' scoped>

</style>

常用增删改查页面完整事例(带弹窗)单页

<template>
  <div class="home">
    <customize-search-bar
      :loading.sync="searchBarLoading"
      @submit="handleSubmitSearch"
      :setting="searchBarSetting"
    >
      <template #button>
        <el-button type="primary" @click="handleAdd">新建</el-button>
      </template>
    </customize-search-bar>

    <customize-table
      :data="tableData"
      :column="tableColumn"
      v-loading="tableLoading"
    >
      <template #slotName>
        <el-table-column label="slot-slotName" align="center">
          <template #default="{ row }">
            <span>_isEmpty  {{ _isEmpty(row.status) }}</span>
          </template>
        </el-table-column>
      </template>
      <template #after>
        <el-table-column label="操作" align="center">
          <template #default="{ row }">
            <el-button type="primary" size="mini" @click="handleEdit(row.id)">编辑</el-button>
          </template>
        </el-table-column>
      </template>
    </customize-table>

    <customize-pagination
      :current.sync="current"
      :total="total"
      :size.sync="size"
      @change="handleChangePage"
      style="padding-bottom: 30px;"
    ></customize-pagination>

    <customize-dialog-form
      :editData="editData"
      title='title'
      :visible.sync="dialogVisible" 
      @submit="handleSubmitDialog"
      :loading.sync="formLoading"
      :setting="formSetting"
      :Enum="formEnum"
      validate  
    ></customize-dialog-form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      /* 列表请求参数 */
      fetchParams: {},

      /* 搜索相关 */
      searchBarLoading: false,
      searchBarSetting: [
        { type: 'input', label: '名称', prop: 'name' },
        { type: 'select', label: '分类', prop: 'type', enum: {
          0: '暂停',
          1: '启用',
        } },
        { slot: 'type', prop: 'type' },
      ],

      /* 表格相关 */
      tableLoading: false,
      tableData: [],
      tableColumn: [
        // { type: 'index', index: (index) => (this.current - 1) * this.size + index + 1 },
        { label: '名称', prop: 'name' },
        { label: '属性', prop: 'aaa', width: '100px', w: '100px' },
        { label: '状态', prop: 'status', formatter(row, column, cellValue, index) { 
          const Enum = {
            0: '暂停',
            1: '启用',
          }
          return Enum[cellValue] || '--';
        }},
        { slot: 'slotName' }
      ],

      /* 分页相关 */
      current: 1,
      total: 0,
      size: 10,

      /* 弹窗相关 */
      formLoading: false,
      formSetting: [
        { type: 'input', label: '名称', prop: 'name', required: true, msg: '321' },
        { type: 'select', label: '分类', prop: 'type', enumProp: 'allList', enumKey: ['label', 'value'], default: '', rules: [
          { required: true, message: 'message' }
        ] },
        { slot: 'slotName', prop: 'slotName' },
        { type: 'text', label: '啊啊', default: 'text' },
        { type: 'radio', label: '单选', prop: 'radio', enum: {
          1: 'a',
          2: 'b',
        }, disabled: true },
        { type: 'checkbox', label: '多选', prop: 'checkbox', enum: [
          { label: 'aa', value: 11 },
          { label: 'bb', value: 22 },
          { label: 'cc', value: 33 },
          { label: 'dd', value: 44 },
          { label: 'ee', value: 55 },
        ], default: [] },
        { type: 'switch', label: '开关', prop: 'switch', text: ['开启', '关闭'], values: [1, 0], default: 0 },
      ],
      formEnum: {
        allList: [],
      },
      dialogVisible: false,
      editData: null,
    }
  },

  methods: {
    /* 请求数据 */
    async fetchData() {
      const params = this._clean({
        ...this.fetchParams,
        current: this.current,
        size: this.size,
      });
      console.log(params);
      this.tableLoading = true;
      try {
        const response = {
          data: [
            { id: 1, name: 'a', aaa: 2, status: 0 },
            { id: 2, name: 'b', aaa: 3, status: null },
            { id: 3, name: 'c', aaa: 4, status: 1 },
            { id: 4, name: 'd', aaa: 5, status: 0 },
          ],
          total: 100,
        }
        await this._sleep(1);

        this.tableData = response.data || [];
        this.total = response.total || 0;
      } catch (error) {
        this.tableData = [];
        this.total = 0;
      }
      this.tableLoading = false;
      this.searchBarLoading = false;
    },

    /* 搜索相关 */
    handleSubmitSearch(searchParams) {
      this.current = 1;
      this.fetchParams = searchParams;
      this.fetchData();
    },

    /* 分页相关 */
    handleChangePage() {
      this.fetchData();
    },

    /* 弹窗相关 */
    handleAdd() {
      this.editData = null
      this.dialogVisible = true;
    },
    async handleEdit(id) {
      this.dialogVisible = true;

      await this._sleep(1);
      const editData = {
        id,
        name: '1',
        type: 2,
        checkbox: [22, 33],
        switch: 1,
      }
      this.editData = editData;
    },
    async handleSubmitDialog(form) {
      console.log(form)
      /* 提交表单 */
      await this._sleep(1);

      this.$message.success('提交成功');
      this.formLoading = false;
      this.dialogVisible = false;
      this.editData = null;
    },

    async formInit() {
      const data = {
        itemInfos: [
          { name: 'a', id: 1 },
          { name: 'b', id: 2 },
          { name: 'c', id: 3 },
        ],
      };
      await this._sleep(1);

      let allList = data.itemInfos || [];
      allList = allList.map(n => ({
        label: n.name,
        value: n.id,
      }))
      this.$set(this.formEnum, 'allList', allList);
    },
  },

  created() {
    this.fetchData();
    this.formInit();
  },
}
</script>