378 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			378 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Vue
		
	
	
	
<template>
 | 
						|
  <div style="text-align: left">
 | 
						|
    <div class="table-search" v-if="searchItems.length > 0">
 | 
						|
      <a-form class="ant-advanced-search-form" :form="form" @submit="handleSearch" layout="inline"
 | 
						|
              @change="handleSearchFormChange">
 | 
						|
        <a-form-item v-for="menuItem in searchItems" :key="menuItem.key" :label="menuItem.label">
 | 
						|
          <a-input
 | 
						|
            v-decorator="[menuItem.key]"
 | 
						|
            :placeholder="menuItem.placeholder || ''"
 | 
						|
            v-if="menuItem.type === 'input'"
 | 
						|
          />
 | 
						|
          <a-select
 | 
						|
            v-decorator="[menuItem.key]"
 | 
						|
            :placeholder="menuItem.placeholder || ''"
 | 
						|
            v-else-if="menuItem.type === 'select'"
 | 
						|
            :dropdown-class-name="dropdownClassName"
 | 
						|
          >
 | 
						|
            <a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">
 | 
						|
              {{ cItem.label }}
 | 
						|
            </a-select-option>
 | 
						|
          </a-select>
 | 
						|
          <a-range-picker
 | 
						|
            :placeholder="menuItem.placeholder || ''" v-decorator="[menuItem.key]"
 | 
						|
            v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD"
 | 
						|
            :dropdown-class-name="dropdownClassName" />
 | 
						|
        </a-form-item>
 | 
						|
        <a-form-item>
 | 
						|
          <a-button type="primary" html-type="submit" v-if="!hideSearch">查询</a-button>
 | 
						|
          <a-button :style="{ marginLeft: '8px' }" @click="reset" v-if="!hideSearch">重置</a-button>
 | 
						|
          <slot name="search-button" v-bind:search="form.getFieldsValue()"></slot>
 | 
						|
        </a-form-item>
 | 
						|
      </a-form>
 | 
						|
    </div>
 | 
						|
    <a-divider v-if="!hideButton && searchItems.length > 0"/>
 | 
						|
    <div class="table-operations" v-if="!hideButton">
 | 
						|
      <a-button @click="addRow" type="primary">
 | 
						|
        添加
 | 
						|
      </a-button>
 | 
						|
    </div>
 | 
						|
    <a-table v-bind="tableProps" :pagination="page" :row-key="primaryKey" :columns="realColumns"
 | 
						|
             :data-source="tableDataSource" @change="handleChange">
 | 
						|
      <span slot="action" slot-scope="text, record">
 | 
						|
        <a-button type="danger" size="small" @click="deleteRow(record)">删除</a-button>
 | 
						|
        <a-divider type="vertical"/>
 | 
						|
        <a-button type="primary" size="small" @click="editRow(record)">编辑</a-button>
 | 
						|
      </span>
 | 
						|
      <span slot="details" slot-scope="text, record">
 | 
						|
