<template>
  <div class="config-list">
    <!--tour-->
    <v-tour
      name="spider-detail-config"
      :steps="tourSteps"
      :callbacks="tourCallbacks"
      :options="$utils.tour.getOptions(true)"
    />
    <!--./tour-->

    <!--preview results-->
    <el-dialog
      :visible.sync="dialogVisible"
      :title="$t('Preview Results')"
      width="90%"
      :before-close="onDialogClose"
    >
      <el-table class="table-header" :data="[{}]" :show-header="false">
        <el-table-column
          v-for="(f, index) in fields"
          :key="f.name + '-' + index"
          min-width="100px"
        >
          <template>
            <el-input v-model="columnsDict[f.name]" size="mini" style="width: calc(100% - 15px)" />
            <a href="javascript:" style="margin-left: 2px;" @click="onDeleteField(index)">X</a>
            <!--<el-button size="mini" type="danger" icon="el-icon-delete" style="width:45px;margin-left:2px"></el-button>-->
          </template>
        </el-table-column>
      </el-table>
      <el-table
        :data="previewCrawlData"
        :show-header="false"
        border
      >
        <el-table-column
          v-for="(f, index) in fields"
          :key="f.name + '-' + index"
          :label="f.name"
          min-width="100px"
        >

          <template slot-scope="scope">
            {{ getDisplayStr(scope.row[f.name]) }}
          </template>
        </el-table-column>
      </el-table>
    </el-dialog>
    <!--./preview results-->

    <!--crawl confirm dialog-->
    <crawl-confirm-dialog
      :visible="crawlConfirmDialogVisible"
      :spider-id="spiderForm._id"
      @close="crawlConfirmDialogVisible = false"
    />
    <!--./crawl confirm dialog-->

    <!--tabs-->
    <el-tabs :active-name="activeTab" @tab-click="onTabClick">
      <!--Stages-->
      <el-tab-pane name="stages" :label="$t('Stages')">
        <!--config detail-->
        <el-row>
          <el-form ref="form" label-width="150px" :model="spiderForm.config" />
        </el-row>
        <!--./config detail-->

        <el-row>
          <div class="top-wrapper">
            <ul class="list">
              <li class="item">
                <label>{{ $t('Start URL') }}: </label>
                <el-input
                  id="start-url"
                  v-model="spiderForm.config.start_url"
                  :placeholder="$t('Start URL')"
                  :class="startUrlClass"
                />
              </li>
              <li class="item">
                <label>{{ $t('Start Stage') }}: </label>
                <el-select
                  id="start-stage"
                  v-model="spiderForm.config.start_stage"
                  :placeholder="$t('Start Stage')"
                  :class="startStageClass"
                  @change="$st.sendEv('爬虫详情', '配置', '改变起始阶段')"
                >
                  <el-option
                    v-for="n in spiderForm.config.stages.map(s => s.name)"
                    :key="n"
                    :value="n"
                    :label="n"
                  />
                </el-select>
              </li>
              <li class="item">
                <label>{{ $t('Engine') }}: </label>
                <el-select
                  v-model="spiderForm.config.engine"
                  :placeholder="$t('Start Stage')"
                  :class="startStageClass"
                  disabled
                >
                  <el-option
                    v-for="n in ['scrapy']"
                    :key="n"
                    :value="n"
                    :label="n"
                  />
                </el-select>
              </li>
              <li class="item">
                <label>{{ $t('Selector Type') }}: </label>
                <div class="selector-type">
                  <span class="selector-type-item" @click="onClickSelectorType('css')">
                    <el-tag
                      :class="isCss ? 'active' : 'inactive'"
                      type="success"
                    >
                      CSS
                    </el-tag>
                  </span>
                  <span class="selector-type-item" @click="onClickSelectorType('xpath')">
                    <el-tag
                      :class="isXpath ? 'active' : 'inactive'"
                      type="primary"
                    >
                      XPath
                    </el-tag>
                  </span>
                </div>
              </li>
            </ul>
          </div>

          <div class="button-group-container">
            <div class="button-group">
              <el-button
                id="btn-run"
                size="small"
                type="danger"
                :disabled="isDisabled"
                icon="el-icon-video-play"
                @click="onCrawl"
              >
                {{ $t('Run') }}
              </el-button>
              <el-button
                id="btn-convert"
                size="small"
                type="warning"
                :disabled="isDisabled"
                icon="el-icon-refresh-right"
                @click="onConvert"
              >
                {{ $t('Convert to Customized') }}
              </el-button>
              <!--              <el-button type="primary" @click="onExtractFields" v-loading="extractFieldsLoading">-->
              <!--                {{$t('ExtractFields')}}-->
              <!--              </el-button>-->
              <!--              <el-button type="warning" @click="onPreview" v-loading="previewLoading">{{$t('Preview')}}</el-button>-->
              <el-button
                id="btn-save"
                size="small"
                type="success"
                :disabled="saveLoading || isDisabled"
                :icon="saveLoading ? 'el-icon-loading' : 'el-icon-check'"
                @click="onSave"
              >
                {{ $t('Save') }}
              </el-button>
            </div>
          </div>
        </el-row>

        <el-collapse
          :value="activeNames"
        >
          <el-collapse-item
            v-for="(stage, index) in spiderForm.config.stages"
            :key="index"
            :name="stage.name"
          >
            <template slot="title">
              <ul class="stage-list">
                <!--actions-->
                <li
                  class="stage-item actions"
                  style="min-width: 80px; flex-basis: 80px; justify-content: flex-end"
                  @click="$event.stopPropagation()"
                >
                  <i class="action-item el-icon-copy-document" @click="onCopyStage(stage)" />
                  <i class="action-item el-icon-remove-outline" @click="onRemoveStage(stage)" />
                  <i class="action-item el-icon-circle-plus-outline" @click="onAddStage(stage)" />
                </li>
                <!--./actions-->

                <!--stage name-->
                <li class="stage-item" style="min-width: 240px" @click="$event.stopPropagation()">
                  <label>{{ $t('Stage Name') }}: </label>
                  <div v-if="!stage.isEdit" class="text-wrapper" @click="onEditStage(stage)">
                    <span class="text">
                      {{ stage.name }}
                    </span>
                    <i class="el-icon-edit-outline" />
                  </div>
                  <el-input
                    v-else
                    :ref="`stage-name-${stage.name}`"
                    v-model="stage.name"
                    class="edit-text"
                    :placeholder="$t('Stage Name')"
                    @focus="onStageNameFocus($event)"
                    @blur="stage.isEdit = false"
                  />
                </li>
                <!--./stage name-->

                <!--list-->
                <li class="stage-item" style="min-width: 240px">
                  <label>{{ $t('List') }}: </label>
                  <el-checkbox
                    style="text-align: left; flex-basis: 20px; margin-right: 5px"
                    :value="isList(stage)"
                    @change="onCheckIsList($event, stage)"
                  />
                  <el-popover v-if="isList(stage)" v-model="stage.isListOpen" placement="top" width="360">
                    <el-form label-width="120px">
                      <el-form-item :label="$t('Selector Type')">
                        <el-tag
                          :class="!stage.list_xpath ? 'active' : 'inactive'"
                          type="success"
                          @click="onSelectStageListType(stage, 'css')"
                        >CSS
                        </el-tag>
                        <el-tag
                          :class="stage.list_xpath ? 'active' : 'inactive'"
                          type="primary"
                          @click="onSelectStageListType(stage, 'xpath')"
                        >XPath
                        </el-tag>
                      </el-form-item>
                      <el-form-item :label="$t('Selector')" class="list-selector">
                        <el-input v-if="!stage.list_xpath" v-model="stage.list_css" />
                        <el-input v-else v-model="stage.list_xpath" />
                      </el-form-item>
                    </el-form>
                    <el-tag
                      v-if="!stage.list_xpath"
                      slot="reference"
                      type="success"
                      @click="onClickStageList($event, stage, 'css')"
                    >
                      <i v-if="!stage.isListOpen" class="el-icon-circle-plus-outline" />
                      <i v-else class="el-icon-remove-outline" />
                      CSS
                    </el-tag>
                    <el-tag
                      v-else
                      slot="reference"
                      type="primary"
                      @click="onClickStageList($event, stage, 'xpath')"
                    >
                      <i v-if="!stage.isListOpen" class="el-icon-circle-plus-outline" />
                      <i v-else class="el-icon-remove-outline" />
                      XPath
                    </el-tag>
                  </el-popover>
                </li>
                <!--./list-->

                <!--pagination-->
                <li class="stage-item" style="min-width: 240px">
                  <label>{{ $t('Pagination') }}: </label>
                  <el-checkbox
                    style="text-align: left; flex-basis: 20px; margin-right: 5px"
                    :value="isPage(stage)"
                    :disabled="!isList(stage)"
                    @change="onCheckIsPage($event, stage)"
                  />
                  <el-popover v-if="isPage(stage)" v-model="stage.isPageOpen" placement="top" width="360">
                    <el-form label-width="120px">
                      <el-form-item :label="$t('Selector Type')">
                        <el-tag
                          :class="!stage.page_xpath ? 'active' : 'inactive'"
                          type="success"
                          @click="onSelectStagePageType(stage, 'css')"
                        >CSS
                        </el-tag>
                        <el-tag
                          :class="stage.page_xpath ? 'active' : 'inactive'"
                          type="primary"
                          @click="onSelectStagePageType(stage, 'xpath')"
                        >XPath
                        </el-tag>
                      </el-form-item>
                      <el-form-item :label="$t('Selector')" class="page-selector">
                        <el-input v-if="!stage.page_xpath" v-model="stage.page_css" />
                        <el-input v-else v-model="stage.page_xpath" />
                      </el-form-item>
                    </el-form>
                    <el-tag
                      v-if="!stage.page_xpath"
                      slot="reference"
                      type="success"
                      @click="onClickStagePage($event, stage, 'css')"
                    >
                      <i v-if="!stage.isPageOpen" class="el-icon-circle-plus-outline" />
                      <i v-else class="el-icon-remove-outline" />
                      CSS
                    </el-tag>
                    <el-tag
                      v-else
                      slot="reference"
                      type="primary"
                      @click="onClickStagePage($event, stage, 'xpath')"
                    >
                      <i v-if="!stage.isPageOpen" class="el-icon-circle-plus-outline" />
                      <i v-else class="el-icon-remove-outline" />
                      XPath
                    </el-tag>
                  </el-popover>
                </li>
                <!--./pagination-->

              </ul>
            </template>
            <fields-table-view
              type="list"
              title="List Page Fields"
              :fields="stage.fields"
              :stage="stage"
              :stage-names="spiderForm.config.stages.map(s => s.name)"
            />
          </el-collapse-item>
        </el-collapse>
      </el-tab-pane>
      <!--./Stages-->

      <!--Graph-->
      <el-tab-pane name="process" :label="$t('Process')">
        <div id="process-chart" />
      </el-tab-pane>
      <!--./Graph-->

      <!--Setting-->
      <el-tab-pane name="settings" :label="$t('Settings')">
        <div class="actions" style="text-align: right;margin-bottom: 10px">
          <el-button type="success" size="small" :disabled="isDisabled" @click="onSave">
            {{ $t('Save') }}
          </el-button>
        </div>
        <setting-fields-table-view
          type="list"
        />
      </el-tab-pane>
      <!--./Setting-->

      <!--Spiderfile-->
      <el-tab-pane name="spiderfile" label="Spiderfile">
        <div class="spiderfile-actions">
          <el-button
            type="primary"
            size="small"
            style="margin-right: 10px;"
            :disabled="isDisabled"
            @click="onSpiderfileSave"
          >
            <font-awesome-icon :icon="['fa', 'save']" />
            {{ $t('Save') }}
          </el-button>
        </div>
        <file-detail />
      </el-tab-pane>
      <!--./Spiderfile-->
    </el-tabs>
    <!--./tabs-->
  </div>
