ll-form-table
基于vue2.6和element-ui封装的以数据驱动视图的表单表格组件,将表单、表格由`template`语法转换成`json`语法。通过json配置模板,快速完成表单、表格的开发。
330
| Install | |
|---|---|
npm install ll-form-table |
|
| Latest Version: | 2.0.1 |
| Vue: | 2 |
| License: | MIT |
| Last Updated: | Jun 5, 2025 |
| Links: | GitHub · npm |
Maintainer: stoneliang
ll-form-table
基于vue2.6和element-ui封装的表单、表格组件。
以数据驱动视图,将表单、表格由template语法转换成json语法。通过json配置模板,快速完成表单、表格的开发。
本地开发
node版本要求:
node >= 12.0.0 && node <= 14.0.0
安装依赖
npm install
运行示例
npm run serve
使用
安装
npm install ll-form-table
使用方法
import Vue from 'vue';
import LlFormTable, { LlFrom, LlTable, LlTableStatic, useTable } from 'll-form-table';
// 全局注册表单
Vue.use(LlForm);
// 全局注册表格
Vue.use(LlTable);
Vue.use(LlTableStatic);
// 当同时注册LlTable和LlTableStatic时,可以使用useTable快捷注册这两个组件
Vue.use(useTable);
// 全部注册,同时注册表单、表格
Vue.use(LlFormTable);
示例
表单示例
<template>
<ll-form
:form-items="formItems"
:model="formData"
:rules="rules"
:post-data="submitForm"
button-position="center"
label-width="100px"
class="ll-form-warp"
></ll-form>
</template>
<script>
import { LlForm } from 'll-form-table';
export default {
name: 'SimpleDemo',
components: { LlForm },
data() {
return {
formItems: [
{ type: 'input', label: '账号', prop: 'account' },
{
// 通过render和viewRender自定义表单项
label: 'nickname',
// labelRender优先级高于label
labelRender: () => {
return <span>昵称</span>;
},
// prop: 'nickname',
tips: '请输入昵称',
render: () => {
return (
<el-input
type='text'
placeholder='昵称'
size='small'
{...{
props: {
value: this.formData.nickname
},
on: {
input: val => {
this.formData.nickname = val;
}
}
}}></el-input>
);
},
// viewRender优先级大于this.model[prop]
viewRender: () => <span>{this.formData.nickname}</span>
},
{
type: 'inputNumber',
label: '年龄',
prop: 'age',
formElementProps: {
// width: 200,
min: 1,
max: 200,
step: 1,
placeholder: '年龄'
}
},
{
type: 'select',
label: '头衔',
prop: 'title',
formElementProps: {
placeholder: '头衔',
options: [
{ label: 'Tester', value: '3' },
{ label: 'Java developer', value: '2' },
{ label: 'Web Developer', value: '1' }
],
loading: false,
filterable: true,
multiple: true
}
},
{
type: 'input',
prop: 'edu.highSchool',
label: '高中'
},
{
type: 'input',
prop: 'edu.university',
label: '大学'
},
{
type: 'input',
label: '数组绑定',
prop: 'arr.0.value',
style: { color: 'red' },
formElementProps: {
on: {
change: val => {
this.$message.success(`value change:${val}`);
},
focus: e => {
console.log('focus event', e);
this.$message.success('input focus');
}
}
}
}
],
formData: {
account: 'stone',
nickname: 'stone',
age: 18,
title: [],
edu: {
highSchool: '',
university: ''
},
arr: [{ value: '数组绑定值' }]
},
rules: {
account: [
{ required: true, message: '请输入账号', trigger: 'blur' },
{
trigger: 'change',
validator: (rule, value, callback) => {
if (/^[a-zA-Z0]+$/.test(value)) {
callback();
} else {
callback(new Error('只能输入字母,不能为空'));
}
}
}
],
nickname: [
{ required: true, message: '请输入昵称', trigger: 'change' },
{ min: 3, message: '昵称长度至少为3个字符', trigger: 'change' }
]
},
formLoading: false
};
},
methods: {
submitForm(formData) {
console.log('formData', formData);
this.formLoading = true;
return new Promise(resolve => {
setTimeout(() => {
this.formLoading = false;
this.$message.success('提交成功');
resolve('success');
}, 1500);
});
}
}
};
</script>
示例渲染结果如下所示:

表格示例
<template>
<ll-table-static
ref="table"
border
:row-key="'id'"
:params.sync="params"
:data.sync="data"
:columns="columns"
:action-column="actionColumn"
:drag-sortable="true"
drag-sort-model="thead"
:editable="true"
:auto-save="false"
:enter-save="true"
:disable-h-scroll="true"
:empty-placeholder="'--'"
:column-filterable="true"
:pagination-selectable="true"
:pagination-props="{
layout: 'slot, ->, sizes, prev, pager, next, jumper, total',
prevText: 'Prev'
}"
:layout="'tool, pagination, table, pagination'"
:row-draggable="true"
:expand-rows.sync="expandRowKeys"
:fix-height="200"
:deep-select="false"
:selection.sync="selections"
:page-size="10"
:current-page.sync="currentPage"
:column-filter-selected="selectedColumns"
:column-filter-always-selected="['name']"
custom-table-class="my-table-class"
@column-reset="handlerFilterReset"
@selection-change="handlerSelect"
@row-sort="handlerRowSorted"
@row-column-confirm="handlerColumnFilterConfirm"
@cell-change="saveData"
@data-edit="handlerDataEdit"
>
<template slot="tool" slot-scope="{ selection, allSelection }">
选中的当前页数据的rowkey: {{ selection }}<br />
选中的所有行数据的rowKey{{ allSelection }}
<tool-bar
ref="toolBar"
:loading="toolBarLoading"
:keyword.sync="query.keyword"
@search="handlerSearch"
@reset="resetTable"
@clear="clearAllSelection"
@add="handlerAdd"
@delete="handlerWarpper(handlerDelete, selection)"
></tool-bar>
</template>
<template slot="extra">这是附加内容</template>
<template slot="empty">没有数据</template>
<template slot="pagination" slot-scope="{ selection }">
<div>
<el-button type="danger" @click="handlerWarpper(handlerDelete, selection)">删除</el-button>
</div>
</template>
<template slot="append">这是append插槽内容</template>
<!-- 操作列操作按钮定义 -->
<template slot="columnActions">
<el-button
type="primary"
icon="el-icon-s-tools"
order="2"
size="mini"
@click="handlerActionColumnClick"
>
设置
</el-button>
</template>
<!-- 模板定义表格列 -->
<el-table-column prop="nametest" label="nametest" display-weight="150">
<template slot-scope="{ row }">
<span class="src-info-value">test--{{ row.name }}</span>
</template>
</el-table-column>
</ll-table-static>
</template>
<script>
import ToolBar from './ToolBar.vue';
export default {
name: 'TableStatic',
components: { ToolBar },
data() {
return {
expandRowKeys: ['1'],
selectedColumns: [],
params: {},
currentPage: 1,
selections: ['1', '1_1', '3', '5'],
data: [
{
name: 'stone',
role: 'developer',
id: '1',
js: 90,
css: 90,
detail: { id: 'id_1' }, // row-key可配置为detail.id,这样子,detail.id可以作为row-key
children: [
{ name: 'stone_1', role: 'developer', id: '1_1', detail: { id: 'id_1_1' } },
{ name: 'stone_2', role: 'developer', id: '1_2', detail: { id: 'id_1_2' } },
{
name: 'stone_3',
role: 'developer',
id: '1_3',
detail: { id: 'id_1_3' },
children: [{ name: 'stone_3_1', role: 'developer', id: '1_3_1', detail: { id: 'id_1_3_1' } }]
}
]
},
{ name: 'zhangsan', role: 'developer', id: '2', js: 80, css: 90, detail: { id: 'id_2' } },
{ name: 'lisi', role: 'tester', id: '3', js: 80, css: 90, detail: { id: 'id_3' } },
{ name: 'wangwu', role: 'designer', id: '4', js: 80, css: 80, detail: { id: 'id_4' } },
{ name: 'wangwu_1', role: 'designer', id: '5', js: 80, css: 80, detail: { id: 'id_5' } },
{ name: 'wangwu_2', role: 'designer', id: '6', js: 80, css: 80, detail: { id: 'id_6' } },
{ name: 'wangwu_3', role: 'designer', id: '7', js: 80, css: 80, detail: { id: 'id_7' } },
{ name: 'wangwu_4', role: 'designer', id: '8', js: 80, css: 80, detail: { id: 'id_8' } },
{ name: 'wangwu_5', role: 'designer', id: '9', js: 80, css: 80, detail: { id: 'id_9' } },
{ name: 'wangwu_6', role: 'designer', id: '10', js: 80, css: 80, detail: { id: 'id_10' } },
{ name: 'wangwu_7', role: 'designer', id: '11', js: 80, css: 80, detail: { id: 'id_11' } },
{ name: 'wangwu_8', role: 'designer', id: '12', js: 80, css: 80, detail: { id: 'id_12' } },
{ name: 'wangwu_9', role: 'designer', id: '13', js: 80, css: 80, detail: { id: 'id_13' } },
{ name: 'wangwu_10', role: 'designer', id: '14', js: 80, css: 80, detail: { id: 'id_14' } }
],
columns: [
{
label: 'ID',
prop: 'id',
order: 10,
displayWeight: 2,
render: (h, { row }) => {
return parseInt(row.id) > 5 ? (
<el-tag type='success'>{row.id}</el-tag>
) : (
<el-tag type='danger'>{row.id}</el-tag>
);
}
},
{ type: 'selection', width: 50 },
{
type: 'index',
width: 50,
label: '#',
displayWeight: 1,
header: function() {
return <div>#$</div>;
}
},
{
label: '姓名',
prop: 'name',
order: 11,
minWidth: 300,
sortable: true,
editable: true
// 自己实现表格可编辑
// render: (h, row) => {
// return (
// <el-input vModel={row.name}></el-input>
// );
// }
},
{
label: '角色',
prop: 'role',
minWidth: 300,
displayWeight: 4,
editable: true,
clickEdit: true, // 点击单元格触发编辑
editorFormatter: val => val, // 编辑内容格式化
header: () => {
// (h, scopr) 可以通过scope获得相关信息
return (
<div>
<label>角色</label>
<input
type='text'
{...{
props: { value: this.query.keyword },
on: {
input: e => {
this.query.keyword = e.target.value;
// todo,执行你的筛选逻辑,前端搜索、后端搜索;或者观察'search'来执行搜索
},
click: function(e) {
e.stopPropagation(); // 点击输入框不触发排序
}
},
style: {
marginLeft: '10px'
}
}}></input>
</div>
);
}
},
{
label: '技能熟练度',
prop: 'skill',
minWidth: '300px',
displayWeight: 3,
order: 12,
children: [
// 子列没order属性
{ label: 'JS', prop: 'js', sortable: true, minWidth: '100px' },
{ label: 'CSS', prop: 'css', sortable: true, minWidth: '100px' }
]
}
],
actionColumn: {
props: {
label: '操作', // 可不传,默认值为‘操作’
width: '200px'
},
// 操作按钮
buttons: [
{
order: 1,
icon: 'el-icon-edit',
type: 'info',
label: '',
// 控制el-button的属性
props: {
// 上面的icon和type属性可不设置,在这里设置
circle: true,
type: 'info',
icon: 'el-icon-edit',
size: 'mini'
},
handler: ({ row }) => {
this.$emit('edit', row);
}
}
],
// 渲染函数,精确控制操作按钮
render: (h, scope) => {
const { row, $index } = scope;
return (
<div>
<el-button
size='mini'
icon='el-icon-delete'
circle
{...{
on: {
click: () => {
this.$emit('delete', row);
}
}
}}
/>
<el-button
size='mini'
icon='el-icon-top'
title='上移'
circle
{...{
on: {
click: () => {
this.handleClickMove('up', row, $index);
}
}
}}
/>
<el-button
size='mini'
icon='el-icon-bottom'
title='下移'
circle
{...{
on: {
click: () => {
this.handleClickMove('down', row, $index);
}
}
}}
/>
</div>
);
}
},
query: { keyword: '' },
toolBarLoading: {
deleteLoading: false,
searchLoading: false
}
};
},
watch: {
data(d) {
console.log('data change', d);
}
},
created() {
this.$on('delete', this.handlerDelete);
},
methods: {
handlerWarpper(func, scope) {
return func.call(this, scope);
},
handlerSearch(nameSearch) {
console.log('handerSearch', nameSearch);
this.$message.success('keyword:' + nameSearch);
this.query = nameSearch;
this.search();
},
handlerAdd() {
this.$emit('add');
this.$message.success('trigger add');
},
handlerDelete(keys) {
console.log('handlerDelete', keys);
const newDatas = this.data.filter(item => {
return !keys.includes(item.key);
});
this.data = newDatas;
this.$$message.success('trigger delete:' + JSON.stringify(keys));
},
handlerSortChange({ prop, order }) {
this.query.headSort = prop;
this.query.headSortType = order && order.replace('ending', '');
this.search();
},
handlerClickMove(...args) {
this.$refs.table.rowClickSort(...args);
},
handlerRowSorted(data, options) {
console.log('rowSorted', data, options);
const { behavior } = options;
// 节点提升
if (behavior === 1) {
this.$confirm('确定提升节点吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
this.$message.success('节点提升了');
})
.catch(() => {
this.$message.info('取消节点提升');
this.$refs.table.revertTableData();
});
}
},
search() {
this.$refs.table.doRequest(this.query);
},
customStyle({ columnIndex }) {
if (columnIndex === this.columns.length) {
return {
'border-right': '0'
};
}
},
handlerTableEdit(data) {
console.log('data edited', data);
this.data = data;
},
handlerFilterReset(selectedColumnProps) {
console.log('filter reset', selectedColumnProps);
},
handlerColumnFilterConfirm(checked) {
console.log('filter confirm', checked);
},
handlerSelect(rows) {
console.log('selected change', rows);
},
saveData(val, prop, row) {
console.log('cell change', val, prop, row);
// todo
},
handlerDataEdit(tableData) {
console.log('data edit', tableData);
},
handlerActionColumnClick(scope) {
console.log('action column click', scope);
},
resetTable() {
this.$refs.table.resetTable();
},
clearAllSelection() {
this.$refs.table.clearAllSelection();
}
}
};
</script>
<style lang="scss" scoped>
::v-deep .ll-table__table {
margin: 15px 0;
}
</style>
渲染结果如下所示:

Related Packages
el-search-table-pagination
A component which combines form、table with pagination components in Element UI.
3,527
303
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 2.0.1 | ^2.6.6 | MIT |