<!--        <a-button type="danger" size="small" @click="deleteRow(record)">删除</a-button>-->
 | 
						|
        <!--        <a-divider type="vertical"/>-->
 | 
						|
        <a-button type="primary" size="small" @click="viewDetails(record)">详情</a-button>
 | 
						|
      </span>
 | 
						|
    </a-table>
 | 
						|
    <a-modal
 | 
						|
      :title="dialog.title"
 | 
						|
      :visible="dialog.visible"
 | 
						|
      :confirm-loading="dialog.loading"
 | 
						|
      @ok="handleOk"
 | 
						|
      @cancel="handleCancel"
 | 
						|
    >
 | 
						|
      <a-form class="ant-advanced-search-form" :form="editForm" @submit="handleSearch" @change="handleSearchFormChange">
 | 
						|
        <a-form-item v-for="menuItem in formItems" :key="menuItem.key" :label="menuItem.label" v-if="!menuItem.hide">
 | 
						|
          <a-input
 | 
						|
            v-bind="menuItem.props || {}"
 | 
						|
            v-decorator="gtDecorator(menuItem)"
 | 
						|
            :placeholder="menuItem.placeholder || ''"
 | 
						|
            v-if="menuItem.type === 'input'"
 | 
						|
          />
 | 
						|
          <a-select
 | 
						|
            v-decorator="gtDecorator(menuItem)"
 | 
						|
            :placeholder="menuItem.placeholder || ''"
 | 
						|
            :dropdown-class-name="dropdownClassName"
 | 
						|
            v-else-if="menuItem.type === 'select'"
 | 
						|
            v-bind="menuItem.props || {}"
 | 
						|
          >
 | 
						|
            <a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">
 | 
						|
              {{ cItem.label }}
 | 
						|
            </a-select-option>
 | 
						|
          </a-select>
 | 
						|
          <a-tree-select
 | 
						|
            v-bind="menuItem.props || {}"
 | 
						|
            v-decorator="gtDecorator(menuItem)"
 | 
						|
            :placeholder="menuItem.placeholder || ''"
 | 
						|
            :dropdown-class-name="dropdownClassName"
 | 
						|
            allow-clear
 | 
						|
            :tree-data="menuItem.children"
 | 
						|
            tree-default-expand-all
 | 
						|
            style="width: 100%"
 | 
						|
            :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
 | 
						|
            v-else-if="menuItem.type === 'treeSelect'"
 | 
						|
          >
 | 
						|
            <a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">
 | 
						|
              {{ cItem.label }}
 | 
						|
            </a-select-option>
 | 
						|
          </a-tree-select>
 | 
						|
          <a-range-picker v-bind="menuItem.props || {}" v-decorator="gtDecorator(menuItem)"
 | 
						|
                          v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD" :dropdown-class-name="dropdownClassName" />
 | 
						|
 | 
						|
          <a-date-picker  style="width: 100%" v-decorator="gtDecorator(menuItem)" v-bind="menuItem.props || {}"
 | 
						|
                         v-else-if="menuItem.type === 'date'" format="YYYY-MM-DD" :dropdown-class-name="dropdownClassName"/>
 | 
						|
        </a-form-item>
 | 
						|
      </a-form>
 | 
						|
    </a-modal>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
<script>
 | 
						|
import moment from "moment";
 | 
						|
import { get } from 'lodash'
 | 
						|
 | 
						|
