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

    <!--add dialog-->
    <el-dialog
      :title="$t('Add Spider')"
      width="40%"
      :visible.sync="addDialogVisible"
      :before-close="onAddDialogClose"
    >
      <el-tabs :active-name="activeTabName">
        <!-- customized -->
        <el-tab-pane name="customized" :label="$t('Customized')">
          <el-form ref="addCustomizedForm" :model="spiderForm" inline-message label-width="120px">
            <el-form-item :label="$t('Spider Name')" prop="name" required>
              <el-input id="spider-name" v-model="spiderForm.name" :placeholder="$t('Spider Name')" />
            </el-form-item>
            <el-form-item :label="$t('Display Name')" prop="display_name" required>
              <el-input id="display-name" v-model="spiderForm.display_name" :placeholder="$t('Display Name')" />
            </el-form-item>
            <el-form-item :label="$t('Project')" prop="project_id" required>
              <el-select
                v-model="spiderForm.project_id"
                :placeholder="$t('Project')"
                filterable
              >
                <el-option
                  v-for="p in projectList"
                  :key="p._id"
                  :value="p._id"
                  :label="p.name"
                />
              </el-select>
            </el-form-item>
            <el-form-item :label="$t('Execute Command')" prop="cmd" required>
              <el-input
                id="cmd"
                v-model="spiderForm.cmd"
                :placeholder="$t('Execute Command')"
                :disabled="spiderForm.is_scrapy"
              />
            </el-form-item>
            <el-form-item :label="$t('Results')" prop="col">
              <el-input
                id="col"
                v-model="spiderForm.col"
                :placeholder="$t('By default: ') + 'results_<spider_name>'"
              />
            </el-form-item>
            <el-form-item :label="$t('Upload Zip File')" label-width="120px" name="site">
              <el-upload
                :action="$request.baseUrl + '/spiders'"
                :data="uploadForm"
                :headers="{Authorization:token}"
                :on-success="onUploadSuccess"
                :file-list="fileList"
                :before-upload="beforeUpload"
              >
                <el-button id="upload" size="small" type="primary" icon="el-icon-upload">
                  {{ $t('Upload') }}
                </el-button>
              </el-upload>
            </el-form-item>
            <el-row>
              <el-col :span="8">
                <el-form-item :label="$t('Is Scrapy')" prop="is_scrapy">
                  <el-switch
                    v-model="spiderForm.is_scrapy"
                    active-color="#13ce66"
                    @change="onIsScrapy"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item :label="$t('Is Git')" prop="is_git">
                  <el-switch
                    v-model="spiderForm.is_git"
                    active-color="#13ce66"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item :label="$t('Is Long Task')" prop="is_long_task">
                  <el-switch
                    v-model="spiderForm.is_long_task"
                    active-color="#13ce66"
                  />
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
          <el-alert
            type="warning"
            :closable="false"
            style="margin-bottom: 10px"
          >
            <p>{{ $t('You can click "Add" to create an empty spider and upload files later.') }}</p>
            <p>{{ $t('OR, you can also click "Upload" and upload a zip file containing your spider project.') }}</p>
            <p>
              <i class="fa fa-exclamation-triangle" /> {{ $t('NOTE: When uploading a zip file, please zip your' +
                ' spider files from the ROOT DIRECTORY.') }}
            </p>
            <p style="font-weight: bolder">
              <template v-if="lang === 'en'">
                Recommend uploading spiders using <a
                  href="https://docs.crawlab.cn/SDK/CLI.html"
                  target="_blank"
                  style="color: #409eff;font-weight: bolder"
                >CLI Tool</a>.
              </template>
              <template v-else-if="lang === 'zh'">
                推荐使用 <a
                  href="https://docs.crawlab.cn/SDK/CLI.html"
                  target="_blank"
                  style="color: #409eff;font-weight: bolder"
                >CLI 工具</a> 上传爬虫。
              </template>
            </p>
          </el-alert>
          <div class="actions">
            <el-button size="small" type="primary" @click="onAddCustomized">{{ $t('Add') }}</el-button>
          </div>
        </el-tab-pane>
        <!-- configurable -->
        <el-tab-pane name="configurable" :label="$t('Configurable')">
          <el-form ref="addConfigurableForm" :model="spiderForm" inline-message label-width="120px">
            <el-form-item :label="$t('Spider Name')" prop="name" required>
              <el-input v-model="spiderForm.name" :placeholder="$t('Spider Name')" />
            </el-form-item>
            <el-form-item :label="$t('Display Name')" prop="display_name" required>
              <el-input v-model="spiderForm.display_name" :placeholder="$t('Display Name')" />
            </el-form-item>
            <el-form-item :label="$t('Project')" prop="project_id" required>
              <el-select
                v-model="spiderForm.project_id"
                :placeholder="$t('Project')"
                filterable
              >
                <el-option
                  v-for="p in projectList"
                  :key="p._id"
                  :value="p._id"
                  :label="p.name"
                />
              </el-select>
            </el-form-item>
            <el-form-item :label="$t('Template')" prop="template" required>
              <el-select
                id="template"
                v-model="spiderForm.template"
                :value="spiderForm.template"
                :placeholder="$t('Template')"
              >
                <el-option
                  v-for="template in templateList"
                  :key="template"
                  :label="template"
                  :value="template"
                />
              </el-select>
            </el-form-item>
            <el-form-item :label="$t('Results')" prop="col" required>
              <el-input v-model="spiderForm.col" :placeholder="$t('Results')" />
            </el-form-item>
          </el-form>
          <div class="actions">
            <el-button id="add" size="small" type="primary" @click="onAddConfigurable">{{ $t('Add') }}</el-button>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-dialog>
    <!--./add dialog-->

    <!--running tasks dialog-->
    <el-dialog
      :visible.sync="isRunningTasksDialogVisible"
      :title="`${$t('Latest Tasks')} (${$t('Spider')}: ${activeSpider ? activeSpider.name : ''})`"
      width="920px"
    >
      <el-tabs v-model="activeSpiderTaskStatus">
        <el-tab-pane name="pending" :label="$t('Pending')" />
        <el-tab-pane name="running" :label="$t('Running')" />
        <el-tab-pane name="finished" :label="$t('Finished')" />
        <el-tab-pane name="error" :label="$t('Error')" />
        <el-tab-pane name="cancelled" :label="$t('Cancelled')" />
        <el-tab-pane name="abnormal" :label="$t('Abnormal')" />
      </el-tabs>
      <el-table
        :data="activeNodeList"
        class="table"
        :header-cell-style="{background:'rgb(48, 65, 86)',color:'white'}"
        border
        default-expand-all
      >
        <el-table-column type="expand">
          <template slot-scope="scope">
            <h4 style="margin: 5px 10px">{{ $t('Tasks') }}</h4>
            <el-table
              :data="getTasksByNode(scope.row)"
              class="table"
              border
              style="margin: 5px 10px"
              max-height="240px"
              @row-click="onViewTask"
            >
              <el-table-column
                :label="$t('Create Time')"
                prop="create_ts"
                width="140px"
              />
              <el-table-column
                :label="$t('Start Time')"
                prop="start_ts"
                width="140px"
              />
              <el-table-column
                :label="$t('Finish Time')"
                prop="finish_ts"
                width="140px"
              />
              <el-table-column
                :label="$t('Parameters')"
                prop="param"
                width="120px"
              />
              <el-table-column
                :label="$t('Status')"
                width="120px"
              >
                <template slot-scope="scope">
                  <status-tag :status="scope.row.status" />
                </template>
              </el-table-column>
              <el-table-column
                :label="$t('Results Count')"
                prop="result_count"
                width="80px"
              />
              <el-table-column
                :label="$t('Action')"
                width="auto"
              >
                <template slot-scope="scope">
                  <el-button
                    v-if="['pending', 'running'].includes(scope.row.status)"
                    type="danger"
                    size="mini"
                    icon="el-icon-video-pause"
                    @click="onStop(scope.row, $event)"
                  />
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
        <el-table-column
          :label="$t('Node')"
          width="150px"
          prop="name"
        />
        <el-table-column
          :label="$t('Status')"
          width="120px"
          prop="status"
        >
          <template slot-scope="scope">
            <el-tag v-if="scope.row.status === 'offline'" type="info">{{ $t('Offline') }}</el-tag>
            <el-tag v-else-if="scope.row.status === 'online'" type="success">{{ $t('Online') }}</el-tag>
            <el-tag v-else type="danger">{{ $t('Unavailable') }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column
          :label="$t('Description')"
          width="auto"
          prop="description"
        />
      </el-table>
      <template slot="footer">
        <el-button type="primary" size="small" @click="isRunningTasksDialogVisible = false">{{ $t('Ok') }}</el-button>
      </template>
    </el-dialog>
    <!--./running tasks dialog-->

    <!--crawl confirm dialog-->
    <crawl-confirm-dialog
      :visible="crawlConfirmDialogVisible"
      :spider-id="activeSpiderId"
      :spiders="selectedSpiders"
      :multiple="isMultiple"
      @close="onCrawlConfirmDialogClose"
      @confirm="onCrawlConfirm"
    />
    <!--./crawl confirm dialog-->

    <!--copy dialog-->
    <copy-spider-dialog
      :visible="copyDialogVisible"
      :spider-id="activeSpiderId"
      @close="copyDialogVisible = false"
      @confirm="onCopyConfirm"
    />
    <!--./copy dialog-->

    <!--batch crawl dialog-->
    <batch-crawl-dialog
      :visible="batchCrawlDialogVisible"
      @close="onBatchCrawlDialogClose"
      @confirm="onBatchCrawlConfirm"
    />
    <!--./batch crawl dialog-->

    <!--set projects dialog-->
    <el-dialog
      :title="$t('Set Projects')"
      :visible.sync="setProjectsVisible"
      :before-close="() => setProjectsVisible = false"
      width="580px"
    >
      <el-form
        ref="set-projects-form"
        label-width="120px"
        :model="form"
      >
        <el-form-item :label="$t('Project')" prop="projectId" required>
          <el-select v-model="form.projectId" size="small" :placeholder="$t('Project')">
            <el-option
              v-for="op in projectList"
              :key="op._id"
              :label="op.name"
              :value="op._id"
            />
          </el-select>
        </el-form-item>
      </el-form>
      <template slot="footer">
        <el-button type="plain" size="small" @click="setProjectsVisible = false">{{ $t('Stop') }}</el-button>
        <el-button type="primary" size="small" @click="onSetProjectsConfirm">
          {{ $t('Confirm') }}
        </el-button>
      </template>
    </el-dialog>
    <!--./set projects dialog-->

    <el-card style="border-radius: 0">
      <!--filter-->
      <div class="filter">
        <div class="left">
          <el-form :inline="true">
            <!--            <el-form-item>-->
            <!--              <el-select clearable @change="onSpiderTypeChange" placeholder="爬虫类型" size="small" v-model="filter.type">-->
            <!--                <el-option v-for="item in types" :value="item.type" :key="item.type"-->
            <!--                           :label="item.type === 'customized'? '自定义':item.type "/>-->
            <!--              </el-select>-->
            <!--            </el-form-item>-->
            <el-form-item>
              <el-select
                v-model="filter.project_id"
                size="small"
                :placeholder="$t('Project')"
                @change="getList"
              >
                <el-option value="" :label="$t('All Projects')" />
                <el-option
                  v-for="p in projectList"
                  :key="p._id"
                  :value="p._id"
                  :label="p.name"
                />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-select
                v-model="filter.owner_type"
                size="small"
                :placeholder="$t('Owner Type')"
                @change="getList"
              >
                <el-option value="me" :label="$t('My Spiders')" />
                <el-option value="all" :label="$t('All Spiders')" />
                <el-option value="public" :label="$t('Public Spiders')" />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-input
                v-model="filter.keyword"
                size="small"
                :placeholder="$t('Spider Name')"
                clearable
                @keyup.enter.native="onSearch"
              >
                <i slot="suffix" class="el-input__icon el-icon-search" />
              </el-input>
            </el-form-item>
            <el-form-item>
              <el-button
                size="small"
                type="success"
                class="btn refresh"
                @click="onRefresh"
              >
                {{ $t('Search') }}
              </el-button>
            </el-form-item>
          </el-form>
        </div>
        <div class="right">
          <el-button
            size="small"
            type="danger"
            icon="el-icon-video-play"
            class="btn add"
            style="font-weight: bolder"
            @click="onBatchCrawl"
          >
            {{ $t('Batch Run') }}
          </el-button>
          <el-button
            v-if="this.selectedSpiders.length"
            size="small"
            type="primary"
            icon="el-icon-s-operation"
            class="btn set-projects"
            style="font-weight: bolder"
            @click="onSetProjects"
          >
            {{ $t('Set Projects') }}
          </el-button>
          <el-button
            v-if="this.selectedSpiders.length"
            size="small"
            type="info"
            :icon="isStopLoading ? 'el-icon-loading' : 'el-icon-video-pause'"
            class="btn add"
            style="font-weight: bolder"
            @click="onStopSelectedSpiders"
          >
            {{ $t('Stop') }}
          </el-button>
          <el-button
            v-if="this.selectedSpiders.length"
            size="small"
            type="danger"
            :icon="isRemoveLoading ? 'el-icon-loading' : 'el-icon-delete'"
            class="btn add"
            style="font-weight: bolder"
            @click="onRemoveSelectedSpiders"
          >
            {{ $t('Remove') }}
          </el-button>
          <el-button
            size="small"
            type="success"
            icon="el-icon-plus"
            class="btn add"
            style="font-weight: bolder"
            @click="onAdd"
          >
            {{ $t('Add Spider') }}
          </el-button>
        </div>
      </div>
      <!--./filter-->

      <!--tabs-->
      <el-tabs v-model="filter.type" class="tabs" @tab-click="onClickTab">
        <el-tab-pane :label="$t('All')" name="all" class="all" />
        <el-tab-pane :label="$t('Customized')" name="customized" class="customized" />
        <el-tab-pane :label="$t('Configurable')" name="configurable" class="configurable" />
        <el-tab-pane :label="$t('Long Task')" name="long-task" class="long-task" />
      </el-tabs>
      <!--./tabs-->

      <!--legend-->
      <status-legend />
      <!--./legend-->

      <!--table list-->
      <el-table
        ref="table"
        :data="spiderList"
        class="table"
        :header-cell-style="{background:'rgb(48, 65, 86)',color:'white'}"
        border
        row-key="_id"
        @row-click="onRowClick"
        @sort-change="onSortChange"
        @selection-change="onSpiderSelect"
      >
        <el-table-column
          type="selection"
          width="45"
          align="center"
          reserve-selection
        />
        <template v-for="col in columns">
          <el-table-column
            v-if="col.name === 'type'"
            :key="col.name"
            :label="$t(col.label)"
            align="left"
            :width="col.width"
            :sortable="col.sortable"
          >
            <template slot-scope="scope">
              {{ $t(scope.row.type) }}
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="col.name === 'last_5_errors'"
            :key="col.name"
            :label="$t(col.label)"
            :width="col.width"
            :sortable="col.sortable"
            align="center"
          >
            <template slot-scope="scope">
              <div :style="{color:scope.row[col.name]>0?'red':''}">
                {{ scope.row[col.name] }}
              </div>
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="col.name === 'cmd'"
            :key="col.name"
            :label="$t(col.label)"
            :width="col.width"
            :sortable="col.sortable"
            align="left"
          >
            <template slot-scope="scope">
              <el-input v-model="scope.row[col.name]" />
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="col.name.match(/_ts$/)"
            :key="col.name"
            :label="$t(col.label)"
            :sortable="col.sortable"
            :align="col.align"
            :width="col.width"
          >
            <template slot-scope="scope">
              {{ getTime(scope.row[col.name]) }}
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="col.name === 'last_status'"
            :key="col.name"
            :label="$t(col.label)"
            align="left"
            :width="col.width"
            :sortable="col.sortable"
          >
            <template slot-scope="scope">
              <status-tag :status="scope.row.last_status" />
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="['is_scrapy', 'is_long_task'].includes(col.name)"
            :key="col.name"
            :label="$t(col.label)"
            align="left"
            :width="col.width"
            :sortable="col.sortable"
          >
            <template slot-scope="scope">
              <el-switch
                v-if="scope.row.type === 'customized'"
                v-model="scope.row[col.name]"
                active-color="#13ce66"
                disabled
              />
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="col.name === 'latest_tasks'"
            :key="col.name"
            :label="$t(col.label)"
            :width="col.width"
            :align="col.align"
            class-name="latest-tasks"
          >
            <template slot-scope="scope">
              <el-tag
                v-if="getTaskCountByStatus(scope.row, 'pending') > 0"
                type="primary"
                size="small"
              >
                <i class="el-icon-loading" />
                {{ getTaskCountByStatus(scope.row, 'pending') }}
              </el-tag>
              <el-tag
                v-if="getTaskCountByStatus(scope.row, 'running') > 0"
                type="warning"
                size="small"
              >
                <i class="el-icon-loading" />
                {{ getTaskCountByStatus(scope.row, 'running') }}
              </el-tag>
              <el-tag
                v-if="getTaskCountByStatus(scope.row, 'finished') > 0"
                type="success"
                size="small"
              >
                <i class="el-icon-check" />
                {{ getTaskCountByStatus(scope.row, 'finished') }}
              </el-tag>
              <el-tag
                v-if="getTaskCountByStatus(scope.row, 'error') > 0"
                type="danger"
                size="small"
              >
                <i class="el-icon-error" />
                {{ getTaskCountByStatus(scope.row, 'error') }}
              </el-tag>
              <el-tag
                v-if="getTaskCountByStatus(scope.row, 'cancelled') > 0"
                type="info"
                size="small"
              >
                <i class="el-icon-video-pause" />
                {{ getTaskCountByStatus(scope.row, 'cancelled') }}
              </el-tag>
              <el-tag
                v-if="getTaskCountByStatus(scope.row, 'abnormal') > 0"
                type="danger"
                size="small"
              >
                <i class="el-icon-warning" />
                {{ getTaskCountByStatus(scope.row, 'abnormal') }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column
            v-else
            :key="col.name"
            :property="col.name"
            :label="$t(col.label)"
            :sortable="col.sortable"
            :align="col.align || 'left'"
            :width="col.width"
          />
        </template>
        <el-table-column :label="$t('Action')" align="left" fixed="right" min-width="220px">
          <template slot-scope="scope">
            <el-tooltip :content="$t('View')" placement="top">
              <el-button
                type="primary"
                icon="el-icon-search"
                size="mini"
                :disabled="isDisabled(scope.row)"
                @click="onView(scope.row, $event)"
              />
            </el-tooltip>
            <el-tooltip :content="$t('Remove')" placement="top">
              <el-button
                type="danger"
                icon="el-icon-delete"
                size="mini"
                :disabled="isDisabled(scope.row)"
                @click="onRemove(scope.row, $event)"
              />
            </el-tooltip>
            <el-tooltip :content="$t('Copy')" placement="top">
              <el-button
                type="info"
                icon="el-icon-copy-document"
                size="mini"
                @click="onCopy(scope.row, $event)"
              />
            </el-tooltip>
            <el-tooltip v-if="!isShowRun(scope.row)" :content="$t('No command line')" placement="top">
              <el-button
                disabled
                type="success"
                icon="fa fa-bug"
                size="mini"
                @click="onCrawl(scope.row, $event)"
              />
            </el-tooltip>
            <el-tooltip v-else :content="$t('Run')" placement="top">
              <el-button
                type="success"
                icon="fa fa-bug"
                size="mini"
                :disabled="isDisabled(scope.row)"
                @click="onCrawl(scope.row, $event)"
              />
            </el-tooltip>
            <el-tooltip :content="$t('Latest Tasks')" placement="top">
              <el-button
                type="warning"
                icon="fa fa-tasks"
                size="mini"
                :disabled="isDisabled(scope.row)"
                @click="onViewRunningTasks(scope.row, $event)"
              />
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination">
        <el-pagination
          :current-page.sync="pagination.pageNum"
          :page-sizes="[10, 20, 50, 100]"
          :page-size.sync="pagination.pageSize"
          layout="sizes, prev, pager, next"
          :total="spiderTotal"
          @current-change="onPageNumChange"
          @size-change="onPageSizeChange"
        />
      </div>
      <!--./table list-->
    </el-card>
  </div>
</template>

<script>
  import {
    mapState,
    mapGetters
  } from 'vuex'
  import dayjs from 'dayjs'
  import CrawlConfirmDialog from '../../components/Common/CrawlConfirmDialog'
  import BatchCrawlDialog from '../../components/Common/BatchCrawlDialog'
  import StatusTag from '../../components/Status/StatusTag'
  import StatusLegend from '../../components/Status/StatusLegend'
  import CopySpiderDialog from '../../components/Spider/CopySpiderDialog'

  export default {
    name: 'SpiderList',
    components: {
      CopySpiderDialog,
      StatusLegend,
      CrawlConfirmDialog,
      StatusTag,
      BatchCrawlDialog
    },
    data() {
      return {
        pagination: {
          pageNum: 1,
          pageSize: 10
        },
        importLoading: false,
        addConfigurableLoading: false,
        isEditMode: false,
        dialogVisible: false,
        addDialogVisible: false,
        crawlConfirmDialogVisible: false,
        batchCrawlDialogVisible: false,
        setProjectsVisible: false,
        isRunningTasksDialogVisible: false,
        activeSpiderId: undefined,
        activeSpider: undefined,
        filter: {
          project_id: '',
          keyword: '',
          type: 'all',
          owner_type: 'me'
        },
        sort: {
          sortKey: '',
          sortDirection: null
        },
        types: [],
        spiderFormRules: {
          name: [{ required: true, message: 'Required Field', trigger: 'change' }]
        },
        fileList: [],
        activeTabName: 'customized',
        tourSteps: [
          {
            target: '#tab-customized',
            content: this.$t('View a list of <strong>Customized Spiders</strong>'),
            params: {
              highlight: false
            }
          },
          {
            target: '#tab-configurable',
            content: this.$t('View a list of <strong>Configurable Spiders</strong>'),
            params: {
              highlight: false
            }
          },
          {
            target: '.table',
            content: this.$t('You can view your created spiders here.<br>Click a table row to view <strong>spider details</strong>.'),
            params: {
              placement: 'top'
            }
          },
          {
            target: '.btn.add',
            content: this.$t('Click to add a new spider.<br><br>You can also add a <strong>Customized Spider</strong> through <a href="https://docs.crawlab.cn/Usage/SDK/CLI.html" target="_blank" style="color: #409EFF">CLI Tool</a>.')
          }
        ],
        tourCallbacks: {
          onStop: () => {
            this.$utils.tour.finishTour('spider-list')
          },
          onPreviousStep: (currentStep) => {
            this.$utils.tour.prevStep('spider-list', currentStep)
          },
          onNextStep: (currentStep) => {
            this.$utils.tour.nextStep('spider-list', currentStep)
          }
        },
        tourAddSteps: [
          {
            target: '#tab-customized',
            content: this.$t('<strong>Customized Spider</strong> is a highly customized spider, which is able to run on any programming language and any web crawler framework.'),
            params: {
              placement: 'bottom',
              highlight: false
            }
          },
          {
            target: '#tab-configurable',
            content: this.$t('<strong>Configurable Spider</strong> is a spider defined by config data, aimed at streamlining spider development and improving dev efficiency.'),
            params: {
              placement: 'bottom',
              highlight: false
            }
          },
          {
            target: '#spider-name',
            content: this.$t('Unique identifier for the spider'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '#display-name',
            content: this.$t('How the spider is displayed on Crawlab'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '#cmd',
            content: this.$t('A shell command to be executed when the spider is triggered to run (only available for <strong>Customized Spider</strong>'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '#col',
            content: this.$t('Where the results are stored in the database'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '#upload',
            content: this.$t('Upload a zip file containing all spider files to create the spider (only available for <strong>Customized Spider</strong>)'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '#template',
            content: this.$t('The spider template to create from (only available for <strong>Configurable Spider</strong>)'),
            params: {
              placement: 'right'
            }
          },
          {
            target: '#add',
            content: this.$t('Click to confirm to add the spider'),
            params: {
              placement: 'right'
            }
          }
        ],
        tourAddCallbacks: {
          onStop: () => {
            this.$utils.tour.finishTour('spider-list-add')
          },
          onPreviousStep: (currentStep) => {
            if (currentStep === 7) {
              this.activeTabName = 'customized'
            }
            this.$utils.tour.prevStep('spider-list-add', currentStep)
          },
          onNextStep: (currentStep) => {
            if (currentStep === 6) {
              this.activeTabName = 'configurable'
            }
            this.$utils.tour.nextStep('spider-list-add', currentStep)
          }
        },
        handle: undefined,
        activeSpiderTaskStatus: 'running',
        selectedSpiders: [],
        isStopLoading: false,
        isRemoveLoading: false,
        isMultiple: false,
        copyDialogVisible: false,
        form: {
          projectId: null
        }
      }
    },
    computed: {
      ...mapState('spider', [
        'importForm',
        'spiderList',
        'spiderForm',
        'spiderTotal',
        'templateList'
      ]),
      ...mapState('task', [
        'batchCrawlList'
      ]),
      ...mapGetters('user', [
        'userInfo',
        'token'
      ]),
      ...mapState('lang', [
        'lang'
      ]),
      ...mapState('project', [
        'projectList'
      ]),
      ...mapState('node', [
        'nodeList'
      ]),
      uploadForm() {
        return {
          name: this.spiderForm.name,
          display_name: this.spiderForm.display_name,
          col: this.spiderForm.col,
          cmd: this.spiderForm.cmd
        }
      },
      columns() {
        const columns = []
        columns.push({ name: 'display_name', label: 'Name', width: '160', align: 'left', sortable: true })
        columns.push({ name: 'type', label: 'Spider Type', width: '120', sortable: true })
        columns.push({ name: 'project_name', label: 'Project', width: '120' })
        columns.push({ name: 'is_long_task', label: 'Is Long Task', width: '80' })
        columns.push({ name: 'is_scrapy', label: 'Is Scrapy', width: '80' })
        columns.push({ name: 'latest_tasks', label: 'Latest Tasks', width: '180' })
        columns.push({ name: 'last_status', label: 'Last Status', width: '120' })
        columns.push({ name: 'last_run_ts', label: 'Last Run', width: '140' })
        columns.push({ name: 'update_ts', label: 'Update Time', width: '140' })
        columns.push({ name: 'create_ts', label: 'Create Time', width: '140' })
        columns.push({ name: 'username', label: 'Owner', width: '100' })
        columns.push({ name: 'remark', label: 'Remark', width: '140' })
        return columns
      },
      activeNodeList() {
        return this.nodeList.filter(d => {
          return d.status === 'online'
        })
      },
      activeSpiderIds() {
        return this.selectedSpiders.map(d => d._id)
      }
    },
    async created() {
      // fetch project list
      await this.$store.dispatch('project/getProjectList')

      // project id
      if (this.$route.params.project_id) {
        this.filter.project_id = this.$route.params.project_id
      }

      // fetch node list
      await this.$store.dispatch('node/getNodeList')

      // fetch spider list
      await this.getList()

      // fetch all spider list
      await this.$store.dispatch('spider/getAllSpiderList')

      // fetch template list
      await this.$store.dispatch('spider/getTemplateList')

      // periodically fetch spider list
      this.handle = setInterval(() => {
        this.getList()
      }, 15000)
    },
    mounted() {
      const vm = this
      this.$nextTick(() => {
        vm.$store.commit('spider/SET_SPIDER_FORM', this.spiderForm)
      })

      if (!this.$utils.tour.isFinishedTour('spider-list')) {
        this.$utils.tour.startTour(this, 'spider-list')
      }
    },
    destroyed() {
      clearInterval(this.handle)
    },
    methods: {
      onSpiderTypeChange(val) {
        this.filter.type = val
        this.getList()
      },
      onPageSizeChange(val) {
        this.pagination.pageSize = val
        this.getList()
      },
      onPageNumChange(val) {
        this.pagination.pageNum = val
        this.getList()
      },
      onSearch() {
        this.getList()
      },
      onAdd() {
        let projectId = '000000000000000000000000'
        if (this.filter.project_id) {
          projectId = this.filter.project_id
        }
        this.$store.commit('spider/SET_SPIDER_FORM', {
          project_id: projectId,
          template: this.templateList[0]
        })
        this.addDialogVisible = true

        setTimeout(() => {
          if (!this.$utils.tour.isFinishedTour('spider-list-add')) {
            this.$utils.tour.startTour(this, 'spider-list-add')
          }
        }, 300)
      },
      onAddConfigurable() {
        this.$refs['addConfigurableForm'].validate(async res => {
          if (!res) return

          let res2
          try {
            res2 = await this.$store.dispatch('spider/addConfigSpider')
          } catch (e) {
            this.$message.error(this.$t('Something wrong happened'))
            return
          }
          this.$router.push(`/spiders/${res2.data.data._id}`)
          this.getList()
          this.$st.sendEv('爬虫列表', '添加爬虫', '可配置爬虫')
        })
      },
      onAddCustomized() {
        this.$refs['addCustomizedForm'].validate(async res => {
          if (!res) return
          let res2
          try {
            res2 = await this.$store.dispatch('spider/addSpider')
          } catch (e) {
            this.$message.error(this.$t('Something wrong happened'))
            return
          }
          this.$router.push(`/spiders/${res2.data.data._id}`)
          this.getList()
          this.$st.sendEv('爬虫列表', '添加爬虫', '自定义爬虫')
        })
      },
      onRefresh() {
        this.getList()
        this.$st.sendEv('爬虫列表', '刷新')
      },
      onSubmit() {
        const vm = this
        const formName = 'spiderForm'
        this.$refs[formName].validate((valid) => {
          if (valid) {
            if (this.isEditMode) {
              vm.$store.dispatch('spider/editSpider')
            } else {
              vm.$store.dispatch('spider/addSpider')
            }
            vm.dialogVisible = false
          } else {
            return false
          }
        })
      },
      onCancel() {
        this.$store.commit('spider/SET_SPIDER_FORM', {})
        this.dialogVisible = false
      },
      onDialogClose() {
        this.$store.commit('spider/SET_SPIDER_FORM', {})
        this.dialogVisible = false
      },
      onAddDialogClose() {
        this.addDialogVisible = false
      },
      onEdit(row) {
        this.isEditMode = true
        this.$store.commit('spider/SET_SPIDER_FORM', row)
        this.dialogVisible = true
      },
      onRemove(row, ev) {
        ev.stopPropagation()
        this.$confirm(this.$t('Are you sure to delete this spider?'), this.$t('Notification'), {
          confirmButtonText: this.$t('Confirm'),
          cancelButtonText: this.$t('Cancel'),
          type: 'warning'
        }).then(async() => {
          await this.$store.dispatch('spider/deleteSpider', row._id)
          this.$message({
            type: 'success',
            message: this.$t('Deleted successfully')
          })
          await this.getList()
          this.$st.sendEv('爬虫列表', '删除爬虫')
        })
      },
      onCrawl(row, ev) {
        ev.stopPropagation()
        this.crawlConfirmDialogVisible = true
        this.activeSpiderId = row._id
        this.$st.sendEv('爬虫列表', '点击运行')
      },
      onCrawlConfirm() {
        setTimeout(() => {
          this.getList()
        }, 1000)
      },
      onBatchCrawlConfirm() {
        setTimeout(() => {
          this.getList()
        }, 1000)
      },
      onCopy(row, ev) {
        ev.stopPropagation()
        this.copyDialogVisible = true
        this.activeSpiderId = row._id
        this.$st.sendEv('爬虫列表', '点击复制')
      },
      onCopyConfirm() {
        setTimeout(() => {
          this.getList()
        }, 1000)
      },
      onView(row, ev) {
        ev.stopPropagation()
        this.$router.push('/spiders/' + row._id)
        this.$st.sendEv('爬虫列表', '查看爬虫')
      },
      onImport() {
        this.$refs.importForm.validate(valid => {
          if (valid) {
            this.importLoading = true
            // TODO: switch between github / gitlab / svn
            this.$store.dispatch('spider/importGithub')
              .then(response => {
                this.$message.success('Import repo successfully')
                this.getList()
              })
              .catch(response => {
                this.$message.error(response.data.error)
              })
              .finally(() => {
                this.dialogVisible = false
                this.importLoading = false
              })
          }
        })
        this.$st.sendEv('爬虫列表', '导入爬虫')
      },
      openImportDialog() {
        this.dialogVisible = true
      },
      isShowRun(row) {
        if (!this.isCustomized(row)) return true
        return !!row.cmd
      },
      isCustomized(row) {
        return row.type === 'customized'
      },
      fetchSiteSuggestions(keyword, callback) {
        this.$request.get('/sites', {
          keyword: keyword,
          page_num: 1,
          page_size: 100
        }).then(response => {
          const data = response.data.items.map(d => {
            d.value = d.name + ' | ' + d.domain
            return d
          })
          callback(data)
        })
      },
      onUploadSuccess(res) {
        // clear fileList
        this.fileList = []

        // fetch spider list
        setTimeout(() => {
          this.getList()
        }, 500)

        // message
        this.$message.success(this.$t('Uploaded spider files successfully'))

        // navigate to spider detail
        this.$router.push(`/spiders/${res.data._id}`)
      },
      beforeUpload(file) {
        return new Promise((resolve, reject) => {
          this.$refs['addCustomizedForm'].validate(res => {
            if (res) {
              resolve()
            } else {
              reject(new Error('form validation error'))
            }
          })
        })
      },
      getTime(str) {
        if (!str || str.match('^0001')) return 'NA'
        return dayjs(str).format('YYYY-MM-DD HH:mm:ss')
      },
      onRowClick(row, column, event) {
        this.onView(row, event)
      },
      onSortChange({ column, prop, order }) {
        this.sort.sortKey = order ? prop : ''
        this.sort.sortDirection = order
        this.getList()
      },
      onClickTab(tab) {
        this.filter.type = tab.name
        this.getList()
      },
      async getList() {
        const params = {
          page_num: this.pagination.pageNum,
          page_size: this.pagination.pageSize,
          sort_key: this.sort.sortKey,
          sort_direction: this.sort.sortDirection,
          keyword: this.filter.keyword,
          type: this.filter.type,
          project_id: this.filter.project_id,
          owner_type: this.filter.owner_type
        }
        await this.$store.dispatch('spider/getSpiderList', params)

        // 更新当前爬虫（任务列表）
        this.updateActiveSpider()
      },
      getTasksByStatus(row, status) {
        if (!row.latest_tasks) return []
        return row.latest_tasks.filter(d => d.status === status)
      },
      getTaskCountByStatus(row, status) {
        return this.getTasksByStatus(row, status).length
      },
      updateActiveSpider() {
        if (this.activeSpider) {
          for (let i = 0; i < this.spiderList.length; i++) {
            const spider = this.spiderList[i]
            if (this.activeSpider._id === spider._id) {
              this.activeSpider = spider
            }
          }
        }
      },
      onViewRunningTasks(row, ev) {
        ev.stopPropagation()
        this.activeSpider = row
        this.isRunningTasksDialogVisible = true
      },
      getTasksByNode(row) {
        if (!this.activeSpider.latest_tasks) {
          return []
        }
        return this.activeSpider.latest_tasks
          .filter(d => d.node_id === row._id && d.status === this.activeSpiderTaskStatus)
          .map(d => {
            d = JSON.parse(JSON.stringify(d))
            d.create_ts = d.create_ts.match('^0001') ? 'NA' : dayjs(d.create_ts).format('YYYY-MM-DD HH:mm:ss')
            d.start_ts = d.start_ts.match('^0001') ? 'NA' : dayjs(d.start_ts).format('YYYY-MM-DD HH:mm:ss')
            d.finish_ts = d.finish_ts.match('^0001') ? 'NA' : dayjs(d.finish_ts).format('YYYY-MM-DD HH:mm:ss')
            return d
          })
      },
      onViewTask(row) {
        this.$router.push(`/tasks/${row._id}`)
        this.$st.sendEv('爬虫列表', '任务列表', '查看任务')
      },
      async onStop(row, ev) {
        ev.stopPropagation()
        const res = await this.$store.dispatch('task/cancelTask', row._id)
        if (!res.data.error) {
          this.$message.success(`Task "${row._id}" has been sent signal to stop`)
          this.getList()
        }
        this.$st.sendEv('爬虫列表', '任务列表', '停止任务')
      },
      onIsScrapy(value) {
        if (value) {
          this.spiderForm.cmd = 'scrapy crawl'
        }
      },
      onSpiderSelect(spiders) {
        this.selectedSpiders = spiders
      },
      async onRemoveSelectedSpiders() {
        this.$confirm(this.$t('Are you sure to delete selected items?'), this.$t('Notification'), {
          confirmButtonText: this.$t('Confirm'),
          cancelButtonText: this.$t('Cancel'),
          type: 'warning'
        }).then(async() => {
          this.isRemoveLoading = true
          try {
            const res = await this.$request.delete('/spiders', {
              spider_ids: this.selectedSpiders.map(d => d._id)
            })
            if (!res.data.error) {
              this.$message.success('Deleted successfully')
              this.$refs['table'].clearSelection()
              await this.getList()
            }
          } finally {
            this.isRemoveLoading = false
          }
          this.$st.sendEv('爬虫列表', '批量删除爬虫')
        })
      },
      async onStopSelectedSpiders() {
        this.$confirm(this.$t('Are you sure to stop selected items?'), this.$t('Notification'), {
          confirmButtonText: this.$t('Confirm'),
          cancelButtonText: this.$t('Cancel'),
          type: 'warning'
        }).then(async() => {
          this.isStopLoading = true
          try {
            const res = await this.$request.post('/spiders-cancel', {
              spider_ids: this.selectedSpiders.map(d => d._id)
            })
            if (!res.data.error) {
              this.$message.success('Sent signals to cancel selected tasks')
              await this.getList()
            }
          } finally {
            this.isStopLoading = false
          }
          this.$st.sendEv('爬虫列表', '批量停止爬虫')
        })
      },
      onBatchCrawl() {
        this.$store.commit('task/SET_BATCH_CRAWL_LIST', this.selectedSpiders.map(d => {
          return {
            spider_id: d._id,
            run_type: 'random',
            param: '',
            scrapy_log_level: 'INFO'
          }
        }))
        if (this.batchCrawlList.length < 10) {
          for (let i = this.batchCrawlList.length; i < 10; i++) {
            this.batchCrawlList.push({
              spider_id: '',
              run_type: 'random',
              param: '',
              scrapy_log_level: 'INFO'
            })
          }
        }
        this.batchCrawlDialogVisible = true
        this.isMultiple = true
        this.$st.sendEv('爬虫列表', '点击批量运行')
      },
      onCrawlConfirmDialogClose() {
        this.crawlConfirmDialogVisible = false
        this.isMultiple = false
      },
      onBatchCrawlDialogClose() {
        this.batchCrawlDialogVisible = false
        this.isMultiple = false
      },
      isDisabled(row) {
        return row.is_public && row.username !== this.userInfo.username && this.userInfo.role !== 'admin'
      },
      onSetProjects() {
        this.setProjectsVisible = true
        this.isMultiple = true
        this.form.projectId = null
      },
      onSetProjectsConfirm() {
        this.$refs['set-projects-form'].validate(async valid => {
          if (!valid) return
          await this.$store.dispatch('spider/setProjects', {
            projectId: this.form.projectId,
            spiderIds: this.selectedSpiders.map(d => d._id)
          })
          this.setProjectsVisible = false
          this.isMultiple = false
          await this.getList()
        })
        this.$st.sendEv('爬虫列表', '批量设置项目')
      }
    }
  }
</script>

<style scoped lang="scss">
  .el-dialog {
    .el-select {
      width: 100%;
    }
  }

  .filter {
    display: flex;
    justify-content: space-between;

    .filter-search {
      width: 240px;
    }

    .right {
      .btn {
        margin-left: 10px;
      }
    }
  }

  .table {
    margin-top: 8px;
    border-radius: 5px;

    .el-button {
      padding: 7px;
    }
  }

  .delete-confirm {
    background-color: red;
  }

  .add-spider-wrapper {
    display: flex;
    justify-content: center;

    .add-spider-item {
      cursor: pointer;
      width: 180px;
      font-size: 18px;
      height: 120px;
      margin: 0 20px;
      flex-basis: 40%;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .add-spider-item.primary {
      color: #409eff;
      background: rgba(64, 158, 255, .1);
      border: 1px solid rgba(64, 158, 255, .1);
    }

    .add-spider-item.success {
      color: #67c23a;
      background: rgba(103, 194, 58, .1);
      border: 1px solid rgba(103, 194, 58, .1);
    }

    .add-spider-item.info {
      color: #909399;
      background: #f4f4f5;
      border: 1px solid #e9e9eb;
    }

  }

  .el-autocomplete {
    width: 100%;
  }

</style>

<style scoped>
  .el-table >>> tr {
    cursor: pointer;
  }

  .actions {
    text-align: right;
  }

  .el-table >>> .latest-tasks .el-tag {
    margin: 3px 3px 0 0;
  }
</style>
