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>

上一篇下一篇

猜你喜欢

热点阅读