render 函数封装element-ui表格 form表单
2022-04-02 本文已影响0人
如果俞天阳会飞
用法
<template>
<div>
<base-search :params="listQuery" :items="queryItems" @search="handleFilter">
<template slot="mobile">
<el-input v-model="listQuery.mobile" placeholder="请输入手机号"/>
</template>
</base-search>
<base-table
:loading="listLoading"
:data="list"
:columns="columns"
:stripe="true"
border
@row-click="rowClick">
<template slot="importance" slot-scope="{row}">
<svg-icon v-for="n in +row.importance" :key="n" icon-class="star" class="meta-item__icon"/>
</template>
<template slot="pageviews" slot-scope="{row}">
<span v-if="row.pageviews" class="link-type" @click="handleFetchPv(row.pageviews)">{{ row.pageviews }}</span>
<span v-else>0</span>
</template>
<template slot="status" slot-scope="{row}">
<el-tag :type="row.status | statusFilter">{{ row.status }}</el-tag>
</template>
<template slot="operation" slot-scope="scope">
<el-button
type="primary"
size="mini"
@click="handleUpdate(scope.row)">{{ $t('table.edit') }}</el-button>
<el-button
v-if="scope.row.status!='published'"
size="mini"
type="success"
@click="handleModifyStatus(scope.row,'published')">{{ $t('table.publish') }}
</el-button>
<el-button
v-if="scope.row.status!='draft'"
size="mini"
@click="handleModifyStatus(scope.row,'draft')">{{ $t('table.draft') }}
</el-button>
<el-button
v-if="scope.row.status!='deleted'"
size="mini"
type="danger"
@click="handleModifyStatus(scope.row,'deleted')">{{ $t('table.delete') }}
</el-button>
</template>
</base-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList" />
<base-dialog
:visible.sync="dialogPvVisible"
:loading="loading"
title="Reading statistics"
@cancel="handleCancel"
@ok="handleOk">
<base-form ref="baseForm" :params="formParams" :items="formItems"/>
</base-dialog>
</div>
</template>
<script>
import BaseTable from './components/baseTable'
import { fetchList, fetchPv } from '@/api/article'
import { parseTime } from '@/utils'
import BaseSearch from './components/baseSearch'
import Pagination from '../../components/Pagination'
import BaseDialog from './components/baseDialog'
import BaseForm from './components/baseForm'
const columns = [
{ prop: 'id', label: 'id' },
{ prop: 'date', label: 'date', render: (h, { row }) => {
const value = parseTime(row.timestamp, '{y}-{m}-{d} {h}:{i}')
return <span>{ value }</span>
} },
{
prop: 'title', label: 'title',
render: (h, { index, row }) => {
return <span>{row.title}</span>
}
},
{
prop: 'author', label: 'author'
},
{
prop: 'importance', label: 'importance'
},
{
prop: 'pageviews', label: 'pageviews'
},
{
prop: 'status', label: 'status'
},
{
prop: 'operation', width: '220', label: '操作'
}
]
const queryItems = [
{ label: 'ID', prop: 'id', type: 'input',
config: {
props: {},
attrs: { placeholder: '请输入ID' },
on: {}
}
},
{ label: '作者', prop: 'author', type: 'input' },
{ label: '手机号', prop: 'mobile', type: 'input' },
{
label: '状态', prop: 'status', type: 'select',
config: {
props: {},
attrs: { placeholder: '请选择文章标签' },
on: {}
},
option: [
{ label: '成功', value: 1 },
{ label: '失败', value: 0 }
]
},
{
label: '日期范围', prop: 'datetimerange', type: 'datePicker',
config: {
props: {
type: 'datetimerange',
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd'
},
attrs: {},
on: {}
}
},
{
label: '日期', prop: 'timer', type: 'datePicker',
config: {
props: {
// type: 'datetime',
// format: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'yyyy-MM-dd HH:mm:ss'
},
attrs: {},
on: {}
}
}
]
const formItems = [
{ label: 'ID', prop: 'id', type: 'input',
rules: [{ required: true, message: '请输入ID' }],
input: {
props: {},
attrs: { placeholder: '请输入ID' },
on: {}
}
},
{ label: '作者', prop: 'author', type: 'input' },
{ label: '手机号', prop: 'mobile', type: 'input' },
{
label: '状态', prop: 'status', type: 'select',
config: {
props: {},
attrs: { placeholder: '请选择文章标签' },
on: {}
},
option: [
{ label: '成功', value: 1 },
{ label: '失败', value: 0 }
]
},
{
label: '日期范围', prop: 'datetimerange', type: 'datePicker',
config: {
props: {
type: 'datetimerange',
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd'
},
attrs: {},
on: {}
}
},
{
label: '日期', prop: 'timer', type: 'datePicker',
config: {
props: {
// type: 'datetime',
// format: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'yyyy-MM-dd HH:mm:ss'
},
attrs: {},
on: {}
}
},
{
label: '资源', prop: 'resource', type: 'radio',
config: {},
option: [{ label: '1', text: '线上品牌商赞助' }, { label: '2', text: '线下场地免费' }]
},
{
label: '资源2', prop: 'resource2', type: 'checkbox',
config: {},
option: [{ label: '1', text: '线上品牌商赞助' }, { label: '2', text: '线下场地免费' }]
}
]
export default {
name: 'TestTable',
components: { BaseForm, BaseDialog, Pagination, BaseSearch, BaseTable },
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
listQuery: {
page: 1,
limit: 20,
author: '111',
status: null,
timer: null,
datetimerange: [],
mobile: '',
importance: undefined,
title: undefined,
type: undefined,
id: ''
},
formParams: {
page: 1,
limit: 20,
author: '111',
status: null,
timer: null,
datetimerange: [],
mobile: '',
importance: undefined,
title: undefined,
type: undefined,
resource: undefined,
resource2: [],
id: ''
},
total: 0,
list: null,
listLoading: false,
columns,
queryItems,
formItems,
pvData: [],
dialogPvVisible: false,
loading: false
}
},
created() {
this.getList()
},
methods: {
rowClick() {
console.log('rowClick')
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
setTimeout(() => {
this.listLoading = false
}, 1.5 * 1000)
})
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData
this.dialogPvVisible = true
})
},
handleModifyStatus(row, status) {
this.$message({
message: '操作成功',
type: 'success'
})
row.status = status
},
handleOk() {
this.$refs.baseForm.validate((valid) => {
if (valid) {
this.loading = true
setTimeout(() => {
this.loading = false
}, 1000)
} else {
console.log('error submit!!')
return false
}
})
},
handleCancel() {
this.dialogPvVisible = false
}
}
}
</script>
<style scoped>
</style>
表格
// baseTable.vue
<script>
export default {
name: 'BaseTable',
props: {
data: {
type: Array,
default() {
return []
}
},
columns: {
type: Array,
default() {
return []
}
}
},
render(h) {
const { data, columns, $attrs, $listeners, $scopedSlots } = this
const { loading } = $attrs
const tableAttrs = {
props: { ...$attrs },
on: { ...$listeners }
}
const table = <el-table
v-loading={loading}
{...tableAttrs}
data={data}>
{
columns.map((col) => {
const colAttrs = {
props: { ...col },
scopedSlots: {
default: scope => {
const hasRender = col.hasOwnProperty('render')
if (hasRender) {
return col.render(h, scope)
}
const customRender = $scopedSlots[col.prop]
return customRender ? customRender(scope) : scope.row[col.prop]
}
}
}
return <el-table-column {...colAttrs}/>
})
}
</el-table>
return <div>
{table}
</div>
}
}
</script>
form 表单
// createForm.js
export default {
methods: {
renderType(row) {
const { params } = this
const { config = {}} = row
const defaultAttrs = {
props: {
value: params[row.prop],
...config.props
},
attrs: {
...config.attrs
},
on: {
input(value) {
params[row.prop] = value
},
...config.on
}
}
const types = {
input: () => {
const { props, attrs } = defaultAttrs
const { clearable } = props
const { placeholder } = attrs
props.clearable = clearable === undefined ? true : clearable
attrs.placeholder = placeholder === undefined ? '请输入' : placeholder
return <el-input {...defaultAttrs}/>
},
select: () => {
const { attrs, on } = defaultAttrs
const { placeholder } = attrs
attrs.placeholder = placeholder === undefined ? '请选择' : placeholder
if (!on.change) {
on.change = function(value) {
params[row.prop] = value
}
}
return <el-select {...defaultAttrs}>
{row.option && row.option.map((item, index) => {
const obj = {
props: { ...item },
on: { ...item }
}
return <el-option key={index} {...obj}></el-option>
})}
</el-select>
},
datePicker: () => {
/*
* type 类型 year/month/date/week/ datetime/datetimerange/daterange
* 默认 date
* 日期格式 yyyy-MM-dd HH:mm:ss
* */
const { attrs } = defaultAttrs
const { startPlaceholder, endPlaceholder } = attrs
attrs.startPlaceholder = startPlaceholder === undefined ? '开始日期' : startPlaceholder
attrs.endPlaceholder = endPlaceholder === undefined ? '结束日期' : endPlaceholder
return <el-date-picker {...defaultAttrs}/>
},
radio: () => {
return <el-radio-group {...defaultAttrs}>
{row.option && row.option.map((item, index) => {
return <el-radio key={index} {...{ props: { ...item }, attrs: { ...item }, on: { ...item }}}>
{item.text}
</el-radio>
})}
</el-radio-group>
},
checkbox: () => {
return <el-checkbox-group {...defaultAttrs}>
{row.option && row.option.map((item, index) => {
return <el-checkbox key={item.label} {...{ props: { ...item }, attrs: { ...item }, on: { ...item }}}>
{item.text}
</el-checkbox >
})}
</el-checkbox-group>
}
}
return types[row.type]()
},
form(h) {
const { items, params,
$listeners, $attrs, $slots } = this
const attrs = {
props: {
model: params,
...$attrs
},
ref: 'form',
on: { ...$listeners }
}
return <el-form {...attrs}>
{
items.map((item, index) => {
const obj = {
props: {
...item
},
scopedSlots: {
default: scope => {
const hasRender = item.hasOwnProperty('render')
if (hasRender) {
return item.render(h, scope)
}
const customRender = $slots[item.prop]
return customRender || this.renderType(item)
}
}
}
return <el-form-item key={index} {...obj}/>
})
}
</el-form>
}
}
}
baseForm.vue
<script>
import createForm from './createForm'
export default {
name: 'BaseForm',
mixins: [createForm],
props: {
params: {
type: Object,
default() {
return {}
}
},
items: {
type: Array,
default() {
return []
}
}
},
methods: {
validate(callback) {
this.$refs.form.validate(callback)
}
},
render(h) {
const { form } = this
const { labelWidth, labelPosition } = this.$attrs
this.$attrs.labelWidth = labelWidth || '100px'
this.$attrs.labelPosition = labelPosition || 'left'
return <div class={'form-container'}>
{form(h)}
</div>
}
}
</script>
<style scoped>
.form-container{
max-width: 460px;
}
</style>
baseSearch.vue
<script>
import createForm from './createForm'
export default {
name: 'BaseSearch',
mixins: [createForm],
props: {
params: {
type: Object,
default() {
return {}
}
},
items: {
type: Array,
default() {
return []
}
}
},
data() {
return {
defaultParams: {}
}
},
mounted() {
this.defaultParams = JSON.parse(JSON.stringify(this.params))
},
methods: {
handleReset() {
for (const key in this.defaultParams) {
this.params[key] = this.defaultParams[key]
}
this.$listeners.search()
}
},
render(h) {
const { form, $listeners } = this
this.$attrs.inline = true
return <el-card class={['filter-container']}>
<el-row type='flex' justify='space-between' { ...{ style: { marginBottom: '10px' }} }>
<el-col>
<i class={['el-icon-search']}/>
<span>筛选搜索</span>
</el-col>
<el-col {...{ style: { textAlign: 'right' }}}>
<el-button {...{
props: { type: 'primary', size: 'small' },
on: { click: $listeners.search }
}}>
查询搜索
</el-button>
<el-button {...{
props: { size: 'small' },
on: { click: this.handleReset }
}}>
重置
</el-button>
</el-col>
</el-row>
{form(h)}
</el-card>
}
}
</script>
<style scoped>
.filter-container{
margin-bottom: 10px;
}
</style>
baseDialog.vue
<script>
export default {
name: 'BaseDialog',
data() {
return {
loading: false
}
},
render(h) {
const { $attrs, $listeners, $slots } = this
const attrs = {
props: { ...$attrs },
on: { ...$listeners },
scopedSlots: {
default: scope => {
return $slots.default
},
title: scope => {
return $slots.title
},
footer: scope => {
const defaultFooter = (<span slot='footer' className={'dialog-footer'}>
<el-button {...{ on: { click: $listeners.cancel }}}>取消</el-button>
<el-button type='primary' {...{ props: { loading: $attrs.loading }, on: { click: $listeners.ok }}}>
确定
</el-button>
</span>)
return $slots.footer || defaultFooter
}
}
}
return <el-dialog {...attrs}>
<div slot='footer'/>
</el-dialog>
}
}
</script>
<style scoped>
</style>