</template>

<script>
  import {
    mapState,
    mapGetters
  } from 'vuex'
  import echarts from 'echarts'
  import FieldsTableView from '../TableView/FieldsTableView'
  import CrawlConfirmDialog from '../Common/CrawlConfirmDialog'

  import 'codemirror/lib/codemirror.js'
  import 'codemirror/mode/yaml/yaml.js'
  import FileDetail from '../File/FileDetail'
  import SettingFieldsTableView from '../TableView/SettingFieldsTableView'

  export default {
    name: 'ConfigList',
    components: {
      SettingFieldsTableView,
      FileDetail,
      CrawlConfirmDialog,
      FieldsTableView
    },
    data() {
      return {
        crawlTypeList: [
          { value: 'list', label: 'List Only' },
          { value: 'detail', label: 'Detail Only' },
          { value: 'list-detail', label: 'List + Detail' }
        ],
        extractFieldsLoading: false,
        previewLoading: false,
        saveLoading: false,
        dialogVisible: false,
        crawlConfirmDialogVisible: false,
        columnsDict: {},
        fieldColumns: [
          { name: 'name', label: 'Name' },
          { name: 'selector_type', label: 'Selector Type' },
          { name: 'selector', label: 'Selector' },
          { name: 'is_attr', label: 'Is Attribute' },
          { name: 'attr', label: 'Attribute' },
          { name: 'next_stage', label: 'Next Stage' }
        ],
        activeTab: 'stages',
        processChart: undefined,
        fileOptions: {
          mode: 'text/x-yaml',
          theme: 'darcula',
          styleActiveLine: true,
          lineNumbers: true,
          line: true,
          matchBrackets: true
        },
        tourSteps: [
          // stage
          {
            target: '.config-list .el-tabs__nav.is-top',
            content: this.$t('You can switch to each section of configurable spider.')
          },
          {
            target: '#start-url',
            content: this.$t('Here is the starting URL of the spider.')
          },
          {
            target: '#start-stage',
            content: this.$t('Here is the starting stage of the spider.<br><br>A <strong>Stage</strong> is basically a callback in the Scrapy spider.')
          },
          {
            target: '#btn-run',
            content: this.$t('You can run a spider task.<br><br>Spider will be automatically saved when clicking on this button.')
          },
          {
            target: '.stage-item.actions',
            content: this.$t('Add/duplicate/delete a stage.'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '.fields-table-view td.action',
            content: this.$t('Add/duplicate/delete an extract field in the stage.'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '.stage-item:nth-child(3)',
            content: this.$t('You can decide whether this is a list page.<br><br>Click on the CSS/XPath tag to enter the selector expression for list items.<br>For example, "<code>ul > li</code>"'),
            params: {
              placement: 'top'
            }
          },
          {
            target: '.stage-item:nth-child(4)',
            content: this.$t('You can decide whether this is a list page with pagination.<br><br>Click on the CSS/XPath tag to enter the selector expression for the pagination.<br>For example, "<code>a.next</code>"'),
            params: {
              placement: 'top'
            }
          },
          {
            target: '.fields-table-view',
            content: this.$t('You should enter necessary information for all fields in the stage.'),
            params: {
              placement: 'top'
            }
          },
          {
            target: '.fields-table-view tr:nth-child(1) td:nth-child(7)',
            content: this.$t('If you have multiple stages, e.g. list page + detail page, you should select the next stage in the detail link\'s field.'),
            params: {
              placement: 'top'
            }
          },
          // process
          {
            target: '#tab-process',
            content: this.$t('You can view the<br> visualization of the stage<br> workflow.')
          },
          // settings
          {
            target: '#tab-settings',
            content: this.$t('You can add the settings here, which will be loaded in the Scrapy\'s <code>settings.py</code> file.<br><br>JSON and Array data are supported.')
          },
          // Spiderfile
          {
            target: '#tab-spiderfile',
            content: this.$t('You can edit the <code>Spiderfile</code> here.<br><br>For more information, please refer to the <a href="https://docs.crawlab.cn/Usage/Spider/ConfigurableSpider.html" target="_blank" style="color: #409EFF">Documentation (Chinese)</a>.')
          }
        ],
        tourCallbacks: {
          onStop: () => {
            this.$utils.tour.finishTour('spider-detail-config')
          },
          onPreviousStep: (currentStep) => {
            if (currentStep === 10) {
              this.activeTab = 'stages'
            } else if (currentStep === 11) {
              this.activeTab = 'process'
            } else if (currentStep === 12) {
              this.activeTab = 'settings'
            }
            this.$utils.tour.prevStep('spider-detail-confg', currentStep)
          },
          onNextStep: (currentStep) => {
            if (currentStep === 9) {
              this.activeTab = 'process'
            } else if (currentStep === 10) {
              this.activeTab = 'settings'
            } else if (currentStep === 11) {
              this.activeTab = 'spiderfile'
            }
            this.$utils.tour.nextStep('spider-detail-config', currentStep)
          }
        }
      }
    },
    computed: {
      ...mapState('spider', [
        'spiderForm',
        'previewCrawlData'
      ]),
      ...mapGetters('user', [
        'userInfo'
      ]),
      isDisabled() {
        return this.spiderForm.is_public && this.spiderForm.username !== this.userInfo.username && this.userInfo.role !== 'admin'
      },
      fields() {
        if (this.spiderForm.crawl_type === 'list') {
          return this.spiderForm.fields
        } else if (this.spiderForm.crawl_type === 'detail') {
          return this.spiderForm.detail_fields
        } else if (this.spiderForm.crawl_type === 'list-detail') {
          return this.spiderForm.fields.concat(this.spiderForm.detail_fields)
        } else {
          return []
        }
      },
      isCss() {
        let i = 0
        this.spiderForm.config.stages.forEach(stage => {
          stage.fields.forEach(field => {
            if (!field.css) i++
          })
        })
        return i === 0
      },
      isXpath() {
        let i = 0
        this.spiderForm.config.stages.forEach(stage => {
          stage.fields.forEach(field => {
            if (!field.xpath) i++
          })
        })
        return i === 0
      },
      activeNames() {
        return this.spiderForm.config.stages.map(d => d.name)
      },
      startUrlClass() {
        if (!this.spiderForm.config.start_url) {
          return 'invalid'
        } else if (!this.spiderForm.config.start_url.match(/^https?:\/\/.+|^\/\/.+/i)) {
          return 'invalid'
        }

        return ''
      },
      startStageClass() {
        if (!this.spiderForm.config.start_stage) {
          return 'invalid'
        } else if (!this.activeNames.includes(this.spiderForm.config.start_stage)) {
          return 'invalid'
        }
        return ''
      },
      stageNodes() {
        const startStage = this.spiderForm.config.stages[this.spiderForm.config.start_stage]
        const nodes = []
        const allStageNames = new Set()

        let i = 0
        let currentStage = startStage
        while (currentStage) {
          // 加入节点信息
          nodes.push({
            x: i++,
            y: 0,
            itemStyle: {
              color: '#409EFF'
            },
            ...currentStage
          })

          // 记录该节点
          allStageNames.add(currentStage.name)

          // 设置当前阶段为下一阶段
          currentStage = this.getNextStage(currentStage)

          if (currentStage && allStageNames.has(currentStage.name)) {
            currentStage = undefined
          }
        }

        // 加入剩余阶段
        i = 0
        const restStages = this.spiderForm.config.stages
          .filter(stage => !allStageNames.has(stage.name))
        restStages.forEach(stage => {
          // 加入节点信息
          nodes.push({
            x: i++,
            y: 1,
            itemStyle: {
              color: '#F56C6C'
            },
            ...stage
          })
        })

        return nodes
      },
      stageEdges() {
        const edges = []
        const stages = this.spiderForm.config.stages
        stages.forEach(stage => {
          for (let i = 0; i < stage.fields.length; i++) {
            const field = stage.fields[i]
            if (field.next_stage) {
              edges.push({
                source: stage.name,
                target: field.next_stage
              })
            }
          }
        })
        return edges
      }
    },

    watch: {
      activeTab() {
        setTimeout(() => {
          // 渲染流程图
          this.renderProcessChart()

          // 获取Spiderfile
          this.getSpiderfile()

          // 获取config
          this.$store.dispatch('spider/getSpiderData', this.spiderForm._id)
        }, 0)
      }
    },
    methods: {
      onSelectCrawlType(value) {
        this.spiderForm.crawl_type = value
      },
      async onSave() {
        this.$st.sendEv('爬虫详情', '配置', '保存')
        this.saveLoading = true
        try {
          const res = await this.$store.dispatch('spider/postConfigSpiderConfig')
          if (!res.data.error) {
            this.$message.success(this.$t('Spider info has been saved successfully'))
            return true
          }
          return false
        } catch (e) {
          return false
        } finally {
          this.saveLoading = false
        }
      },
      onDialogClose() {
        this.dialogVisible = false
        this.fields.forEach(f => {
          f.name = this.columnsDict[f.name]
        })
      },
      onPreview() {
        this.$refs['form'].validate(res => {
          if (res) {
            this.onSave()
              .then(() => {
                this.previewLoading = true
                this.$store.dispatch('spider/getPreviewCrawlData')
                  .then(() => {
                    this.fields.forEach(f => {
                      this.columnsDict[f.name] = f.name
                    })
                    this.dialogVisible = true
                  })
                  .catch(() => {
                    this.$message.error(this.$t('Something wrong happened'))
                  })
                  .finally(() => {
                    this.previewLoading = false
                  })
                this.$st.sendEv('爬虫详情', '配置', '预览')
              })
          }
        })
      },
      async onCrawl() {
        this.$st.sendEv('爬虫详情', '配置', '点击运行')
        const res = await this.onSave()
        if (res) {
          this.crawlConfirmDialogVisible = true
        }
      },
      onExtractFields() {
        this.$refs['form'].validate(res => {
          if (res) {
            this.onSave()
              .then(() => {
                this.extractFieldsLoading = true
                this.$store.dispatch('spider/extractFields')
                  .then(response => {
                    if (response.data.item_selector) {
                      this.$set(this.spiderForm, 'item_selector', response.data.item_selector)
                    }
                    if (response.data.item_selector_type) {
                      this.$set(this.spiderForm, 'item_selector_type', response.data.item_selector_type)
                    }

                    if (response.data.fields && response.data.fields.length) {
                      this.spiderForm.fields = response.data.fields
                    }

                    if (response.data.pagination_selector) {
                      this.spiderForm.pagination_selector = response.data.pagination_selector
                    }
                  })
                  .finally(() => {
                    this.extractFieldsLoading = false
                  })
                this.$st.sendEv('爬虫详情', '配置', '提取字段')
              })
          }
        })
      },
      onDeleteField(index) {
        this.fields.splice(index, 1)
      },
      getDisplayStr(value) {
        if (!value) return value
        value = value.trim()
        if (value.length > 20) return value.substr(0, 20) + '...'
        return value
      },
      onClickSelectorType(selectorType) {
        this.$st.sendEv('爬虫详情', '配置', `点击阶段选择器类别-${selectorType}`)
        this.spiderForm.config.stages.forEach(stage => {
          // 列表
          if (selectorType === 'css') {
            if (stage.list_xpath) stage.list_xpath = ''
            if (!stage.list_css) stage.list_css = 'body'
          } else {
            if (stage.list_css) stage.list_css = ''
            if (!stage.list_xpath) stage.list_xpath = '//body'
          }

          // 分页
          if (selectorType === 'css') {
            if (stage.page_xpath) stage.page_xpath = ''
            if (!stage.page_css) stage.page_css = 'body'
          } else {
            if (stage.page_css) stage.page_css = ''
            if (!stage.page_xpath) stage.page_xpath = '//body'
          }

          // 字段
          stage.fields.forEach(field => {
            if (selectorType === 'css') {
              if (field.xpath) field.xpath = ''
              if (!field.css) field.css = 'body'
            } else {
              if (field.css) field.css = ''
              if (!field.xpath) field.xpath = '//body'
            }
          })
        })
      },
      onStageNameFocus(ev) {
        ev.stopPropagation()
      },
      onEditStage(stage) {
        this.$st.sendEv('爬虫详情', '配置', '更改阶段名称')
        this.$set(stage, 'isEdit', !stage.isEdit)
        setTimeout(() => {
          this.$refs[`stage-name-${stage.name}`][0].focus()
        }, 0)
      },
      onCopyStage(stage) {
        this.$st.sendEv('爬虫详情', '配置', '复制阶段')
        const stages = this.spiderForm.config.stages
        const ts = Math.floor(new Date().getTime()).toString()
        const newStage = JSON.parse(JSON.stringify(stage))
        newStage.name = `${stage.name}_copy_${ts}`
        for (let i = 0; i < stages.length; i++) {
          if (stage.name === stages[i].name) {
            stages.splice(i + 1, 0, newStage)
          }
        }
      },
      addStage(index) {
        const stages = this.spiderForm.config.stages
        const ts = Math.floor(new Date().getTime()).toString()
        const newStageName = `stage_${ts}`
        const newField = { name: `field_${ts}`, next_stage: '' }
        if (this.isCss) {
          newField['css'] = 'body'
        } else if (this.isXpath) {
          newField['xpath'] = '//body'
        } else {
          newField['xpath'] = '//body'
        }
        stages.splice(index + 1, 0, {
          name: newStageName,
          list_css: this.isCss ? 'body' : '',
          list_xpath: this.isXpath ? '//body' : '',
          page_css: '',
          page_xpath: '',
          fields: [newField]
        })
      },
      onRemoveStage(stage) {
        this.$st.sendEv('爬虫详情', '配置', '删除阶段')
        const stages = this.spiderForm.config.stages
        for (let i = 0; i < stages.length; i++) {
          if (stage.name === stages[i].name) {
            stages.splice(i, 1)
            break
          }
        }
        // 如果只剩一个stage，加入新的stage
        if (stages.length === 0) {
          this.addStage(0)
        }
        // 重置next_stage被设置为该stage的field
        stages.forEach(_stage => {
          _stage.fields.forEach(field => {
            if (field.next_stage === stage.name) {
              this.$set(field, 'next_stage', '')
            }
          })
        })
      },
      onAddStage(stage) {
        this.$st.sendEv('爬虫详情', '配置', '添加阶段')
        const stages = this.spiderForm.config.stages
        for (let i = 0; i < stages.length; i++) {
          if (stage.name === stages[i].name) {
            this.addStage(i)
            break
          }
        }
      },
      renderProcessChart() {
        const option = {
          title: {
            text: this.$t('Stage Process')
          },
          series: [
            {
              animation: false,
              type: 'graph',
              // layout: 'force',
              layout: 'none',
              symbolSize: 50,
              roam: true,
              label: {
                normal: {
                  show: true
                }
              },
              edgeSymbol: ['circle', 'arrow'],
              edgeSymbolSize: [4, 10],
              edgeLabel: {
                normal: {
                  textStyle: {
                    fontSize: 20
                  }
                }
              },
              focusOneNodeAdjacency: true,
              force: {
                initLayout: 'force',
                repulsion: 100,
                gravity: 0.00001,
                edgeLength: 200
              },
              // draggable: true,
              data: this.stageNodes,
              links: this.stageEdges,
              lineStyle: {
                normal: {
                  opacity: 0.9,
                  width: 2,
                  curveness: 0
                }
              }
            }
          ],
          tooltip: {
            // formatter: '{b0}: {c0}<br />{b1}: {c1}',
            formatter: (params) => {
              if (!params.data.fields) return

              let str = ''
              str += `<label>${this.$t('Stage')}: ${params.name}</label><br>`
              str += `<ul style="list-style: none; padding: 0; margin: 0;">`
              // 列表
              if (params.data.list_css || params.data.list_xpath) {
                str += `<li><span style="display: inline-block;min-width: 50px;font-weight: bolder;text-align: right;margin-right: 3px">${this.$t('List')}: </span>${params.data.list_css || params.data.list_xpath}</li>`
              }
              if (params.data.page_css || params.data.page_xpath) {
                str += `<li><span style="display: inline-block;min-width: 50px;font-weight: bolder;text-align: right;margin-right: 3px">${this.$t('Pagination')}: </span>${params.data.page_css || params.data.page_xpath}</li>`
              }
              str += `</ul><br>`

              // 字段
              str += `<label>${this.$t('Fields')}: </label><br>`
              str += '<ul style="list-style: none; padding: 0; margin: 0;">'
              for (let i = 0; i < params.data.fields.length; i++) {
                const f = params.data.fields[i]
                str += `
<li>
<span style="display: inline-block; min-width: 50px; font-weight: bolder; text-align: right">${f.name}: </span>
${f.css || f.xpath} ${f.attr ? ('(' + f.attr + ')') : ''} ${f.next_stage ? (' --> ' + '<span style="font-weight:bolder">' + f.next_stage + '</span>') : ''}
</li>
`
              }
              str += '</ul>'
              return str
            }
          }
        }
        const el = document.querySelector('#process-chart')
        this.processChart = echarts.init(el)
        this.processChart.setOption(option)
        this.processChart.resize()
      },
      onTabClick(tab) {
        this.activeTab = tab.name
      },
      update() {
        if (this.activeTab !== 'stages') return

        // 手动显示tab下划线
        const elBar = document.querySelector('.el-tabs__active-bar')
        const elStages = document.querySelector('#tab-stages')
        const totalWidth = Number(getComputedStyle(elStages).width.replace('px', ''))
        const paddingRight = Number(getComputedStyle(elStages).paddingRight.replace('px', ''))
        elBar.setAttribute('style', 'width:' + (totalWidth - paddingRight) + 'px')
      },
      getSpiderfile() {
        this.$store.commit('file/SET_FILE_CONTENT', '')
        this.$store.commit('file/SET_CURRENT_PATH', 'Spiderfile')
        this.$store.dispatch('file/getFileContent', { path: 'Spiderfile' })
      },
      async onSpiderfileSave() {
        try {
          await this.$store.dispatch('spider/saveConfigSpiderSpiderfile')
          this.$message.success(this.$t('Spiderfile saved successfully'))
        } catch (e) {
          this.$message.error('Something wrong happened')
        }
      },
      isList(stage) {
        return !!stage.is_list
      },
      onCheckIsList(value, stage) {
        stage.is_list = value
        if (value) {
          this.$st.sendEv('爬虫详情', '配置', '勾选列表页')
        } else {
          this.$st.sendEv('爬虫详情', '配置', '取消勾选列表页')
        }
      },
      onClickStageList($event, stage, type) {
        $event.stopPropagation()
      },
      onSelectStageListType(stage, type) {
        if (type === 'css') {
          if (!stage.list_css) stage.list_css = 'body'
          stage.list_xpath = ''
        } else {
          if (!stage.list_xpath) stage.list_xpath = '//body'
          stage.list_css = ''
        }
      },
      isPage(stage) {
        return !!stage.page_css || !!stage.page_xpath
      },
      onCheckIsPage(value, stage) {
        if (value) {
          this.$st.sendEv('爬虫详情', '配置', '勾选分页')
          if (!stage.page_css && !stage.page_xpath) {
            stage.page_xpath = '//body'
          }
        } else {
          this.$st.sendEv('爬虫详情', '配置', '取消勾选分页')
          stage.page_css = ''
          stage.page_xpath = ''
        }
      },
      onClickStagePage($event, stage, type) {
        $event.stopPropagation()
      },
      onSelectStagePageType(stage, type) {
        if (type === 'css') {
          if (!stage.page_css) stage.page_css = 'body'
          stage.page_xpath = ''
        } else {
          if (!stage.page_xpath) stage.page_xpath = '//body'
          stage.page_css = ''
        }
      },
      getNextStageField(stage) {
        return stage.fields.filter(f => !!f.next_stage)[0]
      },
      getNextStage(stage) {
        const nextStageField = this.getNextStageField(stage)
        if (!nextStageField) return
        return this.spiderForm.config.stages[nextStageField.next_stage]
      },
      onConvert() {
        this.$confirm(this.$t('Are you sure to convert this spider to customized spider?'), this.$t('Notification'), {
          confirmButtonText: this.$t('Confirm'),
          cancelButtonText: this.$t('Cancel'),
          type: 'warning'
        }).then(async() => {
          this.spiderForm.type = 'customized'
          this.$store.dispatch('spider/editSpider')
            .then(res => {
              if (!res.data.error) {
                this.$store.commit('spider/SET_CONFIG_LIST_TS', +new Date())
                this.$message({
                  type: 'success',
                  message: 'Converted successfully'
                })
              } else {
                this.$message({
                  type: 'error',
                  message: 'Converted unsuccessfully'
                })
              }
              this.$store.dispatch('spider/getSpiderData', this.spiderForm._id)
              this.$st.sendEv('爬虫详情', '配置', '转化为自定义爬虫')
            })
        })
      }
    }
  }
</script>

<style scoped>

  .button-group-container {
    margin-top: 10px;
    /*border-bottom: 1px dashed #dcdfe6;*/
    padding-bottom: 20px;
  }

  .button-group {
    text-align: right;
  }

  .list-fields-container {
    margin-top: 20px;
    /*border-bottom: 1px dashed #dcdfe6;*/
    padding-bottom: 20px;
  }

  .detail-fields-container {
    margin-top: 20px;
  }

  .title {
    color: #606266;
    font-size: 14px;
  }

  .el-table.table-header >>> td {
    padding: 0;
  }

  .el-table.table-header >>> .cell {
    padding: 0;
  }

  .el-table.table-header >>> .el-input .el-input__inner {
    border-radius: 0;
  }

  .selector-type-item {
    margin: 0 5px;
    cursor: pointer;
    font-weight: bolder;
  }

  .el-tag {
    margin-right: 5px;
    font-weight: bolder;
    cursor: pointer;
  }

  .el-tag.inactive {
    opacity: 0.5;
  }

  .stage-list {
    width: 100%;
    /*width: calc(80px + 320px);*/
    display: flex;
    flex-wrap: wrap;
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .stage-list .stage-item {
    /*flex-basis: 320px;*/
    min-width: 120px;
    display: flex;
    align-items: center;
  }

  .stage-list .stage-item label {
    flex-basis: 90px;
    margin-right: 10px;
    justify-self: flex-end;
    text-align: right;
  }

  .stage-list .stage-item .el-input {
    flex-basis: calc(100% - 90px);
    height: 32px;
  }

  .stage-list .stage-item .el-input .el-input__inner {
    height: 32px;
    inline-size: 32px;
  }

  .stage-list .stage-item .action-item {
    cursor: pointer;
    width: 13px;
    margin-right: 5px;
  }

  .stage-list .stage-item .action-item:last-child {
    margin-right: 10px;
  }

  .stage-list .stage-item .text-wrapper {
    display: flex;
    align-items: center;
    max-width: calc(100% - 90px - 10px);
  }

  .stage-list .stage-item .text-wrapper .text {
    text-overflow: ellipsis;
    overflow: hidden;
  }

  .stage-list .stage-item .text-wrapper .text:hover {
    text-decoration: underline;
  }

  .stage-list .stage-item .text-wrapper i {
    margin-left: 5px;
  }

  .stage-list .stage-item >>> .edit-text {
    height: 32px;
    line-height: 32px;
  }

  .stage-list .stage-item >>> .edit-text .el-input__inner {
    height: 32px;
    line-height: 32px;
  }

  .top-wrapper {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .top-wrapper .list {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    padding: 0;
  }

  .top-wrapper .list .item {
    margin-bottom: 10px;
    display: flex;
    align-items: center;
  }

  .top-wrapper .list .item label {
    width: 100px;
    text-align: right;
    margin-right: 10px;
    font-size: 12px;
  }

  .top-wrapper .list .item label + * {
    width: 240px;
  }

  .invalid >>> .el-input__inner {
    border: 1px solid red !important;
  }

  #process-chart {
    width: 100%;
    height: 480px;
  }

  .config-list >>> .file-content {
    height: calc(100vh - 280px);
  }

  .spiderfile-actions {
    margin-bottom: 5px;
    text-align: right;
  }
</style>
