<template>

  <div class="dict-tab-panel sync-panel">

    <el-row :gutter="16" class="mb16">

      <el-col :span="8">

        <div class="sync-mode-card merge">

          <div class="sync-mode-title">MERGE · 合并</div>

          <div class="sync-mode-desc">推荐。新增缺失项，更新平台管控字段，保留租户扩展</div>

        </div>

      </el-col>

      <el-col :span="8">

        <div class="sync-mode-card append">

          <div class="sync-mode-title">APPEND · 追加</div>

          <div class="sync-mode-desc">仅追加模板中不存在的数据，不修改已有项</div>

        </div>

      </el-col>

      <el-col :span="8">

        <div class="sync-mode-card overwrite">

          <div class="sync-mode-title">OVERWRITE · 覆盖</div>

          <div class="sync-mode-desc">覆盖各租户中「平台管控」的字典项，请谨慎使用</div>

        </div>

      </el-col>

    </el-row>



    <el-card shadow="never" class="mb16 sync-config-card">

      <div slot="header" class="card-header">

        <span><i class="el-icon-setting" /> 同步配置</span>

      </div>

      <el-form ref="syncForm" :model="syncForm" label-width="100px" size="small">

        <el-row :gutter="20">

          <el-col :span="8">

            <el-form-item label="同步模式" required>

              <el-select v-model="syncForm.syncMode" style="width: 100%">

                <el-option label="MERGE - 合并（推荐）" value="MERGE" />

                <el-option label="APPEND - 仅追加缺失项" value="APPEND" />

                <el-option label="OVERWRITE - 覆盖平台管控项" value="OVERWRITE" />

              </el-select>

            </el-form-item>

          </el-col>

          <el-col :span="8">

            <el-form-item label="租户范围" required>

              <el-select v-model="syncForm.scopeType" style="width: 100%">

                <el-option label="全部启用租户" value="ALL" />

                <el-option label="仅指定租户" value="INCLUDE" />

                <el-option label="排除指定租户" value="EXCLUDE" />

              </el-select>

            </el-form-item>

          </el-col>

          <el-col :span="8">

            <el-form-item label="并行执行">

              <div class="parallel-row">

                <el-switch v-model="syncForm.parallel" />

                <el-input-number v-if="syncForm.parallel" v-model="syncForm.threads" :min="1" :max="16" size="small" controls-position="right" />

              </div>

            </el-form-item>

          </el-col>

        </el-row>

        <el-row :gutter="20">

          <el-col :span="12">

            <el-form-item label="指定租户" v-if="syncForm.scopeType !== 'ALL'">

              <el-select v-model="syncForm.tenantIds" multiple filterable placeholder="选择租户" style="width: 100%">

                <el-option v-for="t in tenantOptions" :key="t.id" :label="t.tenantName + ' (' + t.tenantCode + ')'" :value="t.id" />

              </el-select>

            </el-form-item>

          </el-col>

          <el-col :span="12">

            <el-form-item label="字典类型">

              <el-select v-model="syncForm.dictTypes" multiple filterable clearable placeholder="空 = 全部模板类型" style="width: 100%">

                <el-option v-for="t in templateTypeOptions" :key="t.dictType" :label="t.dictName + ' / ' + t.dictType" :value="t.dictType" />

              </el-select>

            </el-form-item>

          </el-col>

        </el-row>

        <el-alert

          v-if="syncForm.syncMode === 'OVERWRITE'"

          type="warning"

          :closable="false"

          show-icon

          class="mb12"

          title="OVERWRITE 将覆盖各租户中「平台管控」的字典项，租户自有扩展项不受影响"

        />

        <el-form-item v-if="syncForm.syncMode === 'OVERWRITE'" class="overwrite-check">

          <el-checkbox v-model="syncForm.overwriteConfirm">我已了解上述风险，确认执行覆盖同步</el-checkbox>

        </el-form-item>

        <el-form-item class="action-row">

          <el-button type="info" plain icon="el-icon-view" @click="handleSync(true)" v-hasPermi="['tenant:dict:sync']">全量预览</el-button>

          <el-button type="warning" icon="el-icon-upload2" @click="handleSync(false)" v-hasPermi="['tenant:dict:sync']">开始同步</el-button>

        </el-form-item>

      </el-form>

    </el-card>



    <el-card shadow="never" v-if="previewList.length" class="mb16 result-card">

      <div slot="header" class="card-header">

        <span><i class="el-icon-data-analysis" /> 预览结果</span>

        <el-tag size="mini" type="info" effect="plain">未写入数据库</el-tag>

      </div>

      <el-table :data="previewList" border size="small" class="dict-table">

        <el-table-column label="租户" prop="tenantName" min-width="120" />

        <el-table-column label="编码" prop="tenantCode" width="110" />

        <el-table-column label="新增类型" prop="typeAdded" width="88" align="center">

          <template slot-scope="s"><span class="stat-num add">{{ s.row.typeAdded || 0 }}</span></template>

        </el-table-column>

        <el-table-column label="更新类型" prop="typeUpdated" width="88" align="center">

          <template slot-scope="s"><span class="stat-num update">{{ s.row.typeUpdated || 0 }}</span></template>

        </el-table-column>

        <el-table-column label="新增数据" prop="dataAdded" width="88" align="center">

          <template slot-scope="s"><span class="stat-num add">{{ s.row.dataAdded || 0 }}</span></template>

        </el-table-column>

        <el-table-column label="更新数据" prop="dataUpdated" width="88" align="center">

          <template slot-scope="s"><span class="stat-num update">{{ s.row.dataUpdated || 0 }}</span></template>

        </el-table-column>

        <el-table-column label="跳过" prop="dataSkipped" width="72" align="center" />

        <el-table-column label="移除" prop="dataRemoved" width="72" align="center" />

      </el-table>

    </el-card>



    <el-card shadow="never" v-if="taskNo" class="result-card">

      <div slot="header" class="card-header">

        <span><i class="el-icon-loading" v-if="taskInfo && taskInfo.status === 'RUNNING'" /><i class="el-icon-finished" v-else /> 同步任务 {{ taskNo }}</span>

        <el-button type="text" icon="el-icon-refresh" @click="pollTask">刷新状态</el-button>

      </div>

      <el-descriptions :column="4" border size="small" v-if="taskInfo" class="task-summary">

        <el-descriptions-item label="状态"><el-tag :type="taskStatusType" size="small">{{ taskInfo.status }}</el-tag></el-descriptions-item>

        <el-descriptions-item label="模式">{{ taskInfo.syncMode }}</el-descriptions-item>

        <el-descriptions-item label="成功 / 总数">{{ taskInfo.successTenants }} / {{ taskInfo.totalTenants }}</el-descriptions-item>

        <el-descriptions-item label="失败">

          <span :class="{ 'stat-fail': taskInfo.failedTenants > 0 }">{{ taskInfo.failedTenants }}</span>

        </el-descriptions-item>

      </el-descriptions>

      <el-table v-if="taskInfo && taskInfo.details && taskInfo.details.length" :data="taskInfo.details" border size="small" class="dict-table mt12">

        <el-table-column label="租户" prop="tenantName" min-width="120" />

        <el-table-column label="状态" prop="status" width="90" align="center">

          <template slot-scope="s">

            <el-tag size="mini" :type="s.row.status === 'SUCCESS' ? 'success' : (s.row.status === 'FAILED' ? 'danger' : 'warning')">{{ s.row.status }}</el-tag>

          </template>

        </el-table-column>

        <el-table-column label="新增类型" prop="typeAdded" width="88" align="center" />

        <el-table-column label="更新类型" prop="typeUpdated" width="88" align="center" />

        <el-table-column label="新增数据" prop="dataAdded" width="88" align="center" />

        <el-table-column label="更新数据" prop="dataUpdated" width="88" align="center" />

        <el-table-column label="跳过" prop="dataSkipped" width="72" align="center" />

        <el-table-column label="错误" prop="errorMsg" min-width="160" :show-overflow-tooltip="true" />

      </el-table>

    </el-card>



    <el-empty v-if="!previewList.length && !taskNo" description="配置同步参数后，可先「全量预览」查看差异，再「开始同步」" :image-size="100" />

  </div>

