一个后端管理系统,有大量的表格,表单,所以基于element ui的el-table,el-form里面的表单元素进行二次封装是很有必要的,也可以保证每个页面的统一
接下来的两个组件由我和另外两个同事在使用中不断优化,目前比较稳定
1.目录
2.案例
下面两个文件,是我如何使用组件完成基本的table页面
common-table.vue组件
<template>
<div style="padding:30px">
<!-- 3将组件写入到页面 -->
<!-- 7如果由额外的参数也可以用extraFormData传入 -->
<commonTable
border
:column="versionManageColumn"
:pagination="versionManagePagi"
:data-url="verifyManageUrl"
:filter-btn="filterBtn"
:extra-form-data="extraFormData"
>
<template #operate>
<el-button type="info">查看信息</el-button>
</template>
<template #packageName="{ scope }">
<div style="color:red">{{scope.row.packageName}}(嘿嘿嘿,我要变红)</div>
</template>
</commonTable>
</div>
</template>
<script>
// 4传入参数到组件里面,column必传,data-url必传,pagination必传,如果不需要分页,传versionManagePagi: {hide: true}
// 凡是定义的常量,都可以重新写一个js,引入进来使用,不然页面会很多常量,例如这里的versionManageColumn
import { versionManageColumn, filterBtn } from './commonTableOption'
// 1引入commonTable组件
import commonTable from '@/components/CommonTable'
export default {
components: {
commonTable, // 2注入组件
},
data() {
return {
versionManagePagi: {
pageSize: 20,
currentPage: 1,
total: 0,
},
versionManageColumn,
verifyManageUrl: 'http://47.110.148.106:3030/mock/11/version/manage',
filterBtn,
extraFormData: {
name: '花花',
}
}
}
}
</script>
commonTableOption.js主要是定义常量
export const versionManageColumn = [
{
prop: 'versionName',
label: '版本名称',
filterOption: {
type: 'input',
label: '版本名称',
prop: 'versionName',
placeholder: '请输入',
formChildWidth: '250px',
},
},
{
prop: 'packageName',
label: '包名称',
},
{
prop: 'versionDesc',
label: '版本说明',
},
// 5比较强大的地方在于能够直接配置进行去搜索
{
prop: 'isForceUpdate',
label: '是否强制更新',
filterOption: {
type: 'select',
label: '是否强制更新',
prop: 'isForceUpdate',
placeholder: '请选择',
disabled: false,
multiple: false,
filterable: true,
clearable: false,
selectData: [
{ label: '是', value: 0 }, { label: '否', value: 1 },
],
formChildWidth: '250px',
},
},
{
prop: 'installEquipmentNum',
label: '安装设备数',
},
{
prop: 'proportion',
label: '占比',
},
{
prop: 'versionPublishTime',
label: '版本发布时间',
},
{
prop: 'operate',
label: '操作',
},
]
// 6按钮操作也可以进行配置
export const filterBtn = { type: 'button', children:
[
{ type: 'search' },
{ type: 'reset' },
{
type: 'export',
apiUrl: 'xxx',
isLimit: true,
fileName: '咕咕咕',
},
],
}
3.组件代码 (结合目录哦)
文件1 CommonTable/index.vue
<template>
<div>
<el-form
ref="form"
class="form-flex"
:label-width="formLabelWidth"
:disabled="formDisabled"
:model="formData"
:rules="formRules"
:inline="inline"
>
<el-form-item
v-for="(formItem, itemIndex) in formFields"
v-show="!formItem.hidden"
:key="itemIndex"
:prop="formItem.prop"
:label-width="formItem.type === 'button' ? '0px': formItem.labelWidth"
:label="formItem.label"
:class="formItem.class"
>
<div
v-if="formItem.type === 'extraMention'"
:class="formItem.class"
>{{ formItem.prop }}</div>
<!-- 输入框或者textarea-->
<el-input
v-if="formItem.type === 'input'"
v-model="formData[formItem.prop]"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:clearable="formItem.clearable"
:style="{ formItem.formChildWidth}"
:maxlength="formItem.maxlength"
/>
<el-input
v-if="formItem.type === 'textarea'"
v-model="formData[formItem.prop]"
:placeholder="formItem.placeholder"
type="textarea"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
:clearable="formItem.clearable"
:maxlength="formItem.maxlength"
:autosize="formItem.autoSize"
:rows="formItem.rows || 2"
/>
<el-input
v-if="formItem.type === 'number'"
v-model.number="formData[formItem.prop]"
:placeholder="formItem.placeholder"
type="number"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
:clearable="formItem.clearable"
:min="formItem.min"
:max="formItem.max"
/>
<!-- 下拉框 -->
<el-select
v-if="formItem.type === 'select'"
v-model="formData[formItem.prop]"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
:multiple="formItem.multiple"
:filterable="formItem.filterable"
:clearable="formItem.clearable"
@change="val => selectChange(val, formItem.prop)"
>
<el-option
v-for="(selectItem, index) in formItem.selectData"
:key="index"
:label="selectItem.label"
:value="selectItem.value"
/>
</el-select>
<!-- 时间控件 -->
<el-date-picker
v-if="formItem.type === 'datePicker'"
v-model="formData[formItem.prop]"
:type="formItem.dateType"
:value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
:format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
range-separator="至"
:start-placeholder="formItem.placeholderStart"
:end-placeholder="formItem.placeholderEnd"
:class="formItem.class"
:picker-options="formItem.pickerOptions"
/>
<el-time-picker
v-if="formItem.type === 'timePicker'"
v-model="formData[formItem.prop]"
value-format="HH:mm:ss"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
/>
<el-date-picker
v-if="formItem.type === 'dateTimePiker'"
v-model="formData[formItem.prop]"
:value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
:format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
type="datetime"
:disabled="formItem.disabled"
:placeholder="formItem.placeholder"
:style="{ formItem.formChildWidth}"
/>
<div v-if="formItem.type === 'dateZones'">
<div v-for="(dateItem, dateIndex) in formItem.children" :key="dateIndex" class="date-zone">
<el-form-item :prop="dateItem.prop">
<el-date-picker
v-if="dateItem.type === 'dataPicker'"
v-model="formData[dateItem.prop]"
value-format="yyyy-MM-dd"
type="date"
:placeholder="dateItem.placeholder"
:disabled="dateItem.disabled"
:style="{ dateItem.formChildWidth}"
/>
</el-form-item> <span v-if="dateIndex===0 && inline">至</span>
</div>
</div>
<!-- switch切换 -->
<el-switch
v-if="formItem.type === 'switch'"
v-model="formData[formItem.prop]"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
@change="val => switchChange(val, formItem.prop)"
/>
<!-- checkbox -->
<el-checkbox-group
v-if="formItem.type === 'checkbox'"
v-model="formData[formItem.prop]"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
@change="val => checkChange(val, formItem.prop)"
>
<el-checkbox
v-for="(selectItem, index) in formItem.checkData"
:key="index"
:label="selectItem"
name="type"
/>
</el-checkbox-group>
<!-- radio -->
<el-radio-group
v-if="formItem.type === 'radio'"
v-model="formData[formItem.prop]"
:disabled="formItem.disabled || false"
:fill="formItem.fill || '#409EFF'"
:text-color="formItem.textColor || '#ffffff'"
:style="{ formItem.formChildWidth}"
@change="val => checkChange(val, formItem.prop)"
>
<el-radio
v-for="(radioItem, index) in formItem.radioData"
:key="index"
:label="radioItem.label"
:disabled="radioItem.disabled || false"
:border="radioItem.border || false"
:size="radioItem.size"
:name="radioItem.primevalName || ''"
>{{ radioItem.name || radioItem.label }}</el-radio>
</el-radio-group>
<!-- 普通标签 -->
<div v-if="formItem.type === 'normalTag'" class="tag-fields">
<el-tag
v-for="(tagItem) in formItem.tagData"
:key="tagItem.id"
:closable="tagItem.closable || formItem.closable"
:disable-transitions="tagItem.transitions"
:size="tagItem.size"
:effect="tagItem.effect"
:type="tagItem.type"
@close="tagClose(tagItem.id,formItem.tagData)"
>{{ tagItem.name }}
</el-tag>
<el-tag v-if="!!(Object.keys(formItem.addTagBtnOption).length)" :size="formItem.addTagBtnOption.size" class="add-tag-btn" @click="addTag(formItem.tagData)">{{ formItem.addTagBtnOption.name }}</el-tag>
</div>
<!-- 图片标签 -->
<div v-if="formItem.type === 'imageTag'" class="image-tag-fields-box">
<div v-for="tagItem in formItem.tagData" :key="tagItem.id" class="img-item-wrap">
<i v-if="!formItem.noDeleteIcon" class="el-icon-error delete-icon" @click="imageTagClose(tagItem.id,formItem.tagData)" />
<div class="img-item" @click="uploadPreview(tagItem)"><img :src="tagItem.url" alt="暂无图片"></div>
<span>{{ tagItem.name }}</span>
</div>
<div class="img-item-wrap add-item" @click="addImgTag(formItem.tagData)">
<div class="img-item"> <span><i class="el-icon-plus add-btn" /></span></div>
</div>
</div>
<!-- 自定义表单域 -->
<div v-if="formItem.type === 'customSlot'" class="custom-slot-fields">
<slot :name="`${formItem.prop}Slot`" />
</div>
<!-- upload 上传 -->
<el-upload
v-if="formItem.type === 'upload'"
:ref="'uploadref'"
:action="formItem.action"
:headers="formItem.header"
:multiple="formItem.multiple"
:data="formItem.data"
:name="formItem.name || 'file'"
:with-credentials="formItem.withCredentials || false"
:show-file-list="formItem.showFileList || true"
:drag="formItem.drag || false"
:accept="formItem.accept"
:on-preview="formItem.onPreview || uploadPreview"
:on-remove="formItem.onRemove"
:on-success="formItem.onSuccess"
:on-error="formItem.onError"
:on-progress="formItem.onProgress"
:on-change="formItem.onChange"
:before-upload="formItem.beforeUplaod"
:before-remove="formItem.beforeRemove"
:list-type="formItem.listType || 'picture-card'"
:auto-upload="formItem.autoUpload || true"
:file-list="formData[formItem.prop] || []"
:http-request="formItem.httpRequest"
:disabled="formItem.disabled || false"
:limit="formItem.limit"
:on-exceed="formItem.onExceed"
>
<i class="el-icon-plus" />
<!-- upload 插槽 -->
<template #trigger>
<slot :name="`${formItem.prop}Trigger`" />
</template>
<template #tip>
<slot :name="`${formItem.prop}Tip`" />
</template>
</el-upload>
<!-- 按钮 -->
<div v-if="formItem.type === 'button'" class="button-flex">
<div v-for="(buttonItem, buttonIndex) in formItem.children" :key="buttonIndex" class="button-child-flex">
<button-comp
:button-item="buttonItem"
v-on="{
reset, confirm, cancel, search, exportFile
}"
/>
</div>
</div>
</el-form-item>
</el-form>
<!-- 图片预览弹窗 -->
<el-dialog :visible.sync="uploadDialogVisible" top="7vh" :append-to-body="true">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</template>
<script>
import buttonComp from './buttonComp'
export default {
components: {
buttonComp,
},
props: {
formLabelWidth: { // 表单label宽度
type: String,
default: '200px',
},
formData: { // 表单数据
type: Object,
default: null,
},
formDisabled: { // 是否禁用表单
type: Boolean,
default: false,
},
formRules: { // 表单验证规则
type: Object,
default: null,
},
// 设置json数据
formFields: { // 表单项配置信息
type: Array,
default: () => [],
},
inline: { // 表单横向竖向控制
type: Boolean,
default: false,
},
},
data() {
return {
dialogImageUrl: '',
uploadDialogVisible: false,
}
},
methods: {
reset() {
this.$refs['form'].resetFields()
this.$emit('reset')
},
exportFile(buttonItem) {
this.$emit('exportFile', buttonItem)
},
cancel() {
this.$emit('cancel')
},
confirm() {
this.$refs['form'].validate((valid) => {
if (valid) {
// 传事件到父亲组件
this.$emit('confirm')
} else {
return false
}
})
},
// 下拉
selectChange(val, prop) {
this.$emit(`select${prop}`, val)
},
// switch切换
switchChange(val, prop) {
this.$emit(`switch${prop}`, val)
},
// 选中
checkChange(val, prop) {
this.$emit(`check${prop}`, val)
},
// 标签关闭事件
tagClose(id, tagData) {
const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
tagData.splice(curIndex, 1)
this.$emit('tagClose', id)
},
// 图片标签关闭事件
imageTagClose(id, tagData) {
const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
tagData.splice(curIndex, 1)
this.$emit('imgTagClose', id)
},
addTag(tagData) {
this.$emit('addTagBtn', tagData)
},
addImgTag(tagData) {
this.$emit('addImgTagBtn', tagData)
},
// 查询
search() {
this.$emit('search')
},
// 上传图片预览
uploadPreview(file) {
this.dialogImageUrl = file.url
this.uploadDialogVisible = true
},
},
}
</script>
<style lang="scss" scoped>
.button-flex {
display: flex;
flex-wrap: wrap;
text-align: center;
}
.button-child-flex {
flex: 1;
margin-right: 10px;
}
.date-zone {
margin-bottom: 20px;
}
.tag-fields{
display: flex;
flex-wrap: wrap;
/deep/.el-tag{
margin: 0 10px 10px 0;
&.add-tag-btn{
color: #606266;
background: #ffffff;
border-color: #DCDFE6;
}
&.add-tag-btn:hover {
color: #409EFF;
border-color: #c6e2ff;
background-color: #ecf5ff;
cursor:pointer;
}
}
}
</style>
文件2 CommonTable/pagination.vue
<template>
<div>
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
:page-size="pagination.size"
:current-page="pagination.page"
:page-sizes="pagination.sizeZones"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</template>
<script>
export default {
props: {
pagination: {
type: Object,
default: null
}
},
data() {
return {
}
},
methods: {
handleCurrentChange() {
},
handleSizeChange() {
}
}
}
</script>
文件3 CommonTable/components/buttonComp.vue
<template>
<el-button
:size="buttonItem.size || 'medium'"
:type="buttonItem.btnType"
:plain="buttonItem.plain || false"
:round="buttonItem.round || false"
:circle="buttonItem.circle || false"
:loading="buttonItem.loading || false"
:disabled="buttonItem.disabled || false"
:icon="buttonItem.icon"
@click="btnClick"
>
{{ buttonItem.btnText }}
</el-button>
</template>
<script>
export default {
name: 'ButtonComp',
props: {
buttonItem: {
type: Object,
required: true,
},
},
methods: {
btnClick() {
this.$emit('clickFunc')
},
},
}
</script>
文件4 CommonTable/components/tbPagination.vue
<template>
<div :class="`${pagination.className || ''} defaultPagination`">
<div class="tip-box">
<span>本页个数:{{ pagination.currentTotal }}</span>
<span>总数:{{ pagination.total }}</span>
</div>
<!-- 分页器前插槽 -->
<slot name="beforePagination" />
<el-pagination
v-if="pagination.isShow || true"
:small="pagination.small || false"
:background="pagination.background || true"
:page-size="pagination.pageSize || 20"
:total="pagination.total"
:page-count="pagination.pageCount"
:pager-count="pagination.pagerCount || 7"
:current-page="pagination.currentPage || 1"
:layout="pagination.layout || 'sizes, prev, pager, next'"
:page-sizes="pagination.pageSizes || [5, 10, 20, 30, 40, 50, 100]"
:popper-class="pagination.popperClass"
:prev-text="pagination.prevText"
:next-text="pagination.nextText"
:disabled="pagination.disabled || false"
:hide-on-single-page="pagination.hideOnSinglePage || false"
@size-change="val => $emit('size-change', val)"
@current-change="val => $emit('page-change', val)"
/>
<!-- 分页器后插槽 -->
<slot name="afterPagination" />
</div>
</template>
<script>
export default {
name: 'TbPagination',
props: {
pagination: { // 分页配置数据
type: Object,
required: true,
},
},
methods: {
},
}
</script>
<style lang="scss" scoped>
.defaultPagination{
margin: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.tip-box{
span{
padding-right: 20px;
font-size: 14px;
color: #606266;
}
}
}
</style>
以上是封装table的代码,下面是分装form
文件1
CommomForm/index.vue
<template>
<div>
<el-form
ref="form"
class="form-flex"
:label-width="formLabelWidth"
:disabled="formDisabled"
:model="formData"
:rules="formRules"
:inline="inline"
>
<el-form-item
v-for="(formItem, itemIndex) in formFields"
v-show="!formItem.hidden"
:key="itemIndex"
:prop="formItem.prop"
:label-width="formItem.type === 'button' ? '0px': formItem.labelWidth"
:label="formItem.label"
:class="formItem.class"
>
<div
v-if="formItem.type === 'extraMention'"
:class="formItem.class"
>{{ formItem.prop }}</div>
<!-- 输入框或者textarea-->
<el-input
v-if="formItem.type === 'input'"
v-model="formData[formItem.prop]"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:clearable="formItem.clearable"
:style="{ formItem.formChildWidth}"
:maxlength="formItem.maxlength"
/>
<el-input
v-if="formItem.type === 'textarea'"
v-model="formData[formItem.prop]"
:placeholder="formItem.placeholder"
type="textarea"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
:clearable="formItem.clearable"
:maxlength="formItem.maxlength"
:autosize="formItem.autoSize"
:rows="formItem.rows || 2"
/>
<el-input
v-if="formItem.type === 'number'"
v-model.number="formData[formItem.prop]"
:placeholder="formItem.placeholder"
type="number"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
:clearable="formItem.clearable"
:min="formItem.min"
:max="formItem.max"
/>
<!-- 下拉框 -->
<el-select
v-if="formItem.type === 'select'"
v-model="formData[formItem.prop]"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
:multiple="formItem.multiple"
:filterable="formItem.filterable"
:clearable="formItem.clearable"
@change="val => selectChange(val, formItem.prop)"
>
<el-option
v-for="(selectItem, index) in formItem.selectData"
:key="index"
:label="selectItem.label"
:value="selectItem.value"
/>
</el-select>
<!-- 时间控件 -->
<el-date-picker
v-if="formItem.type === 'datePicker'"
v-model="formData[formItem.prop]"
:type="formItem.dateType"
:value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
:format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
range-separator="至"
:start-placeholder="formItem.placeholderStart"
:end-placeholder="formItem.placeholderEnd"
:class="formItem.class"
:picker-options="formItem.pickerOptions"
/>
<el-time-picker
v-if="formItem.type === 'timePicker'"
v-model="formData[formItem.prop]"
value-format="HH:mm:ss"
:placeholder="formItem.placeholder"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
/>
<el-date-picker
v-if="formItem.type === 'dateTimePiker'"
v-model="formData[formItem.prop]"
:value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
:format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
type="datetime"
:disabled="formItem.disabled"
:placeholder="formItem.placeholder"
:style="{ formItem.formChildWidth}"
/>
<div v-if="formItem.type === 'dateZones'">
<div v-for="(dateItem, dateIndex) in formItem.children" :key="dateIndex" class="date-zone">
<el-form-item :prop="dateItem.prop">
<el-date-picker
v-if="dateItem.type === 'dataPicker'"
v-model="formData[dateItem.prop]"
value-format="yyyy-MM-dd"
type="date"
:placeholder="dateItem.placeholder"
:disabled="dateItem.disabled"
:style="{ dateItem.formChildWidth}"
/>
</el-form-item> <span v-if="dateIndex===0 && inline">至</span>
</div>
</div>
<!-- switch切换 -->
<el-switch
v-if="formItem.type === 'switch'"
v-model="formData[formItem.prop]"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
@change="val => switchChange(val, formItem.prop)"
/>
<!-- checkbox -->
<el-checkbox-group
v-if="formItem.type === 'checkbox'"
v-model="formData[formItem.prop]"
:disabled="formItem.disabled"
:style="{ formItem.formChildWidth}"
@change="val => checkChange(val, formItem.prop)"
>
<el-checkbox
v-for="(selectItem, index) in formItem.checkData"
:key="index"
:label="selectItem"
name="type"
/>
</el-checkbox-group>
<!-- radio -->
<el-radio-group
v-if="formItem.type === 'radio'"
v-model="formData[formItem.prop]"
:disabled="formItem.disabled || false"
:fill="formItem.fill || '#409EFF'"
:text-color="formItem.textColor || '#ffffff'"
:style="{ formItem.formChildWidth}"
@change="val => checkChange(val, formItem.prop)"
>
<el-radio
v-for="(radioItem, index) in formItem.radioData"
:key="index"
:label="radioItem.label"
:disabled="radioItem.disabled || false"
:border="radioItem.border || false"
:size="radioItem.size"
:name="radioItem.primevalName || ''"
>{{ radioItem.name || radioItem.label }}</el-radio>
</el-radio-group>
<!-- 普通标签 -->
<div v-if="formItem.type === 'normalTag'" class="tag-fields">
<el-tag
v-for="(tagItem) in formItem.tagData"
:key="tagItem.id"
:closable="tagItem.closable || formItem.closable"
:disable-transitions="tagItem.transitions"
:size="tagItem.size"
:effect="tagItem.effect"
:type="tagItem.type"
@close="tagClose(tagItem.id,formItem.tagData)"
>{{ tagItem.name }}
</el-tag>
<el-tag v-if="!!(Object.keys(formItem.addTagBtnOption).length)" :size="formItem.addTagBtnOption.size" class="add-tag-btn" @click="addTag(formItem.tagData)">{{ formItem.addTagBtnOption.name }}</el-tag>
</div>
<!-- 图片标签 -->
<div v-if="formItem.type === 'imageTag'" class="image-tag-fields-box">
<div v-for="tagItem in formItem.tagData" :key="tagItem.id" class="img-item-wrap">
<i v-if="!formItem.noDeleteIcon" class="el-icon-error delete-icon" @click="imageTagClose(tagItem.id,formItem.tagData)" />
<div class="img-item" @click="uploadPreview(tagItem)"><img :src="tagItem.url" alt="暂无图片"></div>
<span>{{ tagItem.name }}</span>
</div>
<div class="img-item-wrap add-item" @click="addImgTag(formItem.tagData)">
<div class="img-item"> <span><i class="el-icon-plus add-btn" /></span></div>
</div>
</div>
<!-- 自定义表单域 -->
<div v-if="formItem.type === 'customSlot'" class="custom-slot-fields">
<slot :name="`${formItem.prop}Slot`" />
</div>
<!-- upload 上传 -->
<el-upload
v-if="formItem.type === 'upload'"
:ref="'uploadref'"
:action="formItem.action"
:headers="formItem.header"
:multiple="formItem.multiple"
:data="formItem.data"
:name="formItem.name || 'file'"
:with-credentials="formItem.withCredentials || false"
:show-file-list="formItem.showFileList || true"
:drag="formItem.drag || false"
:accept="formItem.accept"
:on-preview="formItem.onPreview || uploadPreview"
:on-remove="formItem.onRemove"
:on-success="formItem.onSuccess"
:on-error="formItem.onError"
:on-progress="formItem.onProgress"
:on-change="formItem.onChange"
:before-upload="formItem.beforeUplaod"
:before-remove="formItem.beforeRemove"
:list-type="formItem.listType || 'picture-card'"
:auto-upload="formItem.autoUpload || true"
:file-list="formData[formItem.prop] || []"
:http-request="formItem.httpRequest"
:disabled="formItem.disabled || false"
:limit="formItem.limit"
:on-exceed="formItem.onExceed"
>
<i class="el-icon-plus" />
<!-- upload 插槽 -->
<template #trigger>
<slot :name="`${formItem.prop}Trigger`" />
</template>
<template #tip>
<slot :name="`${formItem.prop}Tip`" />
</template>
</el-upload>
<!-- 按钮 -->
<div v-if="formItem.type === 'button'" class="button-flex">
<div v-for="(buttonItem, buttonIndex) in formItem.children" :key="buttonIndex" class="button-child-flex">
<button-comp
:button-item="buttonItem"
v-on="{
reset, confirm, cancel, search, exportFile
}"
/>
</div>
</div>
</el-form-item>
</el-form>
<!-- 图片预览弹窗 -->
<el-dialog :visible.sync="uploadDialogVisible" top="7vh" :append-to-body="true">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</template>
<script>
import buttonComp from './buttonComp'
export default {
components: {
buttonComp,
},
props: {
formLabelWidth: { // 表单label宽度
type: String,
default: '200px',
},
formData: { // 表单数据
type: Object,
default: null,
},
formDisabled: { // 是否禁用表单
type: Boolean,
default: false,
},
formRules: { // 表单验证规则
type: Object,
default: null,
},
// 设置json数据
formFields: { // 表单项配置信息
type: Array,
default: () => [],
},
inline: { // 表单横向竖向控制
type: Boolean,
default: false,
},
},
data() {
return {
dialogImageUrl: '',
uploadDialogVisible: false,
}
},
methods: {
reset() {
this.$refs['form'].resetFields()
this.$emit('reset')
},
exportFile(buttonItem) {
this.$emit('exportFile', buttonItem)
},
cancel() {
this.$emit('cancel')
},
confirm() {
this.$refs['form'].validate((valid) => {
if (valid) {
// 传事件到父亲组件
this.$emit('confirm')
} else {
return false
}
})
},
// 下拉
selectChange(val, prop) {
this.$emit(`select${prop}`, val)
},
// switch切换
switchChange(val, prop) {
this.$emit(`switch${prop}`, val)
},
// 选中
checkChange(val, prop) {
this.$emit(`check${prop}`, val)
},
// 标签关闭事件
tagClose(id, tagData) {
const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
tagData.splice(curIndex, 1)
this.$emit('tagClose', id)
},
// 图片标签关闭事件
imageTagClose(id, tagData) {
const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
tagData.splice(curIndex, 1)
this.$emit('imgTagClose', id)
},
addTag(tagData) {
this.$emit('addTagBtn', tagData)
},
addImgTag(tagData) {
this.$emit('addImgTagBtn', tagData)
},
// 查询
search() {
this.$emit('search')
},
// 上传图片预览
uploadPreview(file) {
this.dialogImageUrl = file.url
this.uploadDialogVisible = true
},
},
}
</script>
<style lang="scss" scoped>
.button-flex {
display: flex;
flex-wrap: wrap;
text-align: center;
}
.button-child-flex {
flex: 1;
margin-right: 10px;
}
.date-zone {
margin-bottom: 20px;
}
.tag-fields{
display: flex;
flex-wrap: wrap;
/deep/.el-tag{
margin: 0 10px 10px 0;
&.add-tag-btn{
color: #606266;
background: #ffffff;
border-color: #DCDFE6;
}
&.add-tag-btn:hover {
color: #409EFF;
border-color: #c6e2ff;
background-color: #ecf5ff;
cursor:pointer;
}
}
}
</style>
文件2 CommomForm/buttonComp.vue
<template>
<el-button
v-if="haveBtn(buttonItem.type)"
v-show="!buttonItem.hidden"
:size="buttonItem.size || 'medium'"
:type="buttonItem.btnType || itemOption.type"
:plain="buttonItem.plain || false"
:round="buttonItem.round || false"
:circle="buttonItem.circle || false"
:loading="buttonItem.loading || false"
:disabled="buttonItem.disabled || false"
:icon="buttonItem.icon || itemOption.icon"
@click="btnClick"
>
{{ buttonItem.btnText || itemOption.text }}
</el-button>
</template>
<script>
export default {
name: 'ButtonComp',
props: {
buttonItem: {
type: Object,
required: true,
},
},
data() {
return {
defaultOption: { // 按钮默认配置
reset: {
icon: 'el-icon-refresh-left', type: 'warning', text: '重 置', emit: 'reset',
},
confirm: {
icon: 'el-icon-circle-check', type: 'primary', text: '确 定', emit: 'confirm',
},
cancel: {
icon: 'el-icon-circle-close', type: '', text: '取 消', emit: 'cancel',
},
search: {
icon: 'el-icon-search', type: 'primary', text: '查 询', emit: 'search',
},
export: {
icon: 'el-icon-folder-opened', type: 'info', text: '导 出', emit: 'exportFile',
},
},
}
},
computed: {
itemOption() {
return this.defaultOption[this.buttonItem.type]
},
},
methods: {
haveBtn(type) {
return this.defaultOption.hasOwnProperty(type)
},
btnClick() {
this.$emit(this.itemOption.emit, this.buttonItem)
},
},
}
</script>
api/commonTable.js
import request from '@/utils/request'
import qs from 'qs'
export function apiRequest(url, params, method) {
return request({
url: url,
method: method || 'get',
params,
})
}
export function apiQsRequest(url, params, method) {
console.log(params, 7878);
return request({
url: url,
method: method || 'get',
params: {
query: params,
},
})
}
export function exportFile(url, params) {
return request({
responseType: 'blob',
headers: {
'Content-Type': 'application/json',
},
timeout: 1000 * 60,
url: url,
method: 'get',
params: {
query: qs.stringify(params),
},
})
}
记得npm install qs --save哦,因为传过去可能是数组,所以直接qs过去拉~
任何不会用,在下面留言吧~ 467015242,我的微信,备注: table组件使用,这样我就来看看你留言了什么,一般不会通过