弹窗表单配置
提示
表格中的弹出的表单是内置组件avue-form组件,配置属性可以参考FORM 组件文档
默认值
配置value为字段默认值
<template>
<avue-crud :option="option"
:data="data"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name',
value: 'small'
}
]
});
</script>
打开表单前
提示
你可以在打开前和关闭前做一些逻辑操作,例如打开根据 id 请求数据,关闭根据 id 保存数据等 v3.5.2 新增 loading 方法
打开表单前会执行beforeOpen方法,相关返回的方法值可以判断表单当前打开的类型是新增还是编辑,调用done方法打开弹窗
<template>
<avue-crud :data="data"
v-model="form"
:before-open="beforeOpen"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessageBox } from 'element-plus';
const form = ref({});
const data = ref([{
name: '张三',
sex: '男'
}]);
const option = {
column: [
{
label: '姓名',
prop: 'name'
},
{
label: '性别',
prop: 'sex'
}
]
};
const beforeOpen = (done, type, loading) => {
ElMessageBox.alert(`我是${type}`, '提示', {
confirmButtonText: '确定'
}).then(() => {
if (['view', 'edit'].includes(type)) {
// 查看和编辑逻辑
} else {
// 新增逻辑
form.value.name = '初始化赋值';
}
done();
}).catch(() => {
// 处理取消或关闭的逻辑(如果有需要)
});
};
</script>
加载数据
提示
3.5.2+
打开表单前会执行beforeOpen方法,调用loading加载等待数据,调用done完成加载
<template>
<avue-crud :data="data"
v-model="form"
:before-open="beforeOpen"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessageBox } from 'element-plus';
const form = ref({});
const data = ref([{
name: '张三',
sex: '男'
}]);
const option = {
column: [
{
label: '姓名',
prop: 'name'
},
{
label: '性别',
prop: 'sex'
}
]
};
const beforeOpen = (done, type, loading) => {
if (['view', 'edit'].includes(type)) {
loading()
setTimeout(() => {
done()
}, 300)
} else {
// 新增逻辑
form.value.name = '初始化赋值';
done();
}
};
</script>
关闭表单前
关闭表单前会执行beforeClose方法,执行返回的done方法后才会彻底关闭表单
<template>
<avue-crud :data="data"
v-model="form"
:before-close="beforeClose"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessageBox } from 'element-plus'
const form = ref({});
const data = ref([{
name: '张三',
sex: '男'
}]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name'
},
{
label: '性别',
prop: 'sex'
}
]
});
const beforeClose = (done, type) => {
ElMessageBox.confirm('确认关闭?')
.then(() => {
done();
})
.catch(() => { });
};
</script>
不关弹窗连续添加
<template>
<avue-crud ref="crud"
:data="data"
v-model="form"
@row-save="rowSave"
:before-open="beforeOpen"
:before-close="beforeClose"
:option="option">
<template #menu-form="{ row, index }">
<el-button type="primary"
icon="el-icon-check"
plain
v-if="type === 'add'"
@click="handleNext(row, index)">继续添加</el-button>
</template>
</avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElButton, ElMessage } from 'element-plus';
import 'element-plus/theme-chalk/el-message.css';
const form = ref({});
const flag = ref(false);
const type = ref('');
const data = ref([]);
const option = ref({
align: 'center',
menuAlign: 'center',
viewBtn: true,
column: [
{
label: '姓名',
prop: 'name'
}, {
label: '性别',
prop: 'sex'
}
]
});
const handleNext = (row, index) => {
flag.value = true;
// @ts-ignore
crud.value.rowSave();
};
const rowSave = (form, done, loading) => {
data.value.push(deepClone(form));
ElMessage.success(JSON.stringify(form));
if (flag.value) {
flag.value = false;
loading();
form.name = "";
form.sex = "";
return;
}
done();
};
const beforeClose = (done) => {
flag.value = false;
done();
};
const beforeOpen = (done, type) => {
type.value = type;
done();
};
const deepClone = (obj) => {
return JSON.parse(JSON.stringify(obj));
};
const crud = ref(null);
</script>
表单按钮位置
配置dialogMenuPosition属性值即可,默认为right
<template>
<el-radio-group v-model="direction">
<el-radio label="left">居左</el-radio>
<el-radio label="center">居中</el-radio>
<el-radio label="right">居右</el-radio>
</el-radio-group>
<br /><br />
<avue-crud :option="option"
:data="list"></avue-crud>
</template>
<script setup>
import { ref, watch } from 'vue';
const direction = ref('right');
const list = ref([
{
id: 1,
name: '张三',
sex: 12
}, {
id: 2,
name: '李四',
sex: 12
}
]);
const option = ref({
dialogMenuPosition: 'right',
column: [
{
label: 'id',
prop: 'id'
}, {
label: '姓名',
prop: 'name'
}, {
label: '年龄',
prop: 'sex'
}
]
});
watch(direction, (value) => {
option.value.dialogMenuPosition = value;
});
</script>
打开表单方式
配置dialogType为弹窗的方式,dialogDirection为弹窗的位置
<template>
<el-radio-group v-model="direction">
<el-radio label="ltr">从左往右开</el-radio>
<el-radio label="rtl">从右往左开</el-radio>
<el-radio label="ttb">从上往下开</el-radio>
<el-radio label="btt">从下往上开</el-radio>
</el-radio-group>
<br /><br />
<avue-crud :option="option"
:data="list"></avue-crud>
</template>
<script setup>
import { ref, watch } from 'vue';
const direction = ref('rtl');
const list = ref([
{
name: '张三',
sex: 12
}, {
name: '李四',
sex: 12
}
]);
const option = ref({
dialogDirection: 'rtl',
dialogType: 'drawer',
column: [
{
label: '姓名',
prop: 'name'
}, {
label: '年龄',
prop: 'sex'
}
]
});
watch(direction, (value) => {
option.value.dialogDirection = value;
});
</script>
防重提交
为了防止数据重复提交,加入了防重提交机制,rowSave方法和rowUpdate方法返回done用于关闭表单方法和loading用于关闭禁止的表单不关闭表单
<template>
<avue-crud :data="data"
:option="option"
@row-save="handleRowSave"
@row-update="handleRowUpdate"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const data = ref([
{ name: '张三', sex: '男' },
{ name: '李四', sex: '女' }
]);
const option = {
column: [
{ label: '姓名', prop: 'name' },
{ label: '性别', prop: 'sex' }
]
};
const handleRowSave = (row, done, loading) => {
ElMessage.success('1秒后关闭禁止表单');
setTimeout(() => {
loading();
ElMessage.success('3秒后关闭表单');
}, 1000);
setTimeout(() => {
done();
}, 3000);
};
const handleRowUpdate = (row, index, done, loading) => {
ElMessage.success('1秒后关闭禁止表单');
setTimeout(() => {
loading();
ElMessage.success('3秒后关闭表单');
}, 1000);
setTimeout(() => {
done();
}, 3000);
};
</script>
标题字段宽度
labelWidth为标题的宽度,默认为90,可以配置到option下作用于全部,也可以单独配置每一项
<template>
<avue-crud :data="data"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ name: '张三', sex: '男' },
{ name: '李四', sex: '女' }
]);
const option = ref({
labelWidth: 150,
column: [
{
labelWidth: 30,
label: '姓名',
prop: 'name'
},
{
label: '性别',
prop: 'sex'
}
]
});
</script>
验证
提示
具体参考async-validator
配置验证字段的rules的数据对象
<template>
<avue-crud :data="data"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ name: '张三', sex: '男' },
{ name: '李四', sex: '女' }
]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name',
rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
},
{
label: '性别',
prop: 'sex',
rules: [{ required: true, message: '请输入性别', trigger: 'blur' }]
}
]
});
</script>
自定义验证
提示
自定义校验 callback 必须被调用。 更多高级用法可参考 async-validator。
<template>
<avue-crud :data="data"
v-model="obj"
:option="option"
@error="handleError"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const obj = ref({});
const data = ref([]);
const validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
callback();
}
};
const validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== obj.value.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
const option = ref({
column: [
{
label: '密码',
prop: 'password',
rules: [{ required: true, validator: validatePass, trigger: 'blur' }]
},
{
label: '确认密码',
prop: 'oldpassword',
rules: [{ required: true, validator: validatePass2, trigger: 'blur' }]
}
]
});
const handleError = (err) => {
ElMessage.success('请查看控制台');
console.log(err);
};
</script>
组件对象
打开表单的时候可以获取相关字段的 ref 对象
<template>
<avue-crud ref="crud"
:data="data"
:before-open="beforeOpen"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const data = ref([{
text: '测试数据'
}]);
const option = ref({
labelWidth: 120,
column: [{
label: '测试框',
prop: 'text',
}]
});
const crud = ref(null);
const beforeOpen = (done) => {
done();
setTimeout(() => {
ElMessage.success('查看控制台');
console.log('text的ref对象');
console.log(crud.value.getPropRef('text'));
}, 0);
};
</script>
字段不同状态
disabled、display、detail等字段在新增和编辑不同状态下,字段的不同状态展示
<template>
<avue-crud :data="data"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{
name: '张三',
sex: '男',
grade: 0
}, {
name: '李四',
sex: '女',
grade: 1
}
]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name',
editDisabled: true
}, {
label: '性别',
prop: 'sex',
editDisplay: false
}, {
label: '权限',
prop: 'grade',
editDetail: true,
addDisabled: true
}, {
label: '测试',
prop: 'test',
addDisplay: false
}
]
});
</script>
深结构绑定
bing绑定深层次的结构对象,prop也是需要填写
<template>
<avue-crud :option="option"
:data="data"
v-model="form">
<template #bind-form="{}">
<el-button type="primary"
@click="changeDeepValue">改变deep.deep.deep.value值</el-button>
<el-button type="primary"
@click="changeTestValue">改变test值</el-button>
<br /><br />
{{ form }}
</template>
</avue-crud>
</template>
<script setup>
import { ref } from 'vue';
import { ElButton } from 'element-plus';
const form = ref({});
const data = ref([{
deep: {
deep: {
deep: {
value: '我是深结构'
}
}
}
}]);
const option = {
labelWidth: 120,
column: [
{
label: '深结构',
prop: 'test',
bind: 'deep.deep.deep.value'
},
{
label: '',
prop: 'bind',
span: 24,
hide: true
}
]
};
const changeDeepValue = () => {
form.value.deep.deep.deep.value = '改变deep.deep.deep.value值';
};
const changeTestValue = () => {
form.value.test = '改变test值';
};
</script>
字段排序
配置order的序号可以实现表单和表格字段不同的顺序
<template>
<avue-crud :data="data"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{
name: '张三',
sex: '男'
}, {
name: '李四',
sex: '女'
}
]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name'
}, {
label: '性别',
prop: 'sex',
order: 1
}
]
});
</script>
表单窗口拖拽
dialogDrag设置为true即可拖动表单
<template>
<avue-crud :data="data"
:option="option"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ name: '张三', sex: true },
{ name: '李四', sex: false }
]);
const option = {
dialogDrag: true,
column: [
{ label: '姓名', prop: 'name' },
{ label: '性别', prop: 'sex' }
]
};
</script>
与其它字段交互
<template>
<avue-crud :data="data"
:option="option"
v-model="form"></avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([{
text1: 0
}]);
const form = ref({});
const option = {
column: [{
label: '内容1',
prop: 'text1',
type: 'radio',
dicData: [{
label: '显示',
value: 0
}, {
label: '隐藏',
value: 1,
}],
control: (val, form) => {
if (val === 0) {
return {
text2: {
display: true
},
text3: {
label: '内容3'
}
};
} else {
return {
text2: {
display: false
},
text3: {
label: '有没有发现我变了'
}
};
}
},
}, {
label: '内容2',
prop: 'text2',
display: true
}, {
label: '内容3',
prop: 'text3'
}]
};
</script>
表单分组
提示
<template>
<avue-crud :option="option"
v-model="form"
:data="data"></avue-crud>
</template>
<script>
let baseUrl = 'https://api.avuejs.com/area'
export default {
data () {
return {
form: {
name: '11'
},
data: [{
name: '张三',
sex: 15,
province: '110000',
city: '110100',
area: '110101',
checkbox: ['110000']
}],
option: {
column: [{
label: '姓名',
prop: 'name',
display: false
},
{
label: '年龄',
prop: 'sex',
display: false
}],
group: [
{
label: '用户信息',
prop: 'jbxx',
icon: 'el-icon-edit-outline',
column: [
{
label: '姓名',
prop: 'name'
},
{
label: '年龄',
prop: 'sex'
}
]
}, {
label: '退款申请',
prop: 'tksq',
icon: 'el-icon-view',
column: [
{
label: '省份',
prop: 'province',
type: 'select',
props: {
label: 'name',
value: 'code'
},
cascader: ['city'],
dicUrl: `${baseUrl}/getProvince`,
rules: [
{
required: true,
message: '请选择省份',
trigger: 'blur'
}
]
},
{
label: '城市',
prop: 'city',
type: 'select',
cascader: ['area'],
props: {
label: 'name',
value: 'code'
},
cascaderIndex: 0,
dicUrl: `${baseUrl}/getCity/{{key}}`,
rules: [
{
required: true,
message: '请选择城市',
trigger: 'blur'
}
]
},
{
label: '地区',
prop: 'area',
type: 'select',
props: {
label: 'name',
value: 'code'
},
cascaderIndex: 0,
dicUrl: `${baseUrl}/getArea/{{key}}`,
rules: [
{
required: true,
message: '请选择地区',
trigger: 'blur'
}
]
}
]
}
, {
label: '用户信息',
prop: 'yhxxs',
icon: 'el-icon-edit-outline',
column: [
{
label: '测试长度',
prop: 'len',
maxlength: 5,
}, {
labelWidth: 120,
label: '测试自定义',
prop: 'names'
}
]
}
]
}
}
}
}
</script>数据过滤
filterDic设置为true返回的对象不会包含$前缀的字典翻译, filterNull设置为true返回的对象不会包含空数据的字段
<template>
<avue-crud :key="reload"
v-model="form"
:option="option"
:data="data">
<template #name-form="{ size }">
<el-button type="danger"
@click="filterDic">过滤数据字典</el-button>
<el-button type="success"
@click="filterNull">过滤空数据</el-button>
<el-button type="primary"
@click="filter">不过滤</el-button>
<p>{{ form }}</p>
<el-input v-model="form.name"
:size="size"></el-input>
</template>
</avue-crud>
</template>
<script setup>
import { ref, reactive } from 'vue';
const data = ref([
{
cascader: [0, 1],
tree: 0
}
]);
const reload = ref(Math.random());
const form = reactive({});
const option = reactive({
column: [
{
label: '姓名',
span: 24,
prop: 'name',
},
{
label: '级别',
prop: 'cascader',
type: 'cascader',
dicData: [
{
label: '一级',
value: 0,
children: [
{
label: '一级1',
value: 1,
},
{
label: '一级2',
value: 2,
}
]
}
]
},
{
label: '树型',
prop: 'tree',
type: 'tree',
dicData: [
{
label: '一级',
value: 0,
children: [
{
label: '一级1',
value: 1,
},
{
label: '一级2',
value: 2,
}
]
}
]
}
]
});
function refresh () {
reload.value = Math.random();
}
function filter () {
option.filterDic = false;
option.filterNull = false;
refresh();
}
function filterDic () {
option.filterDic = true;
refresh();
}
function filterNull () {
option.filterNull = true;
refresh();
}
</script>
render 渲染表单
<template>
<avue-crud :data="data"
:option="option"
v-model="form"></avue-crud>
</template>
<script setup>
import { h, ref } from 'vue';
const form = ref({});
const data = ref([
{
name: '张三',
sex: '男'
}, {
name: '李四',
sex: '女'
}
]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name',
renderForm: ({ row }) => {
return h('p',
{
id: 'box',
class: { 'demo': true },
style: { color: 'pink', lineHeight: '30px' },
}, row.name || 'name');
}
},
{
label: '性别',
prop: 'sex'
}
]
});
</script>
自定义表单
在卡槽中指定列的prop加上-form作为卡槽的名称开启自定义
<template>
<avue-crud :data="data"
:option="option"
v-model="form">
<template #name-form="{ type, disabled }">
<el-tag v-if="type === 'add'">新增</el-tag>
<el-tag v-else-if="type === 'edit'">修改</el-tag>
<el-tag v-else-if="type === 'view'">查看</el-tag>
<el-tag>{{ user.name ? user.name : '暂时没有内容' }}</el-tag>
<el-input :disabled="disabled"
v-model="user.name"></el-input>
</template>
</avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const form = ref({});
const data = ref([
{ name: '张三', sex: '男' },
{ name: '李四', sex: '女' }
]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name',
rules: [
{
required: true,
message: "请输入姓名",
trigger: "blur"
}
]
},
{
label: '性别',
prop: 'sex'
}
]
});
</script>
自定义表单错误提示
在卡槽中指定列的prop加上-error作为卡槽的名称开启自定义
<template>
<avue-crud :data="data"
:option="option">
<template #name-error="{ error }">
<p style="color:green">自定义提示{{ error }}</p>
</template>
</avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ sex: '男' },
{ sex: '女' }
]);
const option = {
column: [
{
label: '姓名',
prop: 'name',
rules: [
{
required: true,
message: '请输入姓名',
trigger: 'blur'
}
]
},
{
label: '性别',
prop: 'sex'
}
]
};
</script>
提示
3.6.4+
自定义表单描述信息
在卡槽中指定列的prop加上-desc作为卡槽的名称开启自定义
<template>
<avue-crud :data="data"
:option="option">
<template #name-desc>
<p style="color:blue">这是姓名字段的自定义描述信息</p>
</template>
</avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ name: '张三', sex: '男' },
{ name: '李四', sex: '女' }
]);
const option = {
column: [
{
label: '姓名',
prop: 'name',
rules: [
{
required: true,
message: '请输入姓名',
trigger: 'blur'
}
]
},
{
label: '性别',
prop: 'sex'
}
]
};
</script>
自定义表单标题
在卡槽中指定列的prop加上-label作为卡槽的名称开启自定义
<template>
<avue-crud :data="data"
:option="option">
<template #name-label="{}">
<span>姓名 </span>
<el-tooltip class="item"
effect="dark"
content="文字提示"
placement="top-start">
<i class="el-icon-warning"></i>
</el-tooltip>
</template>
</avue-crud>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ name: '张三', sex: '男' },
{ name: '李四', sex: '女' }
]);
const option = ref({
column: [
{
label: '姓名',
prop: 'name',
rules: [
{
required: true,
message: '请输入姓名',
trigger: 'blur'
}
]
},
{
label: '性别',
prop: 'sex'
}
]
});
</script>
