筛选表单
<template>
<el-form ref="filterForm" class="filter-form" :rules="rules" :model="formModel" :size="size" :inline="isInline" :label-width="labelWidth" :label-position="labelPosition">
<div class="data_container">
<el-form-item v-for="item in formItemList" :key="item.prop" :label="item.label" :prop="item.prop">
<slot :name="item.prop || item.selectProp">
<template v-if="item.type === 'input'">
<el-input v-model.trim="formModel[item.prop]" :placeholder="item.placeholder || '请输入内容'" clearable :style="[item.width ? { width: item.width } : '']">
<span v-if="item.suffix" slot="suffix">{{ item.suffix }}</span>
</el-input>
</template>
<template v-if="item.type === 'number-input'">
<el-input v-model.trim="formModel[item.prop]" :placeholder="item.placeholder || '请输入内容'" :clearable="item.clearable === false ? item.clearable : true" type="number" :style="[item.width ? { width: item.width } : '']" />
</template>
<template v-if="item.type === 'select'">
<el-select v-model="formModel[item.prop]" :placeholder="item.placeholder || '请选择'" :clearable="item.clearable === false ? item.clearable : true" :multiple="item.multiple || false" collapse-tags :style="[item.width ? { width: item.width } : '']">
<el-option v-for="option in item.options || []" :key="item.prop + '_option_' + (item.valueKey ? option[item.valueKey] : option.value)" :label="item.labelKey ? option[item.labelKey] : option.name" :value="item.valueKey ? option[item.valueKey] : option.value" />
</el-select>
</template>
<template v-if="item.type === 'select-input'">
<el-select v-model="formModel[item.selectProp]" :placeholder="item.selectPlaceholder || '请选择'" :clearable="item.clearable === false ? item.clearable : true" :style="[item.selectWidth ? { width: item.selectWidth } : '']">
<el-option v-for="option in item.options || []" :key="item.selectProp + '_option_' + option.value" :label="option.name" :value="option.value" />
</el-select>
<el-input v-model.trim="formModel[item.inputProp]" :placeholder="item.inputPlaceholder || '请输入内容'" clearable :style="[
item.inputWidth ? { width: item.inputWidth } : '',
{ marginLeft: '10px' }
]" />
</template>
<template v-if="item.type === 'daterange'">
<el-date-picker v-model="formModel[item.prop]" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd" clearable :style="[item.width ? { width: item.width } : '']" />
</template>
<template v-if="item.type === 'yearrange'">
<date-picker v-model="formModel[item.prop]" type="yearrange" start-placeholder="开始时间" end-placeholder="结束时间" value-format="yyyy" clearable :style="[item.width ? { width: item.width } : '']" />
</template>
<template v-if="item.type === 'date'">
<el-date-picker v-model="formModel[item.prop]" type="date" placeholder="选择日期" value-format="yyyy-MM-dd" clearable :style="[item.width ? { width: item.width } : '']" />
</template>
</slot>
</el-form-item>
</div>
<div class="handle_container">
<el-form-item>
<el-button type="primary" @click="submitForm">搜索</el-button>
<el-button @click="resetForm">重置</el-button>
<slot name="handle"></slot>
</el-form-item>
</div>
</el-form>
</template>
<script>
export default {
name: 'FilterForm',
components: {
},
props: {
rules: {
type: Object,
default: () => ({})
},
labelPosition: {
type: String,
default: 'right'
},
isInline: {
type: Boolean,
default: true
},
size: {
type: String,
default: 'mini'
},
formItemList: {
type: Array,
default: () => []
},
labelWidth: {
type: String,
},
},
data() {
return {
formModel: {},
};
},
computed: {
},
watch: {
},
created() {
},
methods: {
submitForm() {
console.log('submitForm',this.formModel);
this.$emit('search',this.formModel)
},
resetForm() {
this.$refs['filterForm'].resetFields();
this.$emit('search',this.formModel)
},
}
};
</script>
<style lang="scss" scoped>
.filter-form {
padding: 0 24px;
display: flex;
align-items: flex-end;
.data_container {
flex: 1;
}
}
</style>
table
<template>
<el-table ref="tableRef" @header-dragend="onHeaderDragend" :data="tableData" style="width: 100%" stripe :border="border" @selection-change="handleSelectionChange" @row-dblclick="rowDblclick" :height="tableHeight ? tableHeight : '100%'" v-bind="$attrs" v-loading="loading">
<template v-for="(item, index) in tableColumn">
<el-table-column v-if="['index', 'selection'].includes(item.type)" :key="index" :type="item.type || ''" :index="item.index || columnIndex" :label="item.label" :align="item.align || 'center'" v-bind="item.attrs || {}" width="100" />
<el-table-column v-else :key="item.prop" :prop="item.prop" :label="item.label" :align="item.align || 'center'" :show-overflow-tooltip="item.showOverflowTooltip || showOverflowTooltip" v-bind="item.attrs || {}">
<template slot-scope="{ row, $index, column }">
<slot :name="item.prop" :row="row" :$index="$index" :column="column">
{{ item.formatter ? item.formatter(row[column.property], row) : row[column.property] }}
</slot>
</template>
</el-table-column>
</template>
</el-table>
</template>
<script>
export default {
name: 'Table',
props: {
tableData: {
type: Array,
default: () => []
},
tableColumn: {
type: Array,
default: () => []
},
border: {
type: Boolean,
default: false
},
showOverflowTooltip: {
type: Boolean,
default: false
},
tableHeight: {
type: [String, Number],
default: ''
},
loading: {
type: Boolean,
default: false
}
},
data() {
return {
multipleSelection: []
};
},
methods: {
onHeaderDragend() {
this.doLayout()
},
async doLayout() {
await this.$nextTick();
this.$refs.tableRef.doLayout()
},
columnIndex(index) {
return index + 1;
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
rowDblclick(row, column, event) {
this.$emit('row-dblclick', row, column, event);
}
},
watch: {
tableData: {
handler(nVal) {
this.doLayout()
},
deep: true
}
}
};
</script>
<style lang="scss" scoped>
.el-table {
margin-top: 10px;
}
</style>
pagination
<template>
<div class="pagination_container">
<el-pagination :current-page.sync="page" :page-size="size" :total="total" background :page-sizes="pageSizes" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</template>
<script>
export default {
name: 'Pagination',
props: {
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
total: {
type: Number,
default: 0
},
pageSizes: {
type: Array,
default: () => [10, 20, 50, 100]
}
},
data() {
return {
page: this.currentPage,
size: this.pageSize
};
},
watch: {
currentPage(val) {
if (val !== this.page) {
this.page = val;
}
},
pageSize(val) {
if (val !== this.size) {
this.size = val;
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('update:currentPage', 1);
this.$emit('update:pageSize', val);
this.$emit('change', { currentPage: this.currentPage, pageSize: val });
},
handleCurrentChange(val) {
this.$emit('update:currentPage', val);
this.$emit('change', { currentPage: val, pageSize: this.pageSize });
}
}
};
</script>
<style lang="scss" scoped>
.pagination_container {
margin-top: 10px;
display: flex;
justify-content: flex-end;
padding: 0 24px;
}
</style>
筛选表单 + table + 分页组合
<template>
<div class="filter_table_pagination">
<FilterForm ref="FilterForm" :formItemList="filterFormProp.formItemList" @search="filterFormProp.getListByForm">
<template #FilterFormExOperation>
<slot name="FilterFormExOperation"></slot>
</template>
</FilterForm>
<div class="table_container">
<Table ref="Table" border :tableData="tableProp.tableData" :tableColumn="tableProp.tableColumn" showOverflowTooltip>
<template #TableOperation="{ row, column, $index }">
<slot name="TableOperation" :row="row" :$index="$index" :column="column"></slot>
</template>
</Table>
</div>
<Pagination :total="paginationProp.total" :currentPage="paginationProp.currentPage" :pageSize="paginationProp.pageSize" @change="paginationProp.handlePageChange" />
</div>
</template>
<script>
import Table from './Table.vue'
import FilterForm from './FilterForm.vue'
import Pagination from './Pagination.vue'
export default {
name: 'FilterTablePagination',
props: {
filterFormProp: {
type: Object,
default: () => ({ formItemList: [], getListByForm: () => { } })
},
tableProp: {
type: Object,
default: () => ({ tableData: [], tableColumn: [], })
},
paginationProp: {
type: Object,
default: () => ({ total: 0, currentPage: 1, pageSize: 10, handlePageChange: () => { } })
},
},
components: {
FilterForm,
Table,
Pagination,
},
data() {
return {
};
},
created() {
},
watch: {
},
computed: {
},
mounted() {
},
methods: {
},
};
</script>
<style lang="scss" scoped>
.filter_table_pagination {
height: 100vh;
display: flex;
flex-direction: column;
.table_container {
flex: 0 1 auto;
overflow: auto;
}
}
</style>
<template>
<div class="filter_table_pagination_page">
<FilterTablePagination :filterFormProp="filterFormProp" :tableProp="tableProp" :paginationProp="paginationProp" ref="FilterTablePaginationRef">
<template #FilterFormExOperation>
<el-button type="primary" @click="smartSearch">高级搜索</el-button>
</template>
<template #TableOperation="{ row, $index, column }">
<i class="el-icon-edit" @click="handleEdit(row, $index, column)"></i>
</template>
</FilterTablePagination>
</div>
</template>
<script>
import FilterTablePagination from '../components/FilterTablePagination.vue'
export default {
name: 'FilterTablePaginationPage',
components: {
FilterTablePagination,
},
props: {
},
data() {
return {
filterFormProp: {
formItemList: [
{
prop: "articleTitle",
type: "input",
label: "文章标题:",
width: "150px",
},
{
prop: "accountType",
type: "select",
label: "文章分类:",
width: "100px",
options: [{ name: '有趣', value: 'youqu' }, { name: "经典", value: 'jingdian' }, { name: "科学", value: 'kexue' }],
},
{
prop: "date",
type: "daterange",
label: "评论时间:",
width: "220px",
},
{
prop: "inp1",
type: "input",
label: "inp1:",
width: "220px",
},
{
prop: "inp2",
type: "input",
label: "inp2:",
width: "220px",
},
{
prop: "inp3",
type: "input",
label: "inp3:",
width: "220px",
},
],
getListByForm: this.getListByForm,
smartSearch: this.smartSearch,
},
tableProp: {
tableData: [
{ id: "111", articleTitle: "标题1", commentStatus: 2, },
{ id: "222", articleTitle: "标题1", commentStatus: 1, },
{ id: "333", articleTitle: "标标题1标题标题1题1", commentStatus: 2, },
{ id: "444", articleTitle: "标题1", commentStatus: 1, },
],
tableColumn: [
{ type: 'index', label: "序号", },
{ prop: "TableOperation", label: "操作", width: 150, attrs: { "class-name": "operation", fixed: 'right', }, },
{ prop: "articleTitle", label: "文章标题", attrs: { 'min-width': 800 } },
{ prop: "author", label: "作者", attrs: { 'min-width': 200 } },
{ prop: "author2", label: "作者2", },
{ prop: "author3", label: "作者2", },
{ prop: "author4", label: "作者2", },
{
prop: "commentStatus", label: "评论状态",
formatter: (val) => {
if (val === 1) {
return '已评论'
} else if (val === 2) {
return '未评论'
}
},
},
],
},
paginationProp: {
total: 0,
currentPage: 1,
pageSize: 10,
handlePageChange: this.handlePageChange
},
}
},
computed: {
},
watch: {
},
created() {
},
mounted() {
},
methods: {
handlePageChange({ currentPage, pageSize } = {}) {
console.log('handlePageChange', currentPage, pageSize);
this.pageSize = pageSize;
this.currentPage = currentPage;
},
handleEdit(row, $index, column) {
console.log('row', row, $index, column);
},
getListByForm(formData = {}) {
console.log('getListByForm---formData', formData);
},
smartSearch() {
console.log('smartSearch---formData', this.$refs.FilterTablePaginationRef.$refs.FilterForm.formModel);
},
},
};
</script>
<style lang="scss">
</style>
编辑性表单
<template>
<el-form ref="addEditFormRef" :model="form" :rules="rules" class="addEdit_Form" size="small" :label-width="labelWidth"
:disabled="disabled">
<el-row type="flex">
<template v-for="item in formItemList">
<el-col v-if="isShowItem(item.prop)" :key="item.prop" :span="item.span || colSpan">
<el-form-item :prop="item.prop" :label="item.label" :label-width="item.labelWidth"
:class="{ 'required': item.isRequired }">
<slot :name="item.prop" :value="form[item.prop]" :form="form" :change="handleChange">
<template v-if="item.type === 'input'">
<el-input v-model.trim="form[item.prop]" :placeholder="disabled ? '' : '请输入内容'" clearable
:maxlength="item.maxlength || null" :disabled="item.disabled || false" class="width100"
@clear="inputClear(item.prop)">
<span v-if="item.suffix" slot="suffix">{{ item.suffix }}</span>
</el-input>
</template>
<template v-else-if="item.type === 'password'">
<el-input v-model="form[item.prop]" placeholder="请输入" clearable class="width100" type="password"
:disabled="item.disabled || false" autocomplete="new-password"
@paste.native.capture.prevent="inputPress" />
</template>
<template v-else-if="item.type === 'textarea'">
<el-input v-model="form[item.prop]" :placeholder="disabled ? '' : '请输入内容'" clearable
:autosize="{ minRows: 3 }" type="textarea" class="width100" :disabled="item.disabled || false"
:maxlength="item.maxlength || null" show-word-limit />
</template>
<template v-else-if="item.type === 'text'">
<span class="form-text">{{ item.formatter ? item.formatter(form[item.prop]) : form[item.prop] }}</span>
</template>
<template v-else-if="item.type === 'select'">
<el-select v-model="form[item.prop]" :placeholder="disabled ? '' : '请选择'" clearable
:filterable="item.filterable" class="width100" :disabled="item.disabled || false"
@change="val => formChange(item.prop, val)">
<el-option v-for="option in item.options || []" :key="item.prop + '_option_' + option.value"
:label="option.name" :value="option.value" />
</el-select>
</template>
<template v-else-if="item.type === 'radio'">
<el-radio-group v-model="form[item.prop]" :disabled="item.disabled || false"
@change="val => formChange(item.prop, val)">
<el-radio v-for="option in item.options || []" :key="item.prop + '_option_' + option.value"
:label="option.value">
{{ option.name }}
</el-radio>
</el-radio-group>
</template>
<template v-else-if="item.type === 'checkbox'">
<el-checkbox-group v-model="form[item.prop]" :disabled="item.disabled || false"
@change="val => formChange(item.prop, val)">
<el-checkbox v-for="option in item.options || []" :key="item.prop + '_option_' + option.value"
:label="option.value">
{{ option.name }}
</el-checkbox>
</el-checkbox-group>
</template>
<template v-else-if="item.type === 'daterange'">
<el-date-picker v-model="form[item.prop]" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
value-format="yyyy-MM-dd" clearable :disabled="item.disabled || false" class="width100" />
</template>
<template v-else-if="item.type === 'date'">
<el-date-picker v-model="form[item.prop]" type="date" placeholder="选择日期" value-format="yyyy-MM-dd"
clearable :disabled="item.disabled || false" class="width100" />
</template>
<template v-else-if="item.type === 'yearrange'">
<date-picker v-model="form[item.prop]" type="yearrange" start-placeholder="开始时间" end-placeholder="结束时间"
value-format="yyyy" clearable :disabled="item.disabled || false" class="width100" />
</template>
<template v-else-if="item.type === 'year'"><el-date-picker v-model="form[item.prop]" type="year"
value-format="yyyy" placeholder="请选择" class="width100"></el-date-picker></template>
</slot>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
<script>
export default {
name: 'AddEditForm',
props: {
rules: {
type: Object,
default: () => { }
},
col: {
type: Number,
default: 1
},
labelWidth: {
type: String,
default: '80px'
},
formItemList: {
type: Array,
default: () => []
},
formData: {
type: Object,
default: () => { }
},
hiddenFormItems: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
form: {}
};
},
computed: {
colSpan() {
return Math.floor(24 / this.col);
}
},
watch: {
formData: {
deep: true,
immediate: true,
handler(val) {
this.form = JSON.parse(JSON.stringify(val));
}
}
},
created() {
},
methods: {
inputPress() {
return false
},
init() {
this.formItemList.forEach((item) => {
const oldVal = this.form[item.prop];
this.$set(this.form, item.prop, oldVal);
});
},
handleChange(key, val = '') {
},
formChange(key, val) {
this.$emit('formChange', key, val, this.handleChange);
},
submitForm() {
this.$refs.addEditFormRef.validate((valid) => {
if (valid) {
const form = JSON.parse(JSON.stringify(this.form));
this.$emit('submitForm', form);
} else {
return false;
}
});
},
getFormData() {
return JSON.parse(JSON.stringify(this.form));;
},
isShowItem(prop) {
return this.hiddenFormItems.indexOf(prop) === -1;
},
clearValidate() {
const addEditFormRef = this.$refs.addEditFormRef;
addEditFormRef && addEditFormRef.clearValidate();
},
resetFields() {
const addEditFormRef = this.$refs.addEditFormRef;
addEditFormRef && addEditFormRef.resetFields();
},
inputClear(prop) {
if (this.rules[prop]) {
this.$refs.addEditFormRef.validateField(prop)
}
},
validateField(val) {
this.$refs.addEditFormRef.validateField(val);
}
}
}
</script>
<style lang="scss" scoped>
.addEdit_Form {
::v-deep .el-row {
flex-wrap: wrap;
}
::v-deep .required {
.el-form-item__label {
&::before {
content: "*";
color: var(--global_dan_color, #ff4d4f);
margin-right: 4px;
}
}
}
.width100 {
width: 100%;
}
::v-deep .form-text {
padding-left: 10px;
// color: $global_font_color;
}
}
</style>
<template>
<div class=''>
<AddEditForm ref="AddEditFormRef" :formData="formData" :col="1" :formItemList="formItemList" :rules="rules"
:disabled="isFormDisabled" labelWidth="180px">
<template #imgs="{ form ,}">
<Upload v-model="form.imgs"></Upload>
</template>
</AddEditForm>
<el-button type="primary" @click="onSave">保存</el-button>
</div>
</template>
<script>
import AddEditForm from '../components/AddEditForm.vue'
import Upload from '../components/Upload.vue'
export default {
name: '',
components: {
AddEditForm,
Upload,
},
props: {
},
data() {
return {
isFormDisabled: false,
formData: {
artworkName: '',
type: '',
creationYear: '',
price: '',
imgs: [],
id: '',
ifLatest: 0,
ifSelected: 0,
showAreaList: [],
},
formItemList: [
{ prop: 'artworkName', label: '艺术品名称:', type: 'input', },
{ prop: 'type', type: 'select', label: '类型:', options: [{ name: "类型1", value: '1' }, { name: "类型2", value: '2' }, { name: "类型3", value: '3' }] },
{ prop: 'creationYear', type: 'year', label: '创作年代:' },
{ prop: 'price', label: '价格:', type: 'input', suffix: "元" },
{ prop: 'imgs', label: '艺术品图片:', isRequired: true },
{ prop: 'showAreaList', label: '是否添加到首页板块:', type: 'checkbox', options: [{ name: "最新上线", value: "ifLatest" }, { name: "作品精选", value: "ifSelected" }] },
],
rules: {
artworkName: [
{ required: true, message: '请输入艺术品名称' },
],
type: [
{ required: true, message: '请选择类型' },
],
creationYear: [
{ required: true, message: '请选择创作年代' },
],
price: [
{ required: true, message: '请输入价格' },
],
imgs: [
{ required: true, message: '请上传艺术品图片' },
],
showAreaList: [
{ required: true, message: '请选择是否添加首页板块' },
],
},
}
},
computed: {
},
watch: {
},
created() {
},
mounted() {
},
methods: {
validateFormsByFormRefs(formRefs = []) {
return formRefs.map(ref => {
return new Promise((resolve, reject) => {
ref.validate((valid) => {
if (valid) resolve();
}
);
});
})
},
async onSave() {
const AddEditFormRef = this.$refs.AddEditFormRef
await Promise.all(this.validateFormsByFormRefs([AddEditFormRef.$refs.addEditFormRef]))
const formData = AddEditFormRef.getFormData()
console.log('AddEditFormRef.getFormData', formData);
},
},
};
</script>
<style scoped>
</style>
上传组件
<template>
<el-upload action=""
:on-remove="onRemove"
:class="{ 'hidden': isDisabled || value.length >= maxLength }"
:on-success="onSuccess"
multiple
:file-list="value"
class="uploader"
:style="{ width: uploadBoxWidth }"
:limit="maxLength"
:on-exceed="onExceed"
:before-upload="beforeUpload"
list-type="picture-card"
:http-request="handleUpload">
<i class="el-icon-plus"></i>
</el-upload>
</template>
<script>
export default {
name: 'UploadComponent',
components: {
},
model: {
prop: 'value',
event: 'change'
},
props: {
value: {
type: Array,
},
maxLength: {
type: Number,
default: 10
},
uploadBoxWidth: {
type: String,
default: '900px'
},
isDisabled: {
type: Boolean,
default: false
},
},
data() {
return {
};
},
created() {
},
watch: {
},
computed: {
},
mounted() {
},
methods: {
onRemove(file, fileList) {
this.$emit('change', fileList)
},
onExceed(files, fileList) {
this.$message.warning(`最大允许上传${this.maxLength}张图片`)
},
beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return isJPG && isLt2M;
},
async handleUpload(e) {
},
onSuccess(res, file, fileList) {
const files = fileList.map(item => {
const file = { ...(item.response ?? item), uid: item.uid }
file.imgUrl = file.url
return file
})
this.$emit('change', files)
},
},
};
</script>
<style lang="scss">
.uploader {
&.hidden {
.el-upload {
display: none;
}
}
}
</style>
带有form的table
<template>
<div class="table_with_form">
<slot name="addRow">
<div style="text-align:right">
<el-button type="primary" style="text-align:right" @click="handleAddRow(tableName)">添加</el-button>
</div>
</slot>
<el-form :model="form" ref="tableWithForm">
<el-table :data="form[tableName]" border style="width: 100%; margin: 10px 0 20px;">
<el-table-column v-for="(item,index) in columns" :key="index" :label="item.label" :width="item.width">
<template slot-scope="scope">
<el-form-item :prop="tableName +'.' + scope.$index +'.'+item.prop" :rules="item.rules">
<el-input v-model="scope.row[item.prop]" v-if="item.type=='input'" :placeholder="item.label"></el-input>
<el-select v-model="scope.row[item.prop]" v-if="item.type=='select'">
<el-option :label="option.label" :value="option.value" v-for="(option,i) in item.options" :key="i"></el-option>
</el-select>
<el-date-picker type="date" value-format="yyyy-MM-dd" v-if="item.type=='date'" v-model="scope.row[item.prop]" style="width: 100%;"></el-date-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="100">
<template slot-scope="scope">
<slot name="handlerColumn" :scope="scope" :tableName="tableName">
<el-button @click="handleDelRow(scope.$index)" type="text" size="small">删除</el-button>
</slot>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
</template>
<script>
export default {
name: "TableWithForm",
props: {
form: {
type: Object
},
tableName: {
type: String
},
columns: {
type: Array
}
},
components: {},
created () { },
mounted () { },
computed: {},
watch: {},
data: function () {
return {};
},
methods: {
handleAddRow (tableName) {
this.form[tableName].push({});
},
handleDelRow (index) {
this.form[this.tableName].splice(index, 1);
}
}
};
</script>
<style scoped>
</style>
- 使用示例(而且演示了同时校验多表单 + slot传参)
<template>
<div id="app">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="活动名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="活动区域" prop="region">
<el-select v-model="ruleForm.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
<TableWithForm ref="tableForm" :form="form" tableName="tableData" :columns='columns' />
<TableWithForm ref="otherTableForm" :form="form" tableName="otherTableData" :columns='otherColumns'>
<template v-slot:handlerColumn="{scope, tableName}">
<el-button @click="handleDelRow(scope.$index, tableName)" type="text" size="small">来自父组件 删除</el-button>
</template>
</TableWithForm>
<div style="text-align:center">
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</div>
</div>
</template>
<script>
import TableWithForm from '@/components/TableWithForm'
export default {
components: {
TableWithForm,
},
data () {
return {
ruleForm: {
name: '',
region: '',
},
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
region: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
},
form: {
tableData: [{ name: "bwf", province: "北京", date: "2022-09-09", city: "上海" }],
otherTableData: [],
},
columns: [
{
prop: "name",
label: "姓名",
width: "",
type: "input",
rules: [{
required: true,
message: '请输入'
},
{
min: 3,
max: 5,
message: '长度在 3 到 5 个字符'
}
],
},
{
prop: "province",
label: "省份",
width: "",
type: "input"
},
{
prop: "date",
label: "日期",
width: "",
type: "date",
rules: [
{
required: true,
message: '请选择日期',
trigger: 'change'
}
]
},
{
prop: "city",
label: "市区",
width: "",
type: "select",
options: [{
label: "北京",
value: "bj"
}, {
label: "上海",
value: "sh"
}],
rules: [{
required: true,
message: '请选择活动区域',
trigger: 'change'
}],
}
],
otherColumns: [
{
prop: "activeName",
label: "活动名称",
width: "",
type: "input",
rules: [
{
required: true,
message: '请输入'
}
],
}
]
};
},
created () {
console.log('App---created---start', process.env, process.env.VUE_APP_BASE_API);
},
mounted () {
},
computed: {
},
methods: {
handleDelRow (index, tableName) {
this.form[tableName].splice(index, 1);
},
validateFormsByFormRefs (formRefs) {
return formRefs.map(ref => {
return new Promise((resolve, reject) => {
ref.validate((valid) => {
if (valid) resolve();
}
);
});
})
},
submitForm () {
const formRef1 = this.$refs.tableForm.$refs.tableWithForm
const formRef2 = this.$refs.otherTableForm.$refs.tableWithForm
const formRef3 = this.$refs.ruleForm
Promise.all(this.validateFormsByFormRefs([formRef1, formRef2, formRef3])).then(() => {
console.log('all formRules passed', this.ruleForm, this.form);
})
},
resetForm () {
const formRef1 = this.$refs.tableForm.$refs.tableWithForm
const formRef2 = this.$refs.otherTableForm.$refs.tableWithForm
const formRef3 = this.$refs.ruleForm
formRef1.resetFields();
formRef2.resetFields();
formRef3.resetFields();
}
},
}
</script>
<style lang="scss">
</style>
动态el-menu
<template>
<el-menu :default-active="activeIndex" style="width:300px" class="el_menu" background-color="#409eff" text-color="white" active-text-color="yellow" :unique-opened="true" router ref="elMenu">
<MenuItem :data="menuArr">
</MenuItem>
</el-menu>
</template>
<script>
import MenuItem from "./MenuItem.vue";
export default {
name: "Menu",
components: {
MenuItem,
},
data () {
return {
activeIndex: this.$route.path,
menuArr: [
{
path: "/home",
name: "首页",
icon: "el-icon-location",
children: [],
},
{
path: "/work",
name: "我的工作台",
icon: "el-icon-s-tools",
children: [
{
path: "/work1",
name: "工作台1",
children: [
{
path: "/work1-1",
name: "我的工作台1-1",
children: [],
},
{
path: "/work1-2",
name: "我的工作台1-2",
children: [],
}
],
},
{
path: "/work2",
name: "工作台2",
children: [],
},
],
},
{
path: "/user",
name: "用户",
icon: "el-icon-user",
children: [],
},
]
};
}
};
</script>
<style scoped>
</style>
<template>
<div class="menu_item">
<template v-for="(item, index) in data">
<el-submenu :key="index" :index="item.path" v-if="item.children.length > 0">
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.name }}</span>
</template>
<MenuItem :data="item.children" />
</el-submenu>
<el-menu-item :key="index" v-else :index="item.path">
<i :class="item.icon"></i>
<span slot="title">{{ item.name }}</span>
</el-menu-item>
</template>
</div>
</template>
<script>
export default {
name: "MenuItem",
props: {
data: {
type: Array,
default: [],
},
},
};
</script>
<style lang="scss">
.menu_item {
[class*='el-icon-'] {
color: yellow;
}
}
</style>
selectTree
<template>
<el-select v-model="selectValue" multiple placeholder="请选择" :popper-append-to-body="false" size="small" @remove-tag="removetag" collapse-tags @clear="clearall" clearable popper-class="select_tree">
<el-option :value="selectedIds" class="select_option" disabled>
<el-tree :data="treeList" :props="defaultProps" ref="tree" :check-strictly="isSingleChoice" show-checkbox :expand-on-click-node="false" @check="treeCurrentChange" node-key="id" check-on-click-node></el-tree>
</el-option>
</el-select>
</template>
<script>
import { areaListRequest, orgListRequest } from "@/coreCommon/request/common.js"
export default {
name: "SelectTree",
props: {
businessType: {
type: String,
default: "area",
validator(value) {
const islegal = ['area', 'org'].includes(value)
if (!islegal) {
console.error(`The businessType must match one of the following strings:${['area', 'org']}`)
}
return islegal;
}
},
isSingleChoice: {
type: Boolean,
default: false
}
},
computed: {
},
data() {
return {
businessMap: new Map([
["area", areaListRequest],
["org", orgListRequest]]),
selectValue: [],
selectedIds: [],
selectedItems:[],
defaultProps: { children: "children", label: "name", id: "id" },
treeList: [],
}
},
watch: {
selectValue(newValue) {
},
selectedIds(newValue) {
},
},
created() {
console.log('SelectTree---created---start---businessType', this.businessType);
this.getTreeList(this.businessType)
},
methods: {
async getTreeList(businessType) {
const treeRes = await this.businessMap.get(businessType)();
if (businessType === 'area') {
treeRes.forEach(item => { item.name = item.areaSimpleName })
}
this.treeList = this.$utils.listToTree(treeRes)
console.log('this.treeList', this.treeList);
},
treeCurrentChange(currentNode, { checkedKeys, checkedNodes }) {
console.log('treeCurrentChange---start---currentNode', currentNode);
if (this.isSingleChoice && checkedKeys.length > 0) {
this.$refs.tree.setCheckedKeys([currentNode.id]);
this.$refs.tree.getCheckedNodes([currentNode]);
}
const checkedNodeList = this.$refs.tree.getCheckedNodes()
this.selectedIds = []
this.selectedItems = []
this.selectValue = []
checkedNodeList.forEach((item) => {
this.selectedIds.push(item.id)
this.selectedItems.push(item)
this.selectValue.push(item.name)
})
},
removetag() {
this.selectedIds.splice(0, 1)
this.selectedItems.splice(0, 1)
const checkedNodes = this.$refs.tree.getCheckedNodes()
checkedNodes.splice(0, 1)
this.$nextTick(() => {
this.$refs.tree.setCheckedNodes(checkedNodes)
})
},
clearall() {
this.selectedIds = []
this.selectedItems = []
this.$nextTick(() => {
this.$refs.tree.setCheckedNodes([])
})
},
},
}
</script>
<style lang="scss" scoped>
.select_option {
overflow: hidden;
min-height: 200px;
padding: 0 !important;
margin: 0;
overflow: auto;
cursor: default !important;
}
</style>
selectList
<template>
<el-select v-loadMore="loadMore" v-model="selectedId" filterable remote reserve-keyword placeholder="请输入" :remote-method="getOptions" :loading="loading" size="small">
<el-option v-for="item in options" :key="item.value" :label="item.name" :value="item.value">
</el-option>
</el-select>
</template>
<script>
import { employeePageRequest, orgInfoPageRequest, prepaidSearchRequest } from "@/coreCommon/request/common.js"
export default {
name: "SelectList",
directives: {
'loadMore': {
bind(el, binding) {
const dropdownDom = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
dropdownDom.addEventListener('scroll', function () {
const condition = this.scrollHeight - this.scrollTop <= this.clientHeight
console.log(' this.scrollHeight - this.scrollTop <= this.clientHeight', this.scrollHeight, this.scrollTop, this.clientHeight);
if (condition) {
console.log('directives---loadMore---start');
binding.value()
}
})
}
}
},
components: {
},
props: {
businessType: {
type: String,
default: "employeePage",
},
},
data() {
return {
businessMap: new Map([
["employeePage", { request: employeePageRequest, data: { keyName: "name", currentName: 'current', sizeName: "size" }, resAlias: { name: "name", value: "userId" } }],
["prepaid", { request: prepaidSearchRequest, data: { keyName: "key", currentName: 'pageIndex', sizeName: "pageSize" }, resAlias: { name: "customerName", value: "id" } }],
["allOrgs", { request: orgInfoPageRequest, data: { keyName: "orgName", currentName: 'current', sizeName: "size" }, resAlias: { name: "customerName", value: "id" } }],
["headOrg", { request: orgInfoPageRequest, data: { keyName: "orgName", currentName: 'current', sizeName: "size", payloads: { level: "0" } }, resAlias: { name: "shortName", value: "id" } }]]),
keyWord: "",
selectedId: "",
selectedItem: {},
options: [],
loading: false,
current: 1,
size: 10,
}
},
async created() {
this.getOptions("")
},
watch: {
selectedId(newValue) {
console.log('watch---selectedId---start', newValue);
this.selectedItem = this.options.find(item => item.value === newValue)
}
},
computed: {
},
mounted() {
},
methods: {
loadMore() {
console.log('methods---loadMore---start');
this.fetchOptions(this.keyWord)
},
resetPageInfo(current = 1, size = 10) {
this.current = current
this.size = size
this.options = []
},
async fetchOptions(keyWord) {
const handler = this.businessMap.get(this.businessType);
if (!handler) {
throw new Error(`this.businessType:${this.businessType}尚未配置`)
}
const data = handler['data']
const keyName = data['keyName']
const currentName = data['currentName']
const sizeName = data['sizeName']
const { records, total } = await handler['request']({ [keyName]: keyWord, [currentName]: this.current, [sizeName]: this.size, ...data.payloads })
if (Array.isArray(records) && records.length > 0) {
const resAlias = handler['resAlias']
const resName = resAlias['name']
const valueName = resAlias['value']
this.current++
this.options = this.options.concat(records.map(item => ({ name: item[resName], value: item[valueName], ...item })))
}
},
getOptions(keyWord) {
console.log('getOptions---start---keyWord', keyWord);
this.keyWord = keyWord
this.resetPageInfo();
this.fetchOptions(keyWord);
}
},
};
</script>
<style lang="scss" >
// .el-select-dropdown__wrap {
// max-height: 80px;
// }
</style>
表单批量校验
validateFormsByFormRefsMixin(formRefs = []) {
return formRefs.map(ref => {
return new Promise((resolve, reject) => {
ref.validate((valid) => {
if (valid) resolve();
}
);
});
})
},
const formRef1 = this.$refs.businessLicenseFormRef
const formRef2 = this.$refs.legalFormRef
const formRef3 = this.$refs.basicInfoRef
const formRef4 = this.$refs.beneficiaryTableFormRef
const formRef5 = this.$refs.stockHolderFormRef
await Promise.all(this.validateFormsByFormRefsMixin([formRef1, formRef2, formRef3, formRef4, formRef5]))
console.log('all rules passed')