</template>



<script>

import { listCompanyOptions } from '@/api/admin/sysCompany'

import { runDictSync, getDictSyncTask, previewDictSync, templateDictTypeOptions } from '@/api/tenant/dict'



export default {

  name: 'AdminTenantDictSync',

  data() {

    return {

      tenantOptions: [],

      templateTypeOptions: [],

      previewList: [],

      taskNo: '',

      taskInfo: null,

      pollTimer: null,

      syncForm: {

        syncMode: 'MERGE',

        scopeType: 'ALL',

        tenantIds: [],

        dictTypes: [],

        parallel: false,

        threads: 4,

        overwriteConfirm: false,

        dryRun: false

      }

    }

  },

  computed: {

    taskStatusType() {

      const s = this.taskInfo && this.taskInfo.status

      if (s === 'SUCCESS') return 'success'

      if (s === 'FAILED') return 'danger'

      if (s === 'PARTIAL') return 'warning'

      return 'info'

    }

  },

  created() {

    listCompanyOptions().then(r => { this.tenantOptions = r.data || [] })

    templateDictTypeOptions().then(r => {

      this.templateTypeOptions = r.data || []

      if (!this.templateTypeOptions.length) {

        this.$message.warning('平台模板为空，请先在「平台模板」Tab 维护字典模板后再同步')

      }

    })

  },

  beforeDestroy() {

    if (this.pollTimer) clearInterval(this.pollTimer)

  },

  methods: {

    buildPayload(dryRun) {

      return {

        syncMode: this.syncForm.syncMode,

        scopeType: this.syncForm.scopeType,

        tenantIds: this.syncForm.scopeType === 'ALL' ? [] : this.syncForm.tenantIds,

        dictTypes: this.syncForm.dictTypes,

        parallel: this.syncForm.parallel,

        threads: this.syncForm.threads,

        overwriteConfirm: this.syncForm.overwriteConfirm,

        dryRun: dryRun

      }

    },

    handlePreview() {

      if (this.syncForm.scopeType === 'INCLUDE' && (!this.syncForm.tenantIds || !this.syncForm.tenantIds.length)) {

        this.$message.warning('请选择要预览的租户')

        return

      }

      const tenantId = this.syncForm.scopeType === 'INCLUDE' ? this.syncForm.tenantIds[0] : (this.tenantOptions[0] && this.tenantOptions[0].id)

      if (!tenantId) {

        this.$message.warning('无可用租户')

        return

      }

      previewDictSync(tenantId, this.syncForm.dictTypes).then(r => {

        this.previewList = r.data || []

        this.$message.success('预览完成（仅首个租户示例，完整预览请用 dryRun 同步）')

      })

    },

    handleSync(dryRun) {

      if (this.syncForm.syncMode === 'OVERWRITE' && !this.syncForm.overwriteConfirm) {

        this.$message.error('OVERWRITE 模式请先勾选确认')

        return

      }

      if (this.syncForm.scopeType !== 'ALL' && (!this.syncForm.tenantIds || !this.syncForm.tenantIds.length)) {

        this.$message.warning('请选择租户')

        return

      }

      this.$confirm(dryRun ? '确认预览同步差异？' : '确认开始字典同步任务？', '提示', { type: 'warning' }).then(() => {

        return runDictSync(this.buildPayload(dryRun))

      }).then(r => {

        const data = r.data || {}

        if (data.dryRun) {

          this.previewList = data.preview || []

          if (!this.previewList.length) {

            this.$message.warning('预览完成：无差异（请确认平台模板中已有字典类型和数据）')

          } else {

            this.$message.success('全量预览完成，共 ' + this.previewList.length + ' 个租户')

          }

          return

        }

        if (!data.taskNo) {

          this.$message.error('同步任务创建失败，请检查后端日志')

          return

        }

        this.taskNo = data.taskNo

        this.taskInfo = { status: 'RUNNING', syncMode: data.syncMode, totalTenants: data.totalTenants, successTenants: 0, failedTenants: 0, details: [] }

        this.startPoll()

        this.$message.success('同步任务已启动：' + data.taskNo)

      }).catch(err => {

        const msg = (err && err.message) || (typeof err === 'string' ? err : '同步失败')

        if (msg && msg !== 'cancel') this.$message.error(msg)

      })

    },

    startPoll() {

      if (this.pollTimer) clearInterval(this.pollTimer)

      this.pollTask()

      this.pollTimer = setInterval(() => this.pollTask(), 3000)

    },

    pollTask() {

      if (!this.taskNo) return

      getDictSyncTask(this.taskNo).then(r => {

        this.taskInfo = r.data

        if (this.taskInfo && ['SUCCESS', 'FAILED', 'PARTIAL'].includes(this.taskInfo.status)) {

          clearInterval(this.pollTimer)

          this.pollTimer = null

        }

      })

    }

  }

}

