<script>
  import {WebpageDataService, WebpageQueryService} from "../../../assets/webpage";

  export default {
    name: 'ZyTable',
    props: {
      source: {
        type: Object, default: () => {
          return {request: new Function(), params: {}};
        }
      },
      pagination: {type: Boolean, default: true,},
      pageSizes: {type: Array, default: () => [30, 50, 100, 500, 1000]},
      selMode: {type: Boolean, default: true,},
      height: {default: 'calc(100% - 36px)'},
      tableHeight: {default: 'calc(100% - 32px)'},
      showRefresh: {type: Boolean, default: true},
      selectable: {type: Function, default: () => true},
      dataType: {type: String, default: 'table'},
      spanMethod: {type: Function, default: null},
      requiredParams: {type: Array, default: () => []},
      filter: {type: String, default: null},
      filterMethod: {type: Function, default: data => data},
      rowStyle: {type: [Function, String], default: () => ''},
      rowClassName: {type: [Function, String], default: () => ''},
      cellStyle: {type: [Function, String], default: () => ''},
      cellClassName: {type: [Function, String], default: () => ''},
    },
    data() {
      return {
        page: 1,
        size: 30,
        tableData: [],
        tableColumns: [],
        customColumnKeys: [],
        total: 0,
        loading: false,
        options: {},
        row: {},
        currentRow: '',
        date: '',
        stripe: true,
        layouts: [{
          key: 'total', label: '总数', checked: true,
        }, {
          key: 'sizes', label: '每页条数', checked: true,
        }, {
          key: 'prev', label: '前一页', checked: true,
        }, {
          key: 'pager', label: '当前页', checked: true,
        }, {
          key: 'next', label: '下一页', checked: true,
        }, {
          key: 'jumper', label: '直接前往', checked: true,
        }, {
          key: 'slot', label: '刷新', checked: true, disabled: true
        }],
      }
    },
    inject: {
      pageData: {
        type: Function, default: () => {
          return () => {
            return {selection: [], params: {}, button: {}}
          }
        }
      }
    },
    created() {
    },
    mounted() {
      let _this = this;
      _this.getTable();
      window.test = this;
      $(document).on('click', function (e) {
        let element = e.target;
        while (element.parentNode) {
          if (element.className.includes('cell-editor')) {
            return;
          }
          element = element.parentNode;
        }
        _this.clearEditor();
      });
    },
    render() {
      let _this = this;
      return <div class="zy-table-div" style={'height: ' + this.height + ';'}>
        {
          _this.$scopedSlots['headnote'] ? _this.$scopedSlots['headnote']() : null
        }
        <el-table
            data={this.datas}
            highlight-current-row
            v-loading={this.loading}
            empty-text='暂无数据'
            border
            stripe={this.stripe}
            ref="table"
            height={this.tableHeight}
            class="zy-table"
            span-method={this.spanMethod ? this.spanMethod : this.generateSpan}
            row-style={this.rowStyle}
            row-class-name={this.rowClassName}
            cell-style={this.cellStyle}
            cell-class-name={this.cellClassName}
            header-row-style={this.rowStyle}
            header-cell-style={this.cellStyle}
            on-row-click={this.rowClick}
            on-cell-click={this.cellClick}
            on-cell-dblclick={this.cellDblClick}
            on-header-click={this.headerClick}
            on-header-dragend={this.headerDragend}
            on-selection-change={this.selectionChange}>
          {
            this.selMode && this.tableColumns.length > 0 ?
                <el-table-column fixed="left" type="selection" width="40" selectable={this.selectable}/>
                : null
          }
          {
            this.tableColumns.length > 0 ? (_this.$scopedSlots['before'] && _this.$scopedSlots['before']()) : null
          }
          {
            this.tableColumns.map(item => item.custom ? (
                this.generateTableCustomColumns(item)
            ) : (
                this.generateTableColumns(item)
            ))
          }
          {
            this.tableColumns.length > 0 ? (_this.$scopedSlots['after'] && _this.$scopedSlots['after']()) : null
          }
        </el-table>
        <el-row style="margin-top: 4px;">
          {
            this.$slots.pagination ? (_this.$scopedSlots['pagination']()) : (
                <el-pagination pager-count={5} page-sizes={this.pageSizes} class="common-pagination" style='float: right;'
                               total={this.pagination ? this.total : this.datas.length}
                               page-size={this.size} on-current-change={this.changePage} current-page={this.page}
                               on-size-change={this.handleSizeChange} layout={this.computedLayout}>
                  <i class="el-icon-refresh" onClick={this.onRefresh}/>
                  {
                    this.pagination ? (
                        <el-popover placement="top" trigger="click">
                          <div style="display: grid">
                            {
                              this.layouts.map(layout =>
                                  <el-checkbox label={layout.label} key={layout.key} value={layout.checked}
                                               disabled={layout.disabled}
                                               on-change={value => layout.checked = value}/>)
                            }
                          </div>
                          <i slot="reference" class="el-icon-menu"/>
                        </el-popover>
                    ) : null
                  }
                </el-pagination>
            )
          }
        </el-row>
      </div>
    },
    watch: {},
    computed: {
      computedLayout: {
        get() {
          let layout = _.map(_.filter(this.layouts, l => l.checked), 'key').join(', ');
          return !this.pagination ? 'total, slot' : layout;
        },
        set(value) {
        }
      },
      datas: {
        get() {
          if (this.filter) {
            return this.getFilteredData();
          } else {
            return this.filterMethod(this.tableData);
          }
        },
        set() {
        }
      },
    },
    methods: {
      clearData() {
        this.data = this.tableData = [];
      },
      getTable(page, size, loading) {
        let _this = this;
        _this.page = page || _this.page;
        _this.size = size || _this.size;
        if (_this.source.request instanceof Function) {
          if (_this.pagination) {
            _this.source.params = _this.source.params || {};
            _this.source.params.page = _this.page;
            _this.source.params.size = _this.size;
          }
          if (_this.requiredParams.length !== 0) {
            let valid = true;
            _this.requiredParams.forEach(item => {
              if (typeof _this.source.params[item] === 'boolean') {
                valid = valid && true;
              } else if (_this.source.params[item] instanceof Object) {
                valid = valid && Object.keys(_this.source.params[item]).length !== 0;
              } else if (_this.source.params[item] instanceof Array) {
                valid = valid && _this.source.params[item].length !== 0;
              } else {
                valid = valid && !!_this.source.params[item];
              }
            });
            if (!valid) {
              return;
            }
          }
          _this.loading = loading !== false;
          _this.source.request(_this.source.params, res => {
            this.stripe = res.content.previewStyle ? res.content.previewStyle.stripe : true;
            _this.generateTable(res.content, res.totalRecords);
            this.$emit('generate-slots', _this.tableColumns);
            _this.loading = false;
            this.selectionChange([], this.tableData);
          }, succeed => {
            _this.loading = false;
            if (!succeed) {
              this.tableData = [];
            }
          })
        } else {
          _this.tableData = _this.source.data;
          _this.total = _this.source.data.length;
          _this.tableColumns = _this.source.columns;
        }
      },
      generateTable(table, totalRecords) {
        let _this = this;
        _this.tableData = table.datas;
        _this.tableColumns = table.columns;
        _this.generateColumns(_this.tableColumns);
        _this.total = _this.pagination ? totalRecords : table.datas.length;
      },
      generateColumns(columns) {
        columns.forEach(column => {
          if (column.children instanceof Array && column.children.length !== 0) {
            this.generateColumns(column.children)
          }
          let formatterScript = column['formatter'];
          if (typeof formatterScript === 'function') {
            column.formatter = formatterScript;
          } else {
            column.formatter = function (row, column, cellValue) {
              if (row && Object.prototype.hasOwnProperty.call(row, column['property'])) {
                let content = cellValue instanceof Object ? cellValue.content : cellValue;
                if (typeof formatterScript === 'string') {
                  let _formatter = new Function('d', 'v', formatterScript);
                  return _formatter(row, content);
                }
                return content;
              }
            };
          }
          column.align = column['dataAlign'];
          column.children = column['children'] || [];
        });
      },
      generateSpan({row, column}) {
        if (this.customColumnKeys.includes(column['property'])) {
          return [1, 1];
        }
        if (column['type'] === 'selection' || !column['property']) {
          return [1, 1];
        }
        if (column['property'].includes('.')) {
          let keys = column['property'].split('.');
          let data = row;
          keys.forEach(key => {
            if (Object.prototype.hasOwnProperty.call(data, key)) {
              data = data[key];
            }
          });
          return data ? [1, 1] : [0, 0];
        }
        if (row && Object.prototype.hasOwnProperty.call(row, column['property'])) {
          if (row[column['property']] && row[column['property']].rowspan !== undefined) {
            return [row[column['property']].rowspan, row[column['property']].colspan];
          } else {
            return [1, 1]
          }
        } else {
          return [0, 0]
        }
      },
      generateTableCustomColumns(item) {
        if (this.customColumnKeys.indexOf(item.key) < 0) {
          this.customColumnKeys.push(item.key);
        }
        return <el-table-column {...{
          props: {
            label: item.label, prop: item.key, columnKey: (item.id || item.key) + '',
            sortable: item.sortable, headerAlign: item.headerAlign ? item.headerAlign : 'left',
            align: item.align ? item.align : 'left', minWidth: item.width,
            fixed: item.fixed ? item.fixed : false
          },
          scopedSlots: {
            default: scope => {
              return this.$scopedSlots[item.key]({row: scope.row})
            }
          }
        }}>
        </el-table-column>
      },
      generateTableColumns(item) {
        let headerAlign = item.children instanceof Array && item.children.length !== 0 ? 'center' : (item.headerAlign ? item.headerAlign : 'left');
        let props = {
          label: item.label, prop: item.key, className: item.lineBreak ? 'line-break' : '',
          columnKey: (item.id || item.key) + '',
          sortable: item.sortable, headerAlign: headerAlign, formatter: item.formatter,
          showOverflowTooltip: item.tooltip !== false, minWidth: item.width,
          align: item.align ? item.align : 'left', fixed: item.fixed ? item.fixed : false
        };
        let scopedSlots = item.editable ? {
          default: scope => this.generateCell(scope, item)
        } : {};
        return <el-table-column {...{
          props: props,
          scopedSlots: scopedSlots
        }}>
          {
            item.children instanceof Array && item.children.map(child => {
              return this.generateTableColumns(child)
            })
          }
        </el-table-column>
      },
      generateCell(scope, item) {
        let _this = this;
        let key = item.key;
        let editRange = item['editRange'] || [-Infinity, Infinity];
        let precision = item['editPrecision'] || 0;
        if (scope.row['cellEditable'] && scope.row['cellEditable'][key]) {
          switch (item['editComponent']) {
            case 'input': {
              return <el-input class="cell-editor" v-model={scope.row[key]}
                               on-blur={() => _this.clearCellEditor(scope.row, item, scope.row[key])}
                               on-focus={_this.editorFocus}/>;
            }
            case 'input_number': {
              return <el-input-number class="cell-editor" v-model={scope.row[key]} max={editRange[1]}
                                      min={editRange[0]} precision={precision} controls={false}
                                      on-blur={() => _this.clearCellEditor(scope.row, item, scope.row[key])}
                                      on-focus={_this.editorFocus}/>;
            }
            case 'select': {
              this.loadOptions(item);
              return <el-select class="cell-editor" v-model={scope.row[key]} filterable={true}
                                on-change={value => _this.clearCellEditor(scope.row, item, value)}
                                on-focus={_this.editorFocus}>
                {
                  (_this.options[item.key] || []).map(option => {
                    return <el-option key={option.key} value={option.key}
                                      label={option.label}/>
                  })
                }
              </el-select>;
            }
            case 'date': {
              return <el-date-picker class="cell-editor" v-model={this.date} type="date" style="width: 100%"
                                     on-blur={() => _this.clearCellEditor(scope.row, item, this.date)}
                                     value-format="yyyy-MM-dd"
                                     on-focus={_this.editorFocus}/>;
            }
            case 'datetime': {
              return <el-date-picker class="cell-editor" v-model={this.date} type="datetime" style="width: 100%"
                                     on-blur={() => _this.clearCellEditor(scope.row, item, this.date)}
                                     value-format="yyyy-MM-dd HH:mm:ss"
                                     on-focus={_this.editorFocus}/>;
            }
            case 'time': {
              this.date = this.date || scope.row[key];
              return <el-time-select class="cell-editor" v-model={this.date} prefix-icon={'s'}
                                     on-blur={() => _this.clearCellEditor(scope.row, item, this.date)}
                                     value-format="HH:mm" picker-options={{start: '00:00', step: '00:05', end: '23:55'}}
                                     on-focus={_this.editorFocus} style="width: 100%"/>;
            }
            default:
              console.error('unsupported editComponent [' + item['editComponent'] + ']');
              this.clearCellEditor(scope.row, item, null, true);
          }
        }
        item.property = key;
        return item.formatter(scope.row, item, scope.row[key]);
      },
      loadOptions(item) {
        if (item['editComponent'] === 'select' && item['editOptions']) {
          this.$set(this.options, item.key, item['editOptions']);
          this.$forceUpdate();
          return;
        }
        if (item.loaded || item['editComponent'] !== 'select' || this.options[item.source]) {
          return;
        }
        item.loaded = true;
        WebpageQueryService.webpage_query_filter_options({
          _code: item['editSource']
        }, res => {
          this.$set(this.options, item.key, res.content);
          this.$forceUpdate();
        });
      },
      editorFocus() {
        $('.cell-editor').parent().parent().addClass('td-editable');
      },
      clearEditor() {
        let _this = this;
        $('td').removeClass('td-editable');
        _this.$set(_this.row, 'cellEditable', {});
      },
      clearCellEditor(row, item, value, error) {
        if (error) {
          return;
        }
        if (item.editKey) {
          let params = {id: row.id, businessType: this.source.params.businessType};
          params[item.editKey] = value;
          params['criteria'] = this.source.params;
          WebpageDataService.webpage_data_edit(params,
              () => row[item.key] = value,
              () => this.date = ''
          );
        }
        row['cellEditable'][item.key] = false;
        $('.cell-editor').parent().parent().removeClass('td-editable');
        let option = _.find(this.options[item.key], o => o.key === value);
        this.$emit('cell-edit', row, item, value, option);
        this.$forceUpdate();
      },
      getFilteredData() {
        return this.filterMethod(_.filter(this.tableData, d => {
          return this.matchFilters(d);
        }));
      },
      matchFilters(data) {
        let matched = true;
        let filters = this.filter.trim().replace(/[ ]+/, ' ').split(' ');
        for (let filter in filters) {
          if (Object.prototype.hasOwnProperty.call(filters, filter)) {
            if (!this.matchFilter(data, filters[filter])) {
              matched = false;
              break;
            }
          }
        }
        return matched;
      },
      matchFilter(data, filter) {
        let filterColumns = _.map(_.filter(this.tableColumns, c => {
          return c.filterable;
        }), 'key');
        if (filterColumns.length === 0) {
          return true;
        }
        let matched = false;
        for (let column in filterColumns) {
          if (Object.prototype.hasOwnProperty.call(filterColumns, column)
              && (data[filterColumns[column]] + '').toLowerCase().includes(filter.toLowerCase())) {
            matched = true;
            break;
          }
        }
        return matched;
      },
      rowClick(row) {
        if (this.selectable(row)) {
          this.currentRow = row;
          this.$refs['table'].clearSelection();
          this.$refs['table'].toggleRowSelection(row, true);
          // this.$emit('row-click', row, column, e);
        }
      },
      cellClick(row, column, cell, e) {
        this.$emit('cell-click', row, column, cell, e);
      },
      cellDblClick(row, column) {
        let header = _.find(this.tableColumns, c => c.key === column.property);
        if (!header || !header.editable) {
          return;
        }
        this.row = row;
        let cellEditable = {};
        let property = column['property'];
        cellEditable[property] = true;
        this.$set(row, 'cellEditable', cellEditable);
        this.$nextTick(() => {
          let input = $('.cell-editor input');
          input.focus();
          input.on("keydown", function (event) {
            if (event.keyCode === 38 || event.keyCode === 40) {
              event.stopPropagation();
              event.preventDefault();
            }
          });
        })
      },
      headerClick(column, e) {
        this.$emit('header-click', column, e);
      },
      headerDragend(newWidth, oldWidth, column, e) {
        this.$emit('header-dragend', newWidth, oldWidth, column, e);
      },
      handleSizeChange(size) {
        let fetchPage = this.page;
        if (this.page > this.total / size) {
          fetchPage = Math.ceil(this.total / size);
        }
        this.page = fetchPage;
        this.size = size;
        this.getTable(fetchPage, this.size);
        this.$emit('clear-filter');
      },
      changePage(page) {
        if (this.page === page) {
          return;
        }
        this.page = page;
        this.getTable(this.page, this.size);
        this.$emit('clear-filter');
      },
      onRefresh() {
        this.$refs['table'].clearSelection();
        this.currentRow = '';
        if (this.source.request instanceof Function) {
          this.getTable();
        }
        this.$emit('refresh');
      },
      selectionChange(selection) {
        if (selection.length > 0) {
          this.$refs['table'].setCurrentRow(selection[selection.length - 1]);
          this.$emit('row-click', selection[selection.length - 1], selection);
        } else {
          this.$emit('row-click', {}, selection);
        }
        this.$emit('selection-change', selection, this.tableData);
      },
    },
  }

