<script>
  import EditDialog from "./edit_dialog";
  import ColumnSetting from "./column_setting";
  import DetailTable from "./detail_table";
  import ZyCharts from "./charts";
  import ColumnSelect from "./column_select";
  import {WebpageDataService, WebpageQueryService} from "../../../assets/webpage";
  import store from "../../../vuex/store";

  export default {
    name: 'ZyGeneralPage',
    components: {ColumnSelect, ZyCharts, DetailTable, ColumnSetting, EditDialog},
    props: {
      requiredParams: {type: Array, default: () => []},
      requestParams: {type: Object, default: () => new Object()},
      showHeader: {type: Boolean, default: false},
      type: {type: String, required: false},
      autoLoad: {type: Boolean, default: true},
      selectable: {type: Boolean, default: true},
      defaultExpandSearch: {type: Boolean, default: false},
      pageSwitchLoading: {type: Boolean, default: true},
      query: {type: Boolean, default: false},
    },
    data() {
      return {
        businessType: '',
        dataKey: 'id',
        page: {},
        pageLoading: false,
        columnPersistShow: false,
        columnSelectShow: false,
        editShow: false,
        importShow: false,
        fileIds: [],
        selection: [],
        params: {},
        id: '',
        button: '',
        forms: {},
        form: {},
        detailTableParams: {},
        detailTableShow: false,
        detailTableTitle: '详情',
        tableColumns: [],
        echartsShow: false,
        echartsTitle: '',
        echartsOption: {},
        attachShow: false,
        attachParams: {businessType: '', businessId: '', editable: true},
        batchForm: {
          show: false, loading: false, filters: [], title: '', width: '', submit: () => {
          }
        },
        activeTab: 'default',
        tabs: [],
        selectedColumns: [],
      }
    },
    created() {
      this.businessType = this.type || this.$route.query.businessType;
      this.init();
    },
    mounted() {
    },
    provide() {
      return {
        pageData: this.getPageData
      }
    },
    render() {
      let searchForm = this.page.searchable ? this.page.forms['search'] : {};
      let scopedSlots = {};
      this.tableColumns.forEach(column => {
        if (column.custom && typeof this.$scopedSlots[column.key] === 'function') {
          scopedSlots[column.key] = (scope) => this.$scopedSlots[column.key]({row: scope.row});
        }
      });
      if (!this.businessType) {
        return <div/>;
      }
      if (this.pageLoading) {
        return <div id="zy-page" v-loading={this.pageLoading}/>
      }
      return <div id="zy-page">
        <el-container>
          <el-aside width={(this.columnPersistShow || this.columnSelectShow) ? '300px' : '0'}>
            {
              this.columnPersistShow ? (
                  <column-setting show={this.columnPersistShow} type={this.page.businessType}
                                  on-close={() => this.columnPersistShow = false} on-save={this.saveColumns}/>
              ) : null
            }
            {
              this.columnSelectShow ? (
                  <column-select button-code={this.button.code} on-close={() => this.columnSelectShow = false}
                                 on-change={this.columnSelectChange}/>
              ) : null
            }
          </el-aside>
          <el-main>
            {this.page.hasTab ? this.generateTabs(searchForm, scopedSlots) : this.generatePage(searchForm, scopedSlots)}
          </el-main>
          <edit-dialog show={this.editShow} button={this.button} forms={this.forms} id={this.id}
                       params={this.params} on-close={this.close} on-submit={this.submit}
                       business-type={this.businessType}
                       {...{
                         scopedSlots: {
                           after: () => (this.button.action === '_view' || this.button.action === '_edit') && this.$scopedSlots['after'] instanceof Function ?
                               this.$scopedSlots['after']({
                                 mode: this.button.action,
                                 row: this.selection[0]
                               }) : null
                         }
                       }}/>
          <zy-charts show={this.echartsShow} title={this.echartsTitle} option={this.echartsOption}
                     on-close={this.close}></zy-charts>
          <zy-dialog show={this.importShow} items={this.form.filters || []} title={this.button.name} width={'400px'}
                     on-close={this.close} on-submit={this.import}{...{
            scopedSlots: {
              after: () => this.importShow ? (
                  <zy-upload limit={9999} multiple={true} on-change={this.uploadChange}></zy-upload>
              ) : (this.fileIds = [])
            }
          }}>
          </zy-dialog>
          <zy-dialog show={this.batchForm.show} filters={this.batchForm.filters} title={this.batchForm.title}
                     loading={this.batchForm.loading} width={this.batchForm.width} on-close={this.close}
                     on-submit={this.batchForm.submit}></zy-dialog>
          <zy-file show={this.attachShow} title="附件" params={this.attachParams} on-close={this.close}></zy-file>
          <detail-table show={this.detailTableShow} title={this.detailTableTitle} params={this.detailTableParams}
                        on-close={this.close} button={this.button}/>
        </el-container>
      </div>
    },
    watch: {
      '$route.query.businessType': {
        handler: function (nv) {
          if (nv && !this.type) {
            this.businessType = nv;
            this.init();
          }
        }
      },
      'type': {
        handler: function (nv) {
          if (nv) {
            this.businessType = nv;
            this.init();
          }
        }
      },
      activeTab: {
        handler: function () {
          this.$nextTick(() => window.onresize())
        }
      },
    },
    computed: {},
    methods: {
      init() {
        if (!this.businessType) {
          return;
        }
        this.pageLoading = this.pageSwitchLoading;
        WebpageQueryService.webpage_query_page_generate(_.merge({
          businessType: this.businessType
        }, this.requestParams), res => {
          this.pageLoading = false;
          this.page = res.content;
          this.dataKey = 'id';
          this.forms = _.cloneDeep(this.page.forms);
          this.generateFormValidator();
          this.$nextTick(() => {
            this.autoLoad && this.loadData();
          })
        })
      },
      generatePage(searchForm, scopedSlots) {
        let headerSlot = () => this.page.title;
        if (typeof this.$scopedSlots.header === 'function') {
          headerSlot = this.$scopedSlots.header;
        }
        return <zy-page ref="page" sel-mode={this.selectable && this.page.selectable} filterable={this.page.filterable}
                        searchable={!!this.page.searchable} show-header={this.showHeader}
                        default-expand-search={this.defaultExpandSearch} pagination={this.page.paginated}
                        request-params={_.merge({
                          businessType: this.businessType,
                          columns: this.selectedColumns
                        }, this.requestParams)}
                        search-filter={this.page.searchFilter} on-refresh={this.refresh}
                        required-params={_.union(this.page.searchFilter && this.page.searchFilter.required ? [this.page.searchFilter.key] : [], this.requiredParams)}
                        request={WebpageDataService.webpage_data_table} auto-load={false} label-width={searchForm.labelWidth || undefined}
                        on-generate-slots={(columns) => this.tableColumns = columns} search-filters={searchForm.filters || []}
                        buttons={this.generateButtons(this.page.buttons)} on-header-dragend={this.headerDragend}
                        on-row-click={this.rowClick} on-selection-change={this.selectionChange}
                        {...{
                          scopedSlots: _.merge({
                            after: this.generateInlineButtons,
                            header: headerSlot
                          }, scopedSlots),
                        }}/>
      },
      loadData() {
        this.$refs['page'].loadData();
      },
      refresh() {
        this.$emit('refresh');
      },
      generateTabs(searchForm, scopedSlots) {
        return <el-tabs v-model={this.activeTab} on-tab-remove={this.tabRemove}>
          <el-tab-pane label={this.page.title}
                       name="default">{this.generatePage(searchForm, scopedSlots)}</el-tab-pane>
          {
            this.tabs.map(tab =>
                <el-tab-pane label={tab.label} name={tab.key} key={tab.key} closable>
                  {this.activeTab === tab.key ? this.generateTabContent(tab) : null}
                </el-tab-pane>
            )
          }
        </el-tabs>
      },
      generateTabContent(tab) {
        if (tab.type === 'page') {
          return <zy-zy-page type={tab.source} request-params={tab.requestParams}
                             required-params={['parentIds']}/>
        }
        if (tab.type === 'layout') {
          return <zy-general-layout type={tab.source} request-params={tab.requestParams}
                                    required-params={['parentIds']}></zy-general-layout>
        }
      },
      tabRemove(tab) {
        let index = _.findIndex(this.tabs, (item) => {
          return item.key === tab;
        });
        this.tabs.splice(index, 1);
        this.activeTab = 'default';
      },
      getPageData() {
        let searchFilterParams = {};
        if (this.page.searchFilter) {
          searchFilterParams[this.page.searchFilter.key] = this.$refs['page'].params[this.page.searchFilter.key];
        }
        return {
          parentParam: this.requestParams,
          selection: this.selection,
          params: this.$refs['page'] ? this.$refs['page'].params : {},
          button: this.button,
          searchFilterParams: searchFilterParams
        }
      },
      generateFormValidator() {
        for (let key in this.page.forms) {
          if (Object.prototype.hasOwnProperty.call(this.page.forms, key)) {
            this.generateCascaderValidator(key);
          }
        }
      },
      generateCascaderValidator(key) {
        this.page.forms[key].filters.forEach(filter =>
            (filter.type === 'cascader') && (filter.validator = (rule, value, callback) => {
              if (!!filter.required && value !== undefined && (!value || value.length === 0)) {
                callback(new Error('请选择' + filter.label));
              } else {
                callback();
              }
            })
        )
      },
      generateButtons(buttons) {
        if (this.query) {
          return [];
        }
        if (buttons instanceof Array) {
          buttons.forEach(button => {
            if (button.buttonType === 'dropdown') {
              button.items = this.generateButtons(button.items);
            } else {
              // button.icon = store.state.provider.icon[button.action.replace(/^_/, '').replace(/_.*/, '')];
            }
            this.buildDisabledAttrAndClickEvent(button);
          })
        }
        return buttons;
      },
      buildDisabledAttrAndClickEvent(button) {
        let _this = this;
        button.disabled = (selection, params) => {
          let checkDisabled;
          switch (button.selectionMode) {
            case 'single':
              checkDisabled = (selection) => selection instanceof Array && selection.length !== 1;
              break;
            case 'multi':
              checkDisabled = (selection) => selection instanceof Array && selection.length === 0;
              break;
            default:
              checkDisabled = new Function('return false;');
          }
          return checkDisabled(selection) || new Function('s', 'p', button['disabledScript'])(selection, params);
        };
        if (button.buttonType !== 'dropdown' && !(_this[button.action] instanceof Function)) {
          button.click = (selection, params) => _this.checkClickable(button) && _this.$emit(button.action, selection, params, button);
        } else {
          button.click = (selection, params) => _this.checkClickable(button) && _this[button.action](selection, params, button);
        }
      },
      checkClickable() {
        if (this.$refs['page'].$refs['commonTable'].tableColumns.length === 0) {
          this.$message.warning("列表未加载");
          return false;
        } else {
          return true;
        }
      },
      generateInlineButtons() {
        let inlineButtons = this.page.inlineButtons || [];
        if (inlineButtons.length === 0) {
          return null;
        }
        let width = 25;
        inlineButtons.forEach(inlineButton => {
          width += inlineButton.name.length * 20;
        });
        return <el-table-column {...{
          props: {
            label: '操作', columnKey: 'actions', headerAlign: 'center',
            align: 'center', minWidth: width, fixed: 'right'
          },
          scopedSlots: {
            default: scope => {
              return inlineButtons.map(inlineButton => {
                if (inlineButton.buttonType === 'popover') {
                  return <zy-popover type="text" name={inlineButton.name}
                                     disabled={new Function('s', 'p', inlineButton['disabledScript'])([scope.row])}
                                     on-submit={() => {
                                       if (this[inlineButton.action] instanceof Function) {
                                         this[inlineButton.action]([scope.row], {businessType: this.businessType}, inlineButton);
                                       } else {
                                         this.checkClickable(inlineButton) && this.$emit(inlineButton.action, [scope.row], {businessType: this.businessType}, inlineButton)
                                       }
                                     }}/>
                }
                return <el-button type='text' style="padding: 0 5px;"
                                  disabled={new Function('s', 'p', inlineButton['disabledScript'])([scope.row])}
                                  on-click={() => {
                                    if (this[inlineButton.action] instanceof Function) {
                                      this[inlineButton.action]([scope.row], {businessType: this.businessType}, inlineButton);
                                    } else {
                                      this.checkClickable(inlineButton) && this.$emit(inlineButton.action, [scope.row], {businessType: this.businessType}, inlineButton)
                                    }
                                  }}>
                  {inlineButton.name}
                </el-button>
              })
            }
          }
        }}/>
      },
      saveColumns(data) {
        WebpageConfigService.webpage_config_column__save_sort(data, () => {
          this.$refs['page'].loadData(1);
        })
      },
      columnSelectChange(columns) {
        this.selectedColumns = columns;
        this.$nextTick(() => this.$refs['page'].loadData(1));
      },
      headerDragend(newWidth, oldWidth, column) {
        if (this.page['headerBusinessType']) {
          WebpageConfigService.webpage_config_column_save_width({
            id: column.columnKey,
            width: Math.round(newWidth)
          })
        }
      },
      selectionChange(selection) {
        this.selection = selection;
        this.$emit('selection-change', selection);
      },
      rowClick(row, selection) {
        this.$emit('row-click', row, selection);
      },
      close() {
        this.editShow = false;
        this.importShow = false;
        this.detailTableShow = false;
        this.echartsShow = false;
        this.attachShow = false;
        this.batchForm = {
          show: false, loading: false, filters: [], title: '', width: '', submit: () => {
          }
        }
      },
      _sub_tab_page(selection, params, button) {
        this.addSubTab(selection, params, button, 'page');
      },
      _sub_tab_layout(selection, params, button) {
        this.addSubTab(selection, params, button, 'layout');
      },
      addSubTab(selection, params, button, type) {
        this.saveButtonParams(selection, params, button);
        let tab = _.find(this.tabs, (item) => item.key === button.key);
        if (tab) {
          tab.requestParams = _.merge({parentIds: _.map(selection, this.dataKey)}, button.sourceParams);
        } else {
          this.tabs.push({
            key: button.key + '', label: button.name, source: button.source, type: type,
            requestParams: _.merge({parentIds: _.map(selection, this.dataKey)}, button.sourceParams)
          });
        }
        this.$nextTick(() => this.activeTab = button.key + '');
      },
      _column_persist(selection, params, button) {
        this.columnPersistShow = true;
      },
      _column_select(selection, params, button) {
        this.button = button;
        this.columnSelectShow = true;
      },
      _create(selection, params, button) {
        this.saveButtonParams(selection, params, button);
        this.editShow = true;
        this.id = '';
      },
      _edit(selection, params, button) {
        this.saveButtonParams(selection, params, button);
        if (selection.length !== 1) {
          return this.$message.warning('请选择一条数据');
        }
        this.editShow = true;
        this.id = selection[0][this.dataKey];
      },
      _view(selection, params, button) {
        this._edit(selection, params, button);
      },
      submit(jsonData) {
        let _this = this;
        if (_this.button.action === '_edit') {
          jsonData[_this.dataKey] = _this.selection[0][this.dataKey];
        }
        jsonData['buttonCode'] = _this.button.code;
        jsonData['businessType'] = _this.businessType;
        jsonData['criteria'] = _this.params;
        let loading = _this.$globalLoading();
        WebpageDataService.webpage_data_save(_.merge(jsonData, _this.requestParams), () => {
          _this.close();
          _this.$message.success('保存成功');
          _this.button.reload && this.loadData();
          loading.close();
        }, () => loading.close())
      },
      _delete(selection, params, button) {
        WebpageDataService.webpage_data_delete({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey)
        }, () => {
          button.reload && this.loadData();
          this.$message.success(button.name + '成功');
        })
      },
      _detail_form(selection, params, button) {
        console.log(selection, params, button)
      },
      _detail(selection, params, button) {
        this.$emit('_detail', selection, params, button)
      },
      _detail_table(selection, params, button) {
        this.saveButtonParams(selection, params, button);
        this.detailTableParams = {
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params
        };
        this.detailTableShow = true;
      },
      _selection(selection, params, button) {
        this.$global.selection({
          source: button.source,
          single: button.sourceSingleSelect,
          callback: (datas) => {
            WebpageDataService.webpage_data_batch_process(_.merge({
              buttonCode: button.code,
              businessType: this.businessType,
              ids: _.map(selection, this.dataKey),
              criteria: params
            }, _.merge({selectionIds: _.map(datas, "id")}, this.requestParams)), () => {
              button.reload && this.loadData();
            })
          }
        })
      },
      _batch(selection, params, button) {
        this.fillForms(selection, params, button, this.batch)
      },
      _batch_stream(selection, params, button) {
        this.fillForms(selection, params, button, this.batch_stream)
      },
      _inline(selection, params, button) {
        this.fillForms(selection, params, button, this.batch)
      },
      batch(selection, params, button, extra, close) {
        let loading = this.$globalLoading();
        WebpageDataService.webpage_data_batch_process(_.merge({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params
        }, extra || {}), () => {
          button.reload && this.loadData();
          close();
          loading.close();
        }, () => loading.close())
      },
      batch_stream(selection, params, button, extra, close) {
        WebpageDataService.webpage_data_batch_stream(_.merge({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params
        }, extra || {}), () => {
          button.reload && this.loadData();
          close();
        })
      },
      _download(selection, params, button) {
        this.fillForms(selection, params, button, this.download)
      },
      download(selection, params, button, extra, close) {
        let loading = this.$globalLoading();
        WebpageDataService.webpage_data_download(_.merge({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params
        }, extra || {}), (res) => {
          this.doDownload(res.content);
          button.reload && this.loadData();
          close();
          loading.close();
        }, () => loading.close())
      },
      doDownload(id) {
        let url = store.state.basePath + `/file/transfer/download?id=` + encodeURIComponent(id);
        let frame = document.getElementById("downloadFrame");
        if (!frame) {
          frame = document.createElement('iframe');
          frame.id = 'downloadFrame';
          frame.style.display = "none";
          document.body.appendChild(frame);
        }
        frame.src = url;
      },
      _import(selection, params, button) {
        this.saveButtonParams(selection, params, button);
        this.importShow = true;
        if (button.forms instanceof Array && button.forms.length !== 0) {
          this.form = this.page.forms[button.forms[0]];
        } else {
          this.form = {};
        }
      },
      uploadChange(fileIds) {
        this.fileIds = fileIds;
      },
      import(values) {
        values.fileIds = this.fileIds;
        if (values.fileIds.length === 0) {
          return this.$message.warning('请上传文件');
        }
        WebpageDataService.webpage_data_import(_.merge({
          buttonCode: this.button.code,
          businessType: this.businessType,
          criteria: this.params,
          ids: _.map(this.selection, this.dataKey)
        }, values), () => {
          this.close();
          this.button.reload && this.loadData();
        })
      },
      _attach(selection, params, button) {
        this.attachParams.businessId = selection[0][this.dataKey];
        this.attachParams.businessType = params.businessType;
        this.attachShow = true;
      },
      _attach_query(selection, params, button) {
        this._attach(selection, params, button);
        this.attachParams.editable = false;
      },
      _export(selection, params, button) {
        this.fillForms(selection, params, button, this.export_sync);
      },
      _export_sync(selection, params, button) {
        this.fillForms(selection, params, button, this.export_sync);
      },
      export_sync(selection, params, button, extra, close) {
        WebpageDataService.webpage_data_export_sync(_.merge({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params,
        }, extra || {}), () => {
          this.close();
          close();
        })
      },
      _template(selection, params, button) {
        WebpageDataService.webpage_data_template({
          buttonCode: button.code,
          businessType: this.businessType,
        })
      },
      _print(selection, params, button) {
        this.fillForms(selection, params, button, this.print);
      },
      _preview(selection, params, button) {
        WebpageDataService.webpage_data_preview({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params
        })
      },
      print(selection, params, button, extra, close) {
        close();
        this.$global.print({
          title: button.name + '预览',
          type: 'html',
          loader: callback => WebpageDataService.webpage_data_print_html(_.merge({
            buttonCode: button.code,
            businessType: this.businessType,
            ids: _.map(selection, this.dataKey),
            criteria: params
          }, extra || {}), callback)
        })
      },
      _print_tables(selection, params, button) {
        this.fillForms(selection, params, button, this.print_tables);
      },
      print_tables(selection, params, button, extra, close) {
        close();
        this.$global.print({
          title: button.name + '预览',
          type: 'table',
          customFootnote: this.$scopedSlots.customFootnote,
          loader: callback => WebpageDataService.webpage_data_print_table(_.merge({
            buttonCode: button.code,
            businessType: this.businessType,
            ids: _.map(selection, this.dataKey),
            criteria: params,
          }, extra || {}), callback)
        })
      },
      _chart(selection, params, button) {
        this.echartsShow = true;
        this.echartsTitle = button.name;
        WebpageDataService.webpage_data_batch_process({
          buttonCode: button.code,
          businessType: this.businessType,
          ids: _.map(selection, this.dataKey),
          criteria: params,
        }, res => this.echartsOption = res.content)
      },
      fillForms(selection, params, button, callback) {
        let _this = this;
        if (button.forms instanceof Array && button.forms.length !== 0) {
          let filters = _this.page.forms[button.forms[0]].filters;
          filters.forEach(item => item.onChange = (value, setData, setRule, formData, clearValidate) =>
              this.$emit('filter-change', item.key, value, setData, setRule, formData, clearValidate));
          this.batchForm = {
            show: true,
            loading: true,
            title: button.name,
            width: button['dialogWidth'],
            labelWidth: _this.page.forms[button.forms[0]].labelWidth > 0 ? _this.page.forms[button.forms[0]].labelWidth + 'px' : undefined,
            filters: filters,
            submit: jsonData => {
              callback(selection, params, button, _.merge({businessType: _this.businessType}, jsonData), this.close);
            }
          };
          WebpageDataService.webpage_data_batch_load({
            buttonCode: button.code,
            businessType: this.businessType,
            ids: _.map(selection, this.dataKey),
            criteria: params,
          }, res => {
            filters.forEach(item => {
              item.defaultValue = res.content[item.key]
            });
            this.batchForm.loading = false;
          });
        } else {
          callback(selection, params, button, {}, () => {
          });
        }
      },
      saveButtonParams(selection, params, button) {
        this.selection = selection;
        this.params = params;
        this.button = button;
      }
    },
  }
</script>
<style scoped lang="scss">
  #zy-page {
    padding: 0;
    height: 100%;
    background-color: #fff;
    ::v-deep div.cell .el-dropdown {
      border: none;
    }
    #downloadFrame {
      display: none;
      width: 0;
      border: 0;
    }
  }
</style>