</script>



<style scoped>

.sync-panel { padding-top: 4px; }

.mb12 { margin-bottom: 12px; }

.mb16 { margin-bottom: 16px; }

.mt12 { margin-top: 12px; }

.dict-table { width: 100%; }

.card-header {

  display: flex;

  align-items: center;

  justify-content: space-between;

  font-weight: 600;

  color: #303133;

}

.card-header i { margin-right: 6px; color: #409eff; }

.sync-mode-card {

  padding: 14px 16px;

  border-radius: 8px;

  border: 1px solid #ebeef5;

  background: #fff;

  height: 100%;

  transition: box-shadow 0.2s;

}

.sync-mode-card:hover { box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); }

.sync-mode-title { font-size: 14px; font-weight: 600; margin-bottom: 6px; }

.sync-mode-desc { font-size: 12px; color: #909399; line-height: 1.5; }

.sync-mode-card.merge { border-left: 3px solid #67c23a; }

.sync-mode-card.merge .sync-mode-title { color: #67c23a; }

.sync-mode-card.append { border-left: 3px solid #409eff; }

.sync-mode-card.append .sync-mode-title { color: #409eff; }

.sync-mode-card.overwrite { border-left: 3px solid #e6a23c; }

.sync-mode-card.overwrite .sync-mode-title { color: #e6a23c; }

.sync-config-card >>> .el-card__body { padding-top: 8px; }

.parallel-row { display: flex; align-items: center; gap: 12px; }

.overwrite-check { margin-bottom: 0; }

.action-row { margin-bottom: 0; margin-top: 4px; }

.stat-num { font-weight: 600; }

.stat-num.add { color: #67c23a; }

.stat-num.update { color: #409eff; }

.stat-fail { color: #f56c6c; font-weight: 600; }

.task-summary { margin-bottom: 0; }

</style>