</script>
<style lang="scss">
  .zy-table {
    .el-table__body-wrapper {
      overflow-y: auto !important;
    }
  }

  .zy-table-div {
    .el-table td {
      padding: 4px 0
    }
    .line-break {
      .cell, .cell.el-tooltip {
        white-space: pre;
      }
    }
    span.el-dropdown {
      display: inline;
    }
    button {
      padding: 0;
      font-size: 12px;
    }
    .el-pagination__jump, .el-pagination__sizes {
      margin: 0;
    }
    slot > span {
      min-width: 28px !important;
    }
    .el-icon-refresh {
      color: #409EFF;
    }
    .common-pagination {
      .el-icon-refresh, .el-icon-menu {
        &:hover {
          background-color: #dddddd;
          border-radius: 3px;
        }
        padding: 5px;
        font-weight: 900;
        cursor: pointer;
        font-size: 1.1rem;
      }
    }
    .gutter {
      display: block !important;
    }
    .td-editable {
      padding: 0 !important;
      .cell {
        padding: 0 !important;
      }
      input {
        padding: 0 9px !important;
      }
    }
    .el-input-number--mini {
      width: 100%;
      input {
        text-align: left;
      }
    }
  }

  .el-table th > .cell {
    white-space: nowrap;
  }

</style>
