<template>
  <a-card>
    <div :class="$style.header">
      <a-button
        type="primary"
        @click="
          () => {
            isCreateFlag = true
            cronJobDialog = true
          }
        "
        >新建任务</a-button
      >
      <a-button
        :disabled="!selectedRowKeys.length || selectedRows.every((row) => row.state === '暂停')"
        @click="stopCronJob"
        >暂停任务</a-button
      >
      <a-button
        :disabled="selectedRowKeys.length !== 1 || selectedRows[0].state === '正常'"
        @click="resumeCronJob"
        >恢复任务</a-button
      >
      <a-button :disabled="selectedRowKeys.length !== 1" @click="removeCronJob">删除任务</a-button>
      <a-button
        :disabled="selectedRowKeys.length !== 1 || selectedRows[0].state === '暂停'"
        @click="executeCronJob"
        >立即执行</a-button
      >
      <a-button
        :disabled="selectedRowKeys.length !== 1 || selectedRows[0].state === '暂停'"
        @click="editCronJobInfo"
        >修改执行规则</a-button
      >
      <a-button @click="importCronJob">导入</a-button>
      <a-button :disabled="selectedRowKeys.length === 0" @click="exportCronJob">导出</a-button>
    </div>
    <a-table
      :data-source="dataList"
      :columns="columns"
      :row-key="'jobName'"
      :row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
      :pagination="{ showQuickJumper: true, showSizeChanger: true }"
    >
      <template slot="nextTime" slot-scope="record">
        <span>{{ moment(record).format('YYYY-MM-DD HH:mm:ss') }}</span>
      </template>
      <a-switch
        slot="state"
        slot-scope="text, record"
        :checked="text === '正常'"
        @click="(flag) => changeState(flag, record)"
      />
      <div slot="actions" slot-scope="text, record" :class="$style.actions">
        <a-popover :overlay-class-name="$style.popover">
          <template slot="content">
            <a-button-group>
              <a-button
                @click="
                  () => {
                    selectedRows = [record]
                    executeCronJob()
                  }
                "
              >
                立即执行
              </a-button>
              <a-button
                @click="
                  () => {
                    selectedRows = [record]
                    editCronJobInfo()
                  }
                "
              >
                修改执行规则
              </a-button>
              <a-button
                @click="
                  () => {
                    selectedRows = [record]
                    exportCronJob()
                  }
                "
              >
                导出
              </a-button>
            </a-button-group>
          </template>
          <a-icon type="ellipsis" />
        </a-popover>
      </div>
    </a-table>
    <a-modal
      v-model="cronJobDialog"
      destroy-on-close
      :title="isCreateFlag === true ? '新增定时任务' : '修改执行规则'"
      ok-text="确认"
      cancel-text="取消"
      width="700px"
      @ok="createCronJob"
      @cancel="
        () => {
          isCreateFlag = true
          cronJobDialog = false
        }
      "
    >
      <sd-form ref="createForm" :init-values="createFormData">
        <template>
          <table>
            <sd-form-colgroup :columns="2" />
            <tr v-if="isCreateFlag">
              <sd-form-item-td name="triggerName" />
            </tr>
            <tr v-if="isCreateFlag">
              <sd-form-item-td name="jobName" />
            </tr>
            <tr v-if="isCreateFlag">
              <sd-form-item-td name="targetBeanId" />
            </tr>
            <tr v-if="isCreateFlag">
              <sd-form-item-td name="description" />
            </tr>
            <tr>
              <sd-form-item-td name="cronExp" />
              <td :class="$style.formTd">
                <a-button
                  v-show="!cronExpSetting"
                  :class="$style.formBtn"
                  @click="
                    () => {
                      cronExpSetting = true
                    }
                  "
                  >高级设置</a-button
                >
                <a-button
                  v-show="cronExpSetting"
                  :class="$style.formBtn"
                  @click="
                    () => {
                      cronExpSetting = false
                    }
                  "
                  >收起</a-button
                >
              </td>
            </tr>
          </table>
        </template>
      </sd-form>
      <SdCronCreate
        v-show="cronExpSetting"
        ref="cronCreate"
        :scheduler="scheduler"
        @setCronExpValue="setCronExpValue"
      ></SdCronCreate>
    </a-modal>
    <a-modal
      title="导入"
      :visible="visibleImport"
      :width="650"
      destroy-on-close
      :mask-closable="false"
      @ok="importConfirm"
      @cancel="visibleImport = false"
    >
      <sd-form ref="importForm" :init-values="importFormData">
        <template>
          <sd-form-item name="attachment">
            <a-upload-dragger
              name="file"
              :multiple="false"
              :custom-request="(file) => customRequest(file)"
              :accept="'.gz'"
              :file-list="fileList"
              @change="handleChange"
            >
              <p class="ant-upload-drag-icon">
                <a-icon type="inbox" />
              </p>
              <p class="ant-upload-text">
                点击或将文件拖拽到这里上传
              </p>
            </a-upload-dragger>
          </sd-form-item>
          <sd-form-item name="type"> </sd-form-item>
        </template>
      </sd-form>
    </a-modal>
  </a-card>
