vue3中,渲染动态表单(三)——梳理动态表单的几种类型、树结构表格row-key、父子传值之props、emit和defineEmits & ElLoading组件用法和post请求动态地址
效果图
1-主页面
2-树结构表格
3-新增
4-编辑
1、主页
index.vue
<span class="text-btn" @click="edit(scope.row)">定义申请表</span>
<script>
const edit = (row: any) => {
console.log(row);
router.push('/myCenter/defineForm')
}
</script>
2、定义申请表管理
src\views\myCenter\applyForm\components\defineForm.vue
<!--
@Description 申请表管理 - 定义申请表
@author wdd
@date 2023/12/13
-->
<template>
<centerHead title="定义申请表"></centerHead>
<div class="content">
<div class="left">
<el-button type="primary" @click="add">新增</el-button>
<el-table ref="tableRef" border class="table-style" :data="tableData" lazy :load="treeLoad" row-key="id"
stripe style="width: 100%; margin-bottom: 20px"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @selection-change="handleChange">
<el-table-column prop="name" align="center" label="字段名称"></el-table-column>
<el-table-column prop="prop" align="center" label="字段参数"></el-table-column>
<el-table-column prop="type" align="center" label="字段类型"></el-table-column>
<el-table-column prop="rules" align="center" label="是否必填"></el-table-column>
<el-table-column prop="placeholder" align="center" label="提示语"></el-table-column>
<el-table-column prop="sort" align="center" label="字段序号"></el-table-column>
<el-table-column prop="hasChildren" align="center" label="是否有子集"></el-table-column>
<el-table-column label="操作" align="right" width="160">
<template #default="scope">
<span class="text-btn" v-if="scope.row.hasChildren"
@click="edit('addChild',scope.row)">新增子项</span>
<span class="text-btn" @click="edit('edit',scope.row)">编辑</span>
<span class="del-btn" @click="onDelete(scope.row)">删除</span>
</template>
</el-table-column>
</el-table>
<div>
<el-button type="primary" @click="onSave">提交</el-button>
<el-button type="primary" @click="see">生成预览</el-button>
</div>
<FieldInfo v-if="showField" v-model:dialogVisible="showField" :info-data="fieldInfo"
@emit-confirm="confirmOk" />
<ExampleInfo v-if="showExample" v-model:dialogVisible="showExample" :info-data="exampleInfo"
@emit-confirm="exampleOk" />
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import FieldInfo from './fieldInfo.vue'
import ExampleInfo from './exampleInfo.vue'
const router = useRouter();
const tableData = ref([{
id: 1,
name: '姓名',
prop: 'name',
type: '输入框',
rules: '是',
placeholder: '请输入',
sort: 1,
hasChildren: false
}, {
id: 2,
name: '性别',
prop: 'sex',
type: '下拉框',
rules: '是',
placeholder: '请选择',
sort: 2,
hasChildren: false
}, {
id: 3,
name: '备注',
prop: 'notes',
type: '多行文本',
rules: '否',
placeholder: '请输入',
sort: 3,
hasChildren: false
}, {
id: 4,
name: '手机号',
prop: 'telephone',
type: '手机',
rules: '是',
placeholder: '请输入',
sort: 4,
hasChildren: false
}, {
id: 5,
name: '身份证号',
prop: 'IDCard',
type: '身份证号',
rules: '否',
placeholder: '请输入',
sort: 5,
hasChildren: false
}, {
id: 6,
name: '邮箱',
prop: 'email',
type: '邮箱',
rules: '否',
placeholder: '请输入',
sort: 6,
hasChildren: false
}, {
id: 7,
name: '年龄',
prop: 'age',
type: '数字',
rules: '否',
placeholder: '请输入',
sort: 7,
hasChildren: false
}, {
id: 8,
name: '金额',
prop: 'money',
type: '数字',
rules: '否',
placeholder: '请输入',
sort: 8,
hasChildren: false
}, {
id: 9,
name: '作品来源',
prop: 'source',
type: '单选',
rules: '是',
sort: 9,
hasChildren: false
}, {
id: 10,
name: '项目属性',
prop: 'attribute',
type: '多选',
rules: '是',
sort: 10,
hasChildren: false
}, {
id: 11,
name: '年度',
prop: 'year',
type: '年度',
rules: '是',
placeholder: '请选择',
sort: 11,
hasChildren: false
}, {
id: 12,
name: '月份',
prop: 'month',
type: '年月',
placeholder: '请选择',
rules: '是',
sort: 12,
hasChildren: false
}, {
id: 13,
name: '出生日期',
prop: 'birthday',
type: '日期',
placeholder: '请选择',
rules: '是',
sort: 13,
hasChildren: false
}])
const showField = ref(false)
const fieldInfo = ref({})
const add = () => {
showField.value = true
fieldInfo.value = {
// id: fieldId,
typeName: 'add'
}
}
const confirmOk = () => {
tableData.value = []
// getList()
}
const edit = (type: any, row: any) => {
showField.value = true
row.typeName = type
// row.fieldId = directoryId
fieldInfo.value = { ...row }
router.push('/myCenter/defineForm')
}
// 懒加载
const treeLoad = (tree, treeNode, resolve) => {
setTimeout(() => {
const params = {
pid: tree.id,
}
const dataList = ref([])
maps.set(tree.id, { tree, treeNode, resolve })
post(constant.ieopCollaborate + '/collaborate/directory/list', params).then((res) => {
const data = res.data.data || []
data.forEach((el) => {
if (el.hasChildren) {
el.children = null
}
})
dataList.value = data
resolve(dataList.value)
})
}, 200)
}
// 批量删除
const ids = ref([])
const handleChange = (val: any) => {
ids.value = val.map((item: any) => item.id)
}
const onDelete = (row: any) => {
console.log(row);
ElMessageBox.confirm('此操作将删除该费率, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
ElMessage.success('删除成功!')
history.go(0)
}).catch(() => {
ElMessage.error('删除失败!')
})
}
const showExample = ref(false)
const exampleInfo = ref({})
const see = () => {
showExample.value = true
exampleInfo.value = {
// id: fieldId,
// typeName: 'add'
}
}
const exampleOk = () => {
// tableData.value = []
// getList()
}
const onSave = () => { }
</script>
<style lang="scss" scoped>
.content {
margin-top: 20px;
margin-right: 20px;
width: 90%;
.text-btn {
font-size: 12px;
color: #409EFF;
margin-left: 4px;
cursor: pointer;
}
.del-btn {
font-size: 12px;
color: #F56C6C;
margin-left: 4px;
cursor: pointer;
}
.el-button {
float: right;
margin: 0 0 10px 10px;
}
.left {
padding-left: 16px;
.el-table {
margin: 20px 0;
}
}
}
</style>
3、新增申请表字段
src\views\myCenter\applyForm\components\fieldInfo.vue
<!--
@Description 申请表管理 - 定义申请表 - 新增/编辑字段弹框
@author wdd
@date 2023/12/14
-->
<template>
<div>
<el-dialog :title="titleText" v-model="dialogVisible" :close-on-press-escape="false"
:close-on-click-modal="false" width="36%">
<div style="padding-right:40px">
<el-form label-width="100px" ref="formRef" :model="formInline">
<el-form-item label="字段名称:" prop="name" :rules="{
required: true,
message: '请输入',
trigger: 'blur',
}">
<el-input v-model="formInline.name"></el-input>
</el-form-item>
<el-form-item label="字段参数:" prop="prop" :rules="{
required: true,
message: '请输入',
trigger: 'blur',
}">
<el-input v-model="formInline.prop"></el-input>
</el-form-item>
<!-- 动态表单类型 typeList -->
<el-form-item label="字段类型:" prop="type" :rules="{
required: true,
message: '请输入',
trigger: 'blur',
}">
<el-select v-model="formInline.type" placeholder="请选择" style="width:100%">
<el-option v-for="item in typeList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="是否必填:" prop="rules">
<el-radio-group v-model="formInline.rules" class="ml-4">
<!-- <el-radio label="1" size="large">是</el-radio>
<el-radio label="2" size="large">否</el-radio> -->
<el-radio :label="'是'">是</el-radio>
<el-radio :label="'否'">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="字段序号:" prop="sort" :rules="{
required: true,
message: '请输入',
trigger: 'blur',
}">
<el-input placeholder="请输入大于0的正整数" @input="changeSort" v-model="formInline.sort"></el-input>
</el-form-item>
<el-form-item label="提示语:" prop="placeholder">
<el-select style="width:100%" v-model="formInline.placeholder">
<el-option value="请输入" label="请输入"></el-option>
<el-option value="请选择" label="请选择"></el-option>
</el-select>
</el-form-item>
<el-form-item v-if="formInline.hasChildren" label="是否有子集:" prop="hasChildren">
<el-radio-group v-model="formInline.hasChildren" class="ml-4">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<template #footer>
<span slot="footer" class="dialog-footer">
<el-button link type="primary" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirm()">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, defineProps, defineEmits, computed, watch, inject } from 'vue';
import { post } from "@/utils/path.js";
import { ElLoading, ElMessage } from "element-plus";
const formRef = ref()
const dataObj = ref()
const titleText = ref('')
const constant = inject('constant')
const formInline = ref({
name: '',
prop: '',
type: '',
sort: '',
rules: '否',
placeholder: '',
hasChildren: false,
})
// 动态表单类型
const typeList = ref([
{
label: "输入框",
value: "text"
}, {
label: "数字",
value: "number"
}, {
label: "下拉框",
value: "select"
}, {
label: "日期",
value: "date"
}, {
label: "年月",
value: "month"
}, {
label: "年度",
value: "year"
}, {
label: "起止日期",
value: "datetimerange"
},
// {
// label:"富文本框",
// value:"editor"
// },
{
label: "单选",
value: "radio"
}, {
label: "多选",
value: "checkbox"
}, {
label: "手机",
value: "telephone"
}, {
label: "邮箱",
value: "email"
}, {
label: "身份证",
value: "IDCard"
},
// {
// label:"list集合",
// value:"list"
// },
// {
// label: "封面",
// value: "thumb"
// },
{
label: "附件",
value: "file"
}, {
label: "多行文本",
value: "textarea"
},
// {
// label:"视频",
// value:"video"
// },
// {
// label: "电话",
// value: "phone"
// },
])
const props = defineProps({
infoData: {
default: null,
type: Object,
},
dialogVisible: {
type: Boolean,
default() {
return false
},
},
})
//监听方法
const emit = defineEmits(['emit-confirm', 'update:dialogVisible'])
const dialogVisible = computed({
get: () => props.dialogVisible,
set: (val) => emit('update:dialogVisible', val),
})
watch(
() => props.dialogVisible,
(newVal) => {
if (newVal) {
dataObj.value = JSON.parse(JSON.stringify(props.infoData))
if (dataObj.value.typeName == 'add') {
titleText.value = '新增字段'
}
if (dataObj.value.typeName == 'addChild') {
formInline.value.pid = dataObj.value.id
formInline.value.hasChildren = dataObj.value.hasChildren
titleText.value = '新增子项'
}
if (dataObj.value.typeName == 'edit') {
formInline.value = { ...dataObj.value }
formInline.value.id = dataObj.value.id
titleText.value = '编辑字段'
}
}
},
{ immediate: true }
)
const changeSort = (val: any) => {
const pattern = /^[1-9][0-9]*$/
if (!pattern.test(val)) {
formInline.value.sort = ''
}
}
const confirm = async () => {
await formRef.value.validate((valid: any) => {
if (!valid) {
return
} else {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
const params = {
...formInline.value
}
const url = dataObj.value.typeName == 'addChild' ? '/collaborate/directory/add' : '/collaborate/directory/addRoot'
const urlInfo = dataObj.value.typeName == 'edit' ? '/collaborate/directory/update' : url
post(constant.ieopCollaborate + urlInfo, params).then((res) => {
const { code } = res.data
if (code == '200') {
loading.close()
ElMessage.success(dataObj.value.typeName == 'edit' ? '修改成功' : '新增成功')
emit('update:dialogVisible', false)
formInline.value = {}
emit('emit-confirm')
} else {
loading.close()
ElMessage.error(res.message)
emit('update:dialogVisible', false)
}
})
.catch(() => {
loading.close()
})
}
})
}
</script>
4、引用文件
src\utils\path.js
import request from "./request";
import { getApiUrl } from "@/utils/tool";
const baseUrl = getApiUrl();
// 通用请求
export function post(url, params = {}){
const data = request.post(baseUrl + url, params);
// const data = request.post(url, params);
return data
}
export function get(url, params = {}){
const data = request.get(baseUrl + url);
return data
}
src\utils\tool.js
export function getApiUrl(v) {
return process.env.VUE_APP_API_HOST
}
})
}
})
}
#### 4、引用文件
src\utils\path.js
```js
import request from "./request";
import { getApiUrl } from "@/utils/tool";
const baseUrl = getApiUrl();
// 通用请求
export function post(url, params = {}){
const data = request.post(baseUrl + url, params);
// const data = request.post(url, params);
return data
}
export function get(url, params = {}){
const data = request.get(baseUrl + url);
return data
}
src\utils\tool.js
export function getApiUrl(v) {
return process.env.VUE_APP_API_HOST
}