export default {
 | 
						|
  name: "user",
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      cacheData: [],
 | 
						|
      isValidate: false,
 | 
						|
      tableDataSource: [],
 | 
						|
      form: this.$form.createForm(this, {name: `form_${new Date().valueOf()}`}),
 | 
						|
      editForm: this.$form.createForm(this, {name: `edit_form_${new Date().valueOf()}`}),
 | 
						|
      dialog: {
 | 
						|
        title: '',
 | 
						|
        visible: false,
 | 
						|
        isEdit: '',
 | 
						|
        loading: false
 | 
						|
      },
 | 
						|
      selectRow: null,
 | 
						|
      page: {
 | 
						|
        pageSize: 10,
 | 
						|
        current: 1,
 | 
						|
        total: 0
 | 
						|
      },
 | 
						|
      searchFormModel: {}
 | 
						|
    }
 | 
						|
  },
 | 
						|
  props: {
 | 
						|
    tableProps: {
 | 
						|
      type: Object,
 | 
						|
      default: () => ({})
 | 
						|
    },
 | 
						|
    dropdownClassName: {
 | 
						|
      type: String,
 | 
						|
      default: ''
 | 
						|
 | 
						|
    },
 | 
						|
    hideSearch: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false
 | 
						|
    },
 | 
						|
    hideAction: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false
 | 
						|
    },
 | 
						|
    hideButton: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false
 | 
						|
    },
 | 
						|
    // 表单内容配置
 | 
						|
    formItems: {
 | 
						|
      type: Array,
 | 
						|
      default: () => []
 | 
						|
    },
 | 
						|
    // 列表列配置
 | 
						|
    columns: {
 | 
						|
      type: Array,
 | 
						|
      default: () => []
 | 
						|
    },
 | 
						|
    primaryKey: {
 | 
						|
      type: String,
 | 
						|
      default: 'id'
 | 
						|
    },
 | 
						|
 | 
						|
    apiConf: {
 | 
						|
      type: Object,
 | 
						|
      default: () => ({})
 | 
						|
    },
 | 
						|
    customSearch: {
 | 
						|
      type: Function,
 | 
						|
      default: () => null
 | 
						|
 | 
						|
    }
 | 
						|
  },
 | 
						|
  computed: {
 | 
						|
    searchItems() {
 | 
						|
      return this.formItems.filter(item => !!item.isSearch)
 | 
						|
    },
 | 
						|
    realColumns() {
 | 
						|
      if (this.hideAction) return [...this.columns]
 | 
						|
      return [
 | 
						|
        ...this.columns,
 | 
						|
        {
 | 
						|
          title: '操作',
 | 
						|
          dataIndex: 'action',
 | 
						|
          scopedSlots: {customRender: 'action'},
 | 
						|
          fixed: 'right',
 | 
						|
          width: 155
 | 
						|
        },
 | 
						|
      ]
 | 
						|
    }
 | 
						|
  },
 | 
						|
  watch: {},
 | 
						|
  mounted() {
 | 
						|
    this.handleSearch()
 | 
						|
  },
 | 
						|
  methods: {
 | 
						|
    viewDetails(data) {
 | 
						|
      this.$emit('viewDetails', data)
 | 
						|
    },
 | 
						|
    gtDecorator(menuItem) {
 | 
						|
      if (menuItem.rules) {
 | 
						|
        this.isValidate = true
 | 
						|
        return [
 | 
						|
          menuItem.key,
 | 
						|
          {rules: menuItem.rules}
 | 
						|
        ]
 | 
						|
      }
 | 
						|
      return [
 | 
						|
        menuItem.key
 | 
						|
      ]
 | 
						|
    },
 | 
						|
    handleSearchFormChange(changedFields) {
 | 
						|
      this.searchFormModel = {...this.searchFormModel, ...changedFields}
 | 
						|
    },
 | 
						|
    addRow() {
 | 
						|
      this.handleCancel()
 | 
						|
      this.$emit('edit', false)
 | 
						|
      this.dialog.visible = true
 | 
						|
    },
 | 
						|
    async editRow(row) {
 | 
						|
      if (this.apiConf.detailApi) {
 | 
						|
        const {api, method, detailHandler} = this.apiConf.detailApi
 | 
						|
        const {data} = await this[`$${method}`](api, {[this.primaryKey]: row[this.primaryKey]})
 | 
						|
        this.selectRow = detailHandler && detailHandler(data) || data
 | 
						|
      } else {
 | 
						|
        this.selectRow = row
 | 
						|
      }
 | 
						|
      this.dialog.title = '编辑'
 | 
						|
      this.dialog.visible = true
 | 
						|
      this.dialog.isEdit = true
 | 
						|
      this.dialog.loading = false
 | 
						|
      this.$emit('edit', true)
 | 
						|
      setTimeout(() => {
 | 
						|
        this.editForm.setFieldsValue(this.selectRow)
 | 
						|
      }, 200)
 | 
						|
    },
 | 
						|
    deleteRow(row) {
 | 
						|
      const self = this
 | 
						|
      if (!self.apiConf.deleteApi) return
 | 
						|
      this.$confirm({
 | 
						|
        title: '确认要删除么?',
 | 
						|
        okText: '确认',
 | 
						|
        okType: 'danger',
 | 
						|
        cancelText: '再想想',
 | 
						|
        onOk() {
 | 
						|
 | 
						|
          const {api, deleteKey, paramsType, method} = self.apiConf.deleteApi
 | 
						|
          let params = {}
 | 
						|
          if (paramsType === 'Array') {
 | 
						|
            params = [row[self.primaryKey]]
 | 
						|
          } else {
 | 
						|
            params[deleteKey] = row[self.primaryKey]
 | 
						|
          }
 | 
						|
          self[`$${method}`](api, params, {params: params}).then(res => {
 | 
						|
            self.handleSearch()
 | 
						|
          })
 | 
						|
        }
 | 
						|
      });
 | 
						|
 | 
						|
 | 
						|
    },
 | 
						|
    handleChange({pageSize, current}) {
 | 
						|
      this.page.pageSize = pageSize
 | 
						|
      this.page.current = current
 | 
						|
      this.handleSearch()
 | 
						|
    },
 | 
						|
    reset() {
 | 
						|
      this.form.resetFields();
 | 
						|
    },
 | 
						|
    doSearch () {
 | 
						|
      if (this.customSearch) {
 | 
						|
        this.tableDataSource = this.customSearch(JSON.parse(JSON.stringify(this.cacheData)))
 | 
						|
      }
 | 
						|
    },
 | 
						|
    async handleSearch(e) {
 | 
						|
      e && e.preventDefault();
 | 
						|
      const rangeList = ['dateRange']
 | 
						|
      const values = this.form.getFieldsValue()
 | 
						|
      const searchValues = {}
 | 
						|
      for (const formItem of this.formItems.filter(item => item.isSearch)) {
 | 
						|
        if (rangeList.indexOf(formItem.type) >= 0) {
 | 
						|
          formItem.fields.forEach((field, index) => {
 | 
						|
            if (formItem.type.indexOf('date') >= 0) {
 | 
						|
              searchValues[field] = values[formItem.key] && values[formItem.key][index] && moment(values[formItem.key][index]).format('YYYY-MM-DD') || ''
 | 
						|
            }
 | 
						|
          })
 | 
						|
        } else {
 | 
						|
          searchValues[formItem.key] = values[formItem.key]
 | 
						|
        }
 | 
						|
      }
 | 
						|
      const {api, noPage, method, renderKey, after} = this.apiConf.listApi
 | 
						|
      const {data} = await this[`$${method}`](api, {
 | 
						|
        ...searchValues,
 | 
						|
        pageSize: this.page.pageSize,
 | 
						|
        current: this.page.current,
 | 
						|
      })
 | 
						|
      if (noPage) {
 | 
						|
        this.page = false
 | 
						|
        this.cacheData = renderKey ? get(data, renderKey) : data
 | 
						|
        this.tableDataSource = renderKey ? get(data, renderKey) : data
 | 
						|
        after && after(data)
 | 
						|
      } else {
 | 
						|
        this.page.total = data.total || 0
 | 
						|
        this.cacheData = renderKey ? get(data, renderKey) : data.items
 | 
						|
        this.tableDataSource = renderKey ? get(data, renderKey) : data.items
 | 
						|
        after && after(data)
 | 
						|
      }
 | 
						|
    },
 | 
						|
    async handleOk() {
 | 
						|
      if (this.isValidate) {
 | 
						|
        this.editForm.validateFields(async (err, values) => {
 | 
						|
          if (!err) {
 | 
						|
            if (this.dialog.isEdit) {
 | 
						|
              const {api, method, updateKey} = this.apiConf.editApi
 | 
						|
              await this[`$${method}`](api, {
 | 
						|
                [updateKey || this.primaryKey]: this.selectRow[this.primaryKey],
 | 
						|
                ...values
 | 
						|
              })
 | 
						|
            } else {
 | 
						|
              const {api, method} = this.apiConf.addApi
 | 
						|
              await this[`$${method}`](api, values)
 | 
						|
            }
 | 
						|
            this.handleCancel()
 | 
						|
            this.handleSearch()
 | 
						|
          }
 | 
						|
        });
 | 
						|
      } else {
 | 
						|
        const values = this.editForm.getFieldsValue()
 | 
						|
        if (this.dialog.isEdit) {
 | 
						|
          const {api, method, updateKey} = this.apiConf.editApi
 | 
						|
          await this[`$${method}`](api, {
 | 
						|
            [updateKey || this.primaryKey]: this.selectRow[this.primaryKey],
 | 
						|
            ...values
 | 
						|
          })
 | 
						|
        } else {
 | 
						|
          const {api, method} = this.apiConf.addApi
 | 
						|
          await this[`$${method}`](api, values)
 | 
						|
        }
 | 
						|
        this.handleCancel()
 | 
						|
        this.handleSearch()
 | 
						|
      }
 | 
						|
 | 
						|
    },
 | 
						|
    handleCancel() {
 | 
						|
      this.selectRow = null
 | 
						|
      this.dialog.title = '添加'
 | 
						|
      this.dialog.visible = false
 | 
						|
      this.dialog.isEdit = false
 | 
						|
      this.dialog.loading = false
 | 
						|
      this.editForm.resetFields()
 | 
						|
    },
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
<style scoped lang="less">
 | 
						|
.table-operations {
 | 
						|
  margin-bottom: 16px;
 | 
						|
}
 | 
						|
 | 
						|
.ant-form-item {
 | 
						|
  margin-bottom: 0;
 | 
						|
}
 | 
						|
 | 
						|
.ant-divider-horizontal {
 | 
						|
  margin: 16px 0;
 | 
						|
}
 | 
						|
 | 
						|
.table-operations > button {
 | 
						|
  margin-right: 8px;
 | 
						|
}
 | 
						|
</style>
 |