</template>

<script>
import axios from '@/common/services/axios-instance'
import moment from 'moment'
import download from '@/common/services/download'
import { Modal, message } from 'ant-design-vue'
import { scheduler } from './util'
import ControlService from './control-service'
import SdCronCreate from './sd-cron-create.vue'
import components from './_import-components/sd-cron-job-list-import'

export default {
  name: 'SdCronJobList',
  metaInfo: {
    title: '定时任务管理',
  },
  components: { ...components, SdCronCreate },
  data() {
    const columns = [
      {
        title: '触发器名称',
        dataIndex: 'triggerName',
        width: '130px',
      },
      {
        title: '任务名称',
        dataIndex: 'jobName',
        width: '130px',
      },
      {
        title: '执行规则',
        dataIndex: 'cronExp',
        width: '180px',
      },
      {
        title: '状态',
        scopedSlots: { customRender: 'state' },
        dataIndex: 'state',
      },
      {
        title: '下次执行时间',
        dataIndex: 'nextTime',
        scopedSlots: { customRender: 'nextTime' },
      },
      {
        title: '任务来源',
        dataIndex: 'type',
      },
      {
        title: '任务描述',
        dataIndex: 'description',
        width: '180px',
      },
      {
        title: '操作',
        scopedSlots: { customRender: 'actions' },
        dataIndex: 'actions',
      },
    ]
    return {
      columns,
      dataList: [],
      selectedRowKeys: [],
      selectedRows: [],
      cronJobDialog: false, // 定时任务弹窗
      isCreateFlag: true, // 是否新增定时任务，true新增，false修改
      createFormData: [
        {
          name: 'triggerName',
          caption: '触发器名称',
          dataType: 'string',
          required: true,
          value: '',
        },
        {
          name: 'jobName',
          caption: '定时任务名称',
          dataType: 'string',
          required: true,
          value: '',
        },
        {
          name: 'targetBeanId',
          caption: '任务目标bean',
          dataType: 'string',
          required: true,
          value: '',
        },
        {
          name: 'description',
          caption: '任务描述',
          dataType: 'string',
          required: false,
          value: '',
        },
        {
          name: 'cronExp',
          caption: 'cron表达式',
          dataType: 'string',
          required: true,
          value: '',
        },
      ],
      cronExpSetting: false, // 是否展示cron表达式高级设置
      visibleImport: false, // 导入弹窗
      fileList: [], // 导入文件列表
      importFormData: [
        // 导入表单
        {
          name: 'type',
          dataType: 'selectlist',
          caption: '导入策略',
          readonly: false,
          required: false,
          attr: {
            defaultValue: '0',
            multiple: 'false',
            selectListItem: [
              {
                label: '跳过已存在的定时任务',
                value: '0',
              },
              {
                label: '停止导入',
                value: '2',
              },
            ],
          },
        },
        {
          name: 'attachment',
          dataType: 'attachment',
          caption: '数据文件',
          readonly: false,
          required: true,
          value: '{"value":"' + this.filegroupid() + '"}',
          attr: {
            allowType: '.xls',
            maxLength: 1,
            uploadable: true,
          },
        },
      ],
      scheduler,
    }
  },
  watch: {
    'selectedRowKeys.length': {
      handler() {
        const records = [...this.dataList, ...this.selectedRows]
        this.selectedRows = this.selectedRowKeys.map((key) =>
          records.find((record) => {
            const rowKey = 'jobName'
            const recordKey = record[rowKey]
            return recordKey === key
          })
        )
      },
      immediate: true,
    },
  },
  created() {
    this.getCronJobList()
  },
  methods: {
    moment,
    changeState(checked, record) {
      this.selectedRows = [record]
      if (checked) this.resumeCronJob()
      else this.stopCronJob()
    },
    onSelectChange(selectedRowKeys) {
      this.selectedRowKeys = selectedRowKeys
    },
    getCronJobList() {
      ControlService.getCronJobList({
        sort: '',
        startPosition: 0,
        maxResults: -1,
        orderBy: '',
      }).then((res) => {
        this.dataList = res.data.data
      })
    },
    resetCreateForm() {
      for (const x in this.$refs.createForm.getFieldsValue()) {
        this.$refs.createForm.setFieldValue(x, undefined)
      }
    },
    createCronJob() {
      this.$refs.createForm.validateFields().then(() => {
        if (this.isCreateFlag) {
          ControlService.addCronJob(this.$refs.createForm.getFieldsValue()).then((res) => {
            this.cronJobDialog = false
            this.resetCreateForm()
            this.$refs.cronCreate.resetCronData()
            message.info(this.scheduler[res.data])
            this.getCronJobList()
          })
        } else {
          ControlService.editCronJob(this.$refs.createForm.getFieldsValue()).then((res) => {
            this.cronJobDialog = false
            this.resetCreateForm()
            this.$refs.cronCreate.resetCronData()
            message.info(this.scheduler[res.data])
            this.getCronJobList()
          })
        }
      })
    },
    editCronJobInfo() {
      Modal.confirm({
        title: '修改执行规则',
        content: this.scheduler['scheduler.message.confirm.update'],
        okText: '确定',
        okType: 'danger',
        onOk: () => {
          this.createFormData.forEach((item, index) => {
            this.createFormData[index].value = this.selectedRows[0][item.name]
          })
          ControlService.getCronExpByTrigger({
            triggerName: this.selectedRows[0].triggerName,
          }).then((res) => {
            this.isCreateFlag = false
            this.cronJobDialog = true
            this.createFormData[4].value = res.data
          })
        },
      })
    },
    stopCronJob() {
      Modal.confirm({
        title: '暂停',
        content: this.scheduler['scheduler.message.confirm.update'],
        okText: '确定',
        okType: 'danger',
        onOk: () => {
          const data = this.selectedRows.map((row) => {
            return {
              jobName: row.jobName,
              triggerName: row.triggerName,
            }
          })
          ControlService.stopCronJob(data).then((res) => {
            if (res.data) {
              message.success(`任务暂停成功`)
              this.selectedRowKeys = []
              this.getCronJobList()
            }
          })
        },
      })
    },
    resumeCronJob() {
      Modal.confirm({
        title: '恢复',
        content: this.scheduler['scheduler.message.confirm.update'],
        okText: '确定',
        okType: 'danger',
        onOk: () => {
          const data = {
            jobName: this.selectedRows[0].jobName,
            triggerName: this.selectedRows[0].triggerName,
          }
          ControlService.resumeCronJob(data).then((res) => {
            message.info(this.scheduler[res.data])
            this.selectedRowKeys = []
            this.getCronJobList()
          })
        },
      })
    },
    removeCronJob() {
      Modal.confirm({
        title: '删除',
        content: '您确认要将选定的定时任务删除吗？',
        okText: '确定',
        okType: 'danger',
        onOk: () => {
          ControlService.removeCronJob(this.selectedRows).then((res) => {
            message.info(this.scheduler[res.data])
            this.selectedRowKeys = []
            this.getCronJobList()
          })
        },
      })
    },
    executeCronJob() {
      const data = {
        jobName: this.selectedRows[0].jobName,
        triggerName: this.selectedRows[0].triggerName,
      }
      ControlService.executeCronJob(data).then((res) => {
        message.info(this.scheduler[res.data])
        this.getCronJobList()
      })
    },
    importCronJob() {
      this.visibleImport = true
      this.fileList = []
    },
    // 表单确认
    importConfirm() {
      return new Promise((resolve) => {
        this.$refs.importForm.validateFields().then((success) => {
          if (success) {
            const values = this.$refs.importForm.getFieldsValue()
            let xhrReq = null
            xhrReq = ControlService.importCronJob({
              tmpFileInfo: values.attachment[0].response,
              strategy: values.type,
            })
            xhrReq
              .then((res) => {
                if (res.status === 200) {
                  Modal.info({
                    title: '处理明细',
                    content: res.data.join('\n'),
                    okText: '关闭',
                    onOk: () => {
                      this.visibleImport = false
                      this.getCronJobList()
                    },
                  })
                } else {
                  message.error(res.data?.message || '导入失败')
                }
              })
              .catch(() => message.error('导入失败'))
              .finally(() => {})
          }
        })
      })
    },
    // 上传临时文件
    customRequest(files, item) {
      if (files.file.size === 0) {
        message.warn(`不能上传大小为0的文件`)
        return
      }
      const formData = new FormData()
      formData.append('file', files.file)
      const url = 'api/framework/v1/tmp-file/upload'
      axios
        .post(url, formData, {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        })
        .then((res) => {
          if (res.status === 200) {
            files.onSuccess(res.data, files)
          } else {
            message.warn(`上传失败`)
          }
        })
        .catch((e) => {
          throw e
        })
    },
    // 文件更改
    handleChange(info) {
      let fileList = [...info.fileList]
      fileList = fileList.slice(-1)
      fileList = fileList.map((file) => {
        if (file.response) {
          file.url = file.response.url
          file.id = parseInt(file.response.id, 10)
        }
        return file
      })

      this.$refs.importForm.setFieldValue('attachment', fileList)
      this.fileList = fileList
    },
    // 获取groupId
    filegroupid() {
      return 'app_' + new Date().getTime() + '_' + (Math.floor(Math.random() * 100) + 1)
    },
    exportCronJob() {
      ControlService.exportCronJob(this.selectedRows).then((res) => {
        download(`api/framework/v1/tmp-file/download/${res.data.id}`, `${res.data.name}`, true)
      })
    },
    // 设置cron表达式的值
    setCronExpValue(val) {
      this.$refs.createForm.setFieldValue('cronExp', val)
    },
  },
}
</script>

<style module lang="scss">
@use '@/common/design' as *;
.header {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  float: right;
  margin: 3px 0;
  :global(.ant-btn) {
    margin: 5px 0 5px 10px;
  }
}
.form-td {
  line-height: 2.5;
  vertical-align: top;

  .form-btn {
    margin-left: $padding-md;
  }
}
.popover {
  :global .ant-popover-inner-content {
    padding: 0;
    margin-bottom: -10px;
  }
  :global .ant-popover-arrow {
    display: none;
  }

  :global .ant-btn-group {
    white-space: nowrap;
  }
}
</style>
