<template>
  <div class="auto-promo-container" v-loading="loading" :element-loading-text="$t('autoPromotion.loading')">
    <div class="page-header">
      <el-breadcrumb separator="/">
        <el-breadcrumb-item>
          <i class="el-icon-s-promotion"></i> {{ $t('autoPromotion.title') }}
      <el-button
        type="text"
        icon="el-icon-refresh"
        class="auto-promo-refresh-btn"
        :disabled="loading"
        :title="$t('autoPromotion.refresh')"
        @click="refreshAll"
      />
                </el-breadcrumb-item>
            </el-breadcrumb>
            <div class="page-header-actions">
                <el-button type="warning" plain size="small" @click="openFactoryBatchImportDialog" style="display: none;">
                    {{ $t('autoPromotion.factoryBatchImportBtn') }}
                </el-button>
                <el-button
                    type="primary"
                    size="small"
                    @click="openFactoryTaskDialogForJournal(null)"
                >
                    {{ $t('autoPromotion.factoryCreateBtn') }}
                </el-button>
            </div>
    </div>

        <div class="journal-list-wrapper">
            <div v-for="journal in allJournals" :key="journal.journal_id" class="journal-group-section">
                <div class="journal-main-header">
                    <div class="journal-brand">
                        <div class="brand-icon">
              <i class="el-icon-notebook-2"></i>
            </div>
                        <h2 class="journal-title-text">{{ journal.title }}</h2>
          </div>
        </div>

                <div v-if="getJournalDisplayTasks(journal).length" class="cards-grid">
                    <div
                        v-for="taskCard in getJournalDisplayTasks(journal)"
                        :key="taskCard.cardKey"
                        :class="['stat-card', taskCard.enabled ? 'is-active' : 'is-stopped']"
                    >
                        <div class="card-inner-header">
                            <div class="card-label-area">
                                <div class="card-main-title">
                                    {{ taskCard.typeLabel }}
                                    <!-- <span v-if="taskCard.showCount" class="scene-task-count">
                                        ({{ taskCard.totalCount }})
                                    </span> -->
                <el-button
                                        v-if="taskCard.initialized"
                  type="text"
                  size="mini"
                                        icon="el-icon-edit"
                  class="config-inline-btn"
                                        @click="openFactoryTaskDialogForTask(journal, taskCard)"
                >
                  {{ $t('autoPromotion.editConfig') }}
                </el-button>
                                </div>
                                <div class="status-tag">
                                    <span class="dot"></span>
                                    {{ taskCard.enabled ? $t('autoPromotion.running') : $t('autoPromotion.stopped') }}
                                </div>
              </div>
              <el-switch 
                                :value="taskCard.enabled"
                size="small" 
                                active-color="#3DBB6A"
                                :disabled="!taskCard.taskId || !!taskCard.switchLoading"
                                @change="onFactoryTaskSwitchChange(journal, taskCard, $event)"
              />
            </div>

                        <div class="card-content">
                            <div class="template-preview-box">
                                <div class="meta-row">
                                    <i class="el-icon-message"
                                    ><span style="font-size: 11px; margin-left: 3px; margin-right: 3px">Template&nbsp;:</span></i
                                >
                                <span class="tpl-name tpl-name-single-line">{{ taskCard.templateName || 'No Template Configured' }}</span>
                </div>
                                <div class="meta-row">
                                    <i class="el-icon-user"
                                    ><span style="font-size: 11px; margin-left: 3px; margin-right: 3px">Type&nbsp;:</span></i
                                >
                                <span class="tpl-name">{{ taskCard.expertTypeLabel || '-' }}</span>
                  </div>
                                
                                <!-- <div class="meta-row">
                                    <i class="el-icon-collection-tag"
                                    ><span style="font-size: 11px; margin-left: 3px; margin-right: 3px">Promotion Fields&nbsp;:</span></i
                                >
                                <span class="tpl-name">{{ taskCard.fieldCountText }}</span>
                                </div> -->
                               
                                <div class="meta-row" v-if="String(taskCard.expertType || '') === '5'">
                                    <i class="el-icon-location-outline"
                                    ><span style="font-size: 11px; margin-left: 3px; margin-right: 3px">Country&nbsp;:</span></i
                                >
                                <span class="tpl-name">{{ taskCard.countryScopeLabel || '-' }}</span>
                </div>
                               
              </div>

                            <!-- <div class="stats-container">
                                <div class="stat-box">
                                    <span class="stat-label">SENT</span>
                                    <span class="stat-value"><i class="el-icon-circle-check"></i> 1,240</span>
                                </div>
                                <div class="stat-box">
                                    <span class="stat-label">PENDING</span>
                                    <span class="stat-value warning"><i class="el-icon-time"></i> 150</span>
                                </div>
                            </div> -->
            </div>

                        <div class="card-actions">
                           
                            <button class="action-main-btn" @click="handleFactoryTaskAction(journal, taskCard)">
                                <span
                                    v-if="
                                        promotionUpdating &&
                                        String(promotionUpdatingJournalId) === String(journal.journal_id) &&
                                        String(promotionUpdatingTaskId) === String(taskCard.taskId || '')
                                    "
                                >
                                    <i class="el-icon-loading"></i>
                </span>
                                <span v-else>{{ getFactoryTaskActionText(taskCard) }}</span>
              </button>
                            <div class="task-card-footer-row">
                                <button
                                    v-if="!taskCard.enabled && taskCard.taskId"
                                    type="button"
                                    class="promo-history-link"
                                    @click.stop="openDetail(journal, taskCard)"
                                >
                                    <span>{{ $t('autoPromotion.logs') }}</span>
                                    <i class="el-icon-document"></i>
                                </button>
                                <span v-else class="promo-history-spacer"></span>
                                <div v-if="taskCard.createdAtText" class="task-create-time">
                                    {{ $t('autoPromotion.createdAt') }}: {{ taskCard.createdAtText }}
            </div>
          </div>
                        </div>
                    </div>
                </div>
                <div v-else class="no-task-tip">
                    <span>{{ $t('autoPromotion.noFactoryTask') }}</span>
                    <el-button type="text" size="mini" class="no-task-create-btn" @click="openFactoryTaskDialogForJournal(journal)">
                        <i class="el-icon-plus"></i>
                        {{ $t('autoPromotion.factoryCreateNow') }}
                    </el-button>
        </div>
      </div>
    </div>

    <journal-detail-dialog v-if="detailVisible" :visible.sync="detailVisible" :journal="currentJournal" />

    <auto-promotion-wizard
      mode="dialog"
      :visible.sync="showWizardDialog"
      :config="wizardConfig"
      :wizardStartDate.sync="wizardStartDate"
      :selectedFieldIds.sync="selectedFieldIds"
            :selectedCountryIds.sync="selectedCountryIds"
      :availableFields="availableFields"
            :availableCountries="availableCountries"
      :fieldsLoading="fieldsLoading"
      :fieldsSaving="fieldsSaving"
      :currentJournalName="wizardJournal ? wizardJournal.title : ''"
      :selectedTemplateThumbHtml="selectedTemplateThumbHtml"
      :selectedTemplateName="selectedTemplateName"
      :selectedStyleName="selectedStyleName"
      :saving="saving"
      :title="`${$t('autoPromotion.journalManage')}: ${wizardJournal ? wizardJournal.title : ''}`"
      @open-template-selector="openTemplateSelector"
      @confirm-fields="savePromotionFieldsNow"
            @confirm-countries="savePromotionCountriesNow"
      @cancel="showWizardDialog = false"
      @confirm="saveWizardConfig"
    />

    <template-selector-dialog
      v-if="showTemplateDialog"
      :visible.sync="showTemplateDialog"
      :journalId="wizardJournal ? wizardJournal.journal_id : ''"
      :journalLabel="wizardJournal ? wizardJournal.title : ''"
      :initial-style-id="templateDialogInitialStyleId"
      :initial-template-id="templateDialogInitialTemplateId"
      :return-source="'autoPromotion'"
      @confirm="handleTemplateApply"
      @close-all-dialogs="closeAllDialogs"
    />

        <promotion-factory-task-dialog
            :visible.sync="showFactoryTaskDialog"
            :initial-journal-id="factoryDialogInitialJournalId"
            :initial-task="factoryDialogInitialTask"
            :journal-options="allJournals"
            @success="refreshAll"
        />

        <el-dialog
            :title="$t('autoPromotion.factoryBatchImportTitle')"
            :visible.sync="batchImportVisible"
            width="1200px"
            append-to-body
            :close-on-click-modal="false"
            custom-class="factory-batch-import-dialog"
            @closed="onFactoryBatchImportDialogClosed"
        >
            <div class="batch-import-layout">
                <aside class="batch-import-journal-rail" v-loading="batchImportJournalPickerLoading">
                    <div class="batch-import-rail-title">{{ $t('autoPromotion.factoryBatchImportJournalPick') }}</div>
                    <div class="batch-import-journal-scroll">
                        <div v-if="!batchImportJournalPickerLoading && !batchImportJournalPickerList.length" class="batch-import-journal-empty">
                            {{ $t('autoPromotion.factoryBatchImportJournalEmpty') }}
                        </div>
                        <div v-else class="batch-import-journal-grid batch-import-journal-grid--rail">
                        <div
                            v-for="j in batchImportJournalPickerList"
                            :key="'batch_j_' + j.journal_id"
                            class="batch-import-journal-card"
                            :class="{ 'is-active': String(batchImportJournalId) === String(j.journal_id) }"
                            @click="onBatchImportJournalCardClick(j)"
                        >
                            <div class="batch-import-mini-cover-wrapper">
                                <el-image :src="j.journal_icon" fit="cover" class="batch-import-mini-cover">
                                    <div slot="error" class="batch-import-image-slot"><i class="el-icon-picture-outline"></i></div>
                                </el-image>
                                <div v-if="String(batchImportJournalId) === String(j.journal_id)" class="batch-import-mini-badge">
                                    <i class="el-icon-check"></i>
                                </div>
                            </div>
                            <div
                                class="batch-import-mini-abbr"
                                :class="{ 'is-active': String(batchImportJournalId) === String(j.journal_id) }"
                            >
                                {{ j.abbr || j.title }}
                            </div>
                        </div>
                    </div>
                    </div>
                    <div class="batch-import-journal-id-row batch-import-journal-id-row--rail">
                        <span class="batch-import-id-label">{{ $t('autoPromotion.factoryBatchImportJournalId') }}</span>
                        <el-input
                            v-model="batchImportJournalId"
                            clearable
                            size="small"
                            class="batch-import-journal-id-input"
                            :placeholder="$t('autoPromotion.factoryBatchImportJournalManualPlaceholder')"
                            @input="onBatchImportJournalIdInput"
                        />
                    </div>
                </aside>
                <div class="batch-import-main">
                    <p class="batch-import-hint-compact">{{ $t('autoPromotion.factoryBatchImportHintShort') }}</p>
            <el-row :gutter="12" class="batch-import-common-fields">
                <el-col :span="12">
                    <div class="batch-import-field-label">{{ $t('autoPromotion.factoryBatchImportTemplateId') }}</div>
                    <el-input v-model="batchImportTemplateId" clearable size="small" :placeholder="$t('autoPromotion.factoryBatchImportTemplatePlaceholder')" />
                </el-col>
                <el-col :span="12">
                    <div class="batch-import-field-label">{{ $t('autoPromotion.factoryBatchImportStyleId') }}</div>
                    <el-input v-model="batchImportStyleId" clearable size="small" :placeholder="$t('autoPromotion.factoryBatchImportStylePlaceholder')" />
                </el-col>
            </el-row>
            <div class="batch-import-fetch-block">
                <div class="batch-import-field-row-head">
                    <span class="batch-import-field-label">{{ $t('autoPromotion.factoryBatchImportFetchIdsLabel') }}</span>
                    <span class="batch-import-fetch-count">{{ $t('autoPromotion.selectedCount', { count: batchImportSelectedFieldIds.length }) }}</span>
                    <el-button
                        type="primary"
                        plain
                        size="mini"
                        icon="el-icon-refresh"
                        :disabled="!String(batchImportJournalId || '').trim()"
                        :loading="batchImportFieldsLoading"
                        @click="loadBatchImportAvailableFields"
                    >
                        {{ $t('autoPromotion.factoryBatchImportLoadFields') }}
                    </el-button>
                </div>
                <div v-if="batchImportAvailableFields.length" class="batch-import-field-toolbar">
                    <el-input
                        v-model="batchImportFieldSearchText"
                        size="small"
                        clearable
                        class="batch-import-field-search"
                        prefix-icon="el-icon-search"
                        :placeholder="$t('autoPromotion.fieldSearchPlaceholder')"
                    />
                    <el-button size="mini" @click="selectBatchImportFieldsBulk">{{ $t('autoPromotion.selectAll') }}</el-button>
                    <el-button size="mini" @click="clearBatchImportFieldSelection">{{ $t('autoPromotion.clearAll') }}</el-button>
                </div>
                <div v-if="batchImportAvailableFields.length" class="batch-import-field-checkbox-wrap">
                    <el-checkbox-group v-model="batchImportSelectedFieldIds" @change="onBatchImportFieldIdsGroupChange">
                        <el-checkbox v-for="f in batchImportFilteredFields" :key="'bf_' + f.id" :label="f.id">
                            {{ f.label }} ({{ f.id }})
                        </el-checkbox>
                    </el-checkbox-group>
                    <div
                        v-if="String(batchImportFieldSearchText || '').trim() && !batchImportFilteredFields.length"
                        class="batch-import-field-empty"
                    >
                        {{ $t('autoPromotion.noFieldMatch') }}
                    </div>
                </div>
                <div class="batch-import-field-label batch-import-fetch-text-label">{{ $t('autoPromotion.factoryBatchImportFetchIdsManual') }}</div>
                <el-input
                    v-model="batchImportFetchIdsOverride"
                    type="textarea"
                    :rows="2"
                    size="small"
                    class="batch-import-fetch-textarea"
                    :placeholder="$t('autoPromotion.factoryBatchImportFetchIdsPlaceholder')"
                    @blur="syncBatchImportSelectionFromFetchIdsString"
                />
            </div>
            <el-table
                v-if="batchImportAccounts.length"
                :data="batchImportAccounts"
                :show-header="false"
                border
                size="mini"
                class="batch-import-accounts-table"
                max-height="220"
            >
                <el-table-column prop="j_email_id" :label="$t('autoPromotion.factoryBatchImportColEmailId')" width="100" />
                <el-table-column :label="$t('autoPromotion.factoryBatchImportColAddress')" min-width="220">
                    <template slot-scope="scope">{{ batchImportAccountDisplay(scope.row) }}</template>
                </el-table-column>
            </el-table>
            <div class="batch-import-json-toolbar">
                <el-button size="small" type="primary" plain @click="syncBatchImportUiToJson">{{
                    $t('autoPromotion.factoryBatchImportSyncToJson')
                }}</el-button>
                <el-button size="small" @click="syncBatchImportJsonToUiFirst">{{ $t('autoPromotion.factoryBatchImportSyncFromJson') }}</el-button>
                <el-checkbox v-model="batchImportSyncIncludeEmails" class="batch-import-sync-email-check">{{
                    $t('autoPromotion.factoryBatchImportSyncIncludeEmails')
                }}</el-checkbox>
            </div>
            <el-input
                v-model="batchImportText"
                type="textarea"
                :rows="14"
                class="batch-import-textarea"
                spellcheck="false"
            />
                </div>
            </div>
            <span slot="footer" class="dialog-footer">
                <el-button size="small" @click="batchImportVisible = false">{{ $t('autoPromotion.cancel') }}</el-button>
                <el-button type="primary" size="small" :loading="batchImporting" @click="runPromotionFactoryBatchImport">{{
                    $t('autoPromotion.factoryBatchImportRun')
                }}</el-button>
            </span>
        </el-dialog>
  </div>
</template>

<script>
import JournalDetailDialog from './components/autoPromotion/JournalDetailDialog.vue';
import AutoPromotionWizard from './components/autoPromotion/AutoPromotionWizard.vue';
import PromotionFactoryTaskDialog from './components/autoPromotion/PromotionFactoryTaskDialog.vue';
import TemplateSelectorDialog from '@/components/page/components/email/TemplateSelectorDialog.vue';

export default {
    components: { JournalDetailDialog, AutoPromotionWizard, PromotionFactoryTaskDialog, TemplateSelectorDialog },
  data() {
    return {
      loading: true,
      detailVisible: false,
      currentJournal: null,
      showWizardDialog: false,
      showTemplateDialog: false,
      saving: false,
      promotionUpdating: false,
      promotionUpdatingJournalId: '',
            promotionUpdatingTaskId: '',
      wizardJournal: null,
      wizardStartDate: '',
      wizardConfig: {
        defaultTemplateId: '',
        defaultStyleId: '',
        enabled: false
      },
      selectedTemplateThumbHtml: '',
      selectedTemplateName: '',
      selectedStyleName: '',
      selectedFieldIds: [],
            selectedCountryIds: [],
      availableFields: [],
            availableCountries: [],
      fieldsLoading: false,
      fieldsSaving: false,
      templateDialogInitialStyleId: '',
      templateDialogInitialTemplateId: '',
      templateNameMap: {},
            factoryTemplateNameMap: {},
            allJournals: [],
            showFactoryTaskDialog: false,
            factoryDialogInitialJournalId: '',
            factoryDialogInitialTask: null,
            /** 临时：JSON 数组批量创建 promotion_factory，与图二提交字段一致 */
            batchImportVisible: false,
            batchImportText: '',
            batchImporting: false,
            /** 批量创建：与 JSON 合并进接口（非空则覆盖每条里的同名字段） */
            batchImportJournalId: '',
            batchImportTemplateId: '',
            batchImportStyleId: '',
            /** 批量弹窗：按期刊拉取邮箱账号（api/email_client/getAccounts） */
            batchImportAccounts: [],
            batchImportAccountsLoading: false,
            /** 批量弹窗：期刊封面选择（api/Journal/getAllJournal） */
            batchImportJournalPickerList: [],
            batchImportJournalPickerLoading: false,
            /** 推广领域 fetch_ids：勾选 + 文本合并覆盖每条 JSON */
            batchImportAvailableFields: [],
            batchImportSelectedFieldIds: [],
            batchImportFetchIdsOverride: '',
            batchImportFieldsLoading: false,
            batchImportFieldSearchText: '',
            _batchImportAccountsFetchTimer: null,
            /** 写入 JSON 时是否用当前已加载邮箱列表覆盖每条 email_ids */
            batchImportSyncIncludeEmails: true
        };
  },
  created() {
    this.fetchPromotionJournals();
  },
  computed: {
        /** 推广领域列表：按名称 / ID 关键字筛选（支持空格、逗号多关键词 OR） */
        batchImportFilteredFields() {
            const raw = String(this.batchImportFieldSearchText || '').trim();
            const list = this.batchImportAvailableFields || [];
            if (!raw) return list;
            const tokens = raw
                .split(/[,，\s]+/)
                .map((t) => t.trim().toLowerCase())
                .filter(Boolean);
            if (!tokens.length) return list;
            return list.filter((f) => {
                const label = String(f.label || '').toLowerCase();
                const id = String(f.id || '').toLowerCase();
                return tokens.some((t) => label.indexOf(t) !== -1 || id.indexOf(t) !== -1);
            });
        }
  },
  watch: {
        batchImportJournalId() {
            this.scheduleBatchImportAccountsFetch();
        },
        batchImportVisible(val) {
            if (val) {
                this.$nextTick(() => {
                    this.scheduleBatchImportAccountsFetch();
                });
            } else {
                this.clearBatchImportAccountsFetchTimer();
            }
        }
    },
  methods: {
        async savePromotionCountriesNow() {
            if (!this.wizardJournal || !this.wizardJournal.journal_id) return;
            await this.$api.post(
                'api/email_client/setJournalPromotionFields',
                this.journalPromotionFieldsPayload(this.wizardJournal.journal_id)
            );
            this.$message.success(this.$t('autoPromotion.countriesSaved'));
        },
        openFactoryBatchImportDialog() {
            if (!this.batchImportText || !String(this.batchImportText).trim()) {
                this.batchImportText = this.defaultFactoryBatchImportSample();
            }
            this.batchImportVisible = true;
            this.loadBatchImportJournalPicker();
        },
        async loadBatchImportJournalPicker() {
            this.batchImportJournalPickerLoading = true;
            try {
                const res = await this.$api.post('api/Journal/getAllJournal', {});
                let raw = [];
                if (res && Number(res.code) === 0) {
                    if (res.data && Array.isArray(res.data.journals)) raw = res.data.journals;
                    else if (Array.isArray(res.data)) raw = res.data;
                }
                let mapped = (Array.isArray(raw) ? raw : []).map((j) => ({
                    journal_id: j.journal_id != null ? j.journal_id : j.id,
                    title: j.title || j.name || '',
                    abbr: j.abbr || j.short_name || j.abbreviation || j.code || '',
                    journal_icon: j.journal_icon || j.cover_url || j.cover || j.icon || ''
                }));
                if (!mapped.length && Array.isArray(this.allJournals) && this.allJournals.length) {
                    mapped = this.allJournals.map((j) => ({
                        journal_id: j.journal_id,
                        title: j.title || '',
                        abbr: j.abbr || '',
                        journal_icon: j.journal_icon || ''
                    }));
                }
                this.batchImportJournalPickerList = mapped;
                const cur = String(this.batchImportJournalId || '').trim();
                if (!cur && mapped.length) {
                    this.batchImportJournalId = String(mapped[0].journal_id);
                }
            } catch (e) {
                console.error(e);
                this.batchImportJournalPickerList = [];
            } finally {
                this.batchImportJournalPickerLoading = false;
            }
        },
        onBatchImportJournalCardClick(j) {
            if (!j || j.journal_id == null) return;
            const id = String(j.journal_id);
            if (id === String(this.batchImportJournalId || '')) return;
            this.batchImportJournalId = id;
            this.batchImportAccounts = [];
            this.clearBatchImportFieldPicker();
        },
        onBatchImportJournalIdInput() {
            this.batchImportAccounts = [];
            this.clearBatchImportFieldPicker();
        },
        clearBatchImportFieldPicker() {
            this.batchImportAvailableFields = [];
            this.batchImportSelectedFieldIds = [];
            this.batchImportFetchIdsOverride = '';
            this.batchImportFieldSearchText = '';
        },
        async loadBatchImportAvailableFields() {
            const jid = String(this.batchImportJournalId || '').trim();
            if (!jid) {
                this.$message.warning(this.$t('autoPromotion.factoryBatchImportNeedJournalForFields'));
                return;
            }
            this.batchImportFieldsLoading = true;
            try {
                const res = await this.$api.post('api/email_client/getAvailableFields', { journal_id: jid });
                const availableArr = this.findArray(res.data || res) || [];
                this.batchImportAvailableFields = availableArr.map((item, idx) => ({
                    id: String(item.expert_fetch_id || item.fetch_id || item.id || idx),
                    label: item.field || item.title || item.name || 'Field'
                }));
                this.syncBatchImportSelectionFromFetchIdsString();
            } catch (e) {
                console.error(e);
                this.batchImportAvailableFields = [];
            } finally {
                this.batchImportFieldsLoading = false;
            }
        },
        onBatchImportFieldIdsGroupChange() {
            this._syncBatchImportFetchIdsStringFromSelected();
        },
        _syncBatchImportFetchIdsStringFromSelected() {
            const order = (this.batchImportAvailableFields || []).map((f) => String(f.id));
            const set = new Set((this.batchImportSelectedFieldIds || []).map(String));
            const ordered = order.filter((id) => set.has(id));
            const extra = (this.batchImportSelectedFieldIds || []).map(String).filter((id) => order.indexOf(id) === -1);
            this.batchImportFetchIdsOverride = [...ordered, ...extra].join(',');
        },
        syncBatchImportSelectionFromFetchIdsString() {
            const raw = String(this.batchImportFetchIdsOverride || '');
            const parts = raw.split(/[,，\s]+/).map((s) => s.trim()).filter(Boolean);
            if (!this.batchImportAvailableFields.length) return;
            const availIds = new Set((this.batchImportAvailableFields || []).map((f) => String(f.id)));
            this.batchImportSelectedFieldIds = parts.filter((id) => availIds.has(String(id)));
        },
        /** 有搜索时全选当前筛选结果，无搜索时全选全部（与创建任务里「选择领域」一致） */
        selectBatchImportFieldsBulk() {
            const hasSearch = String(this.batchImportFieldSearchText || '').trim();
            const source = hasSearch ? this.batchImportFilteredFields || [] : this.batchImportAvailableFields || [];
            const set = new Set((this.batchImportSelectedFieldIds || []).map(String));
            source.forEach((f) => set.add(String(f.id)));
            this.batchImportSelectedFieldIds = Array.from(set);
            this._syncBatchImportFetchIdsStringFromSelected();
        },
        clearBatchImportFieldSelection() {
            this.batchImportSelectedFieldIds = [];
            this.batchImportFetchIdsOverride = '';
        },
        onFactoryBatchImportDialogClosed() {
            this.batchImporting = false;
            this.batchImportAccounts = [];
            this.batchImportAccountsLoading = false;
            this.batchImportJournalPickerList = [];
            this.clearBatchImportAccountsFetchTimer();
            this.clearBatchImportFieldPicker();
        },
        clearBatchImportAccountsFetchTimer() {
            if (this._batchImportAccountsFetchTimer) {
                clearTimeout(this._batchImportAccountsFetchTimer);
                this._batchImportAccountsFetchTimer = null;
            }
        },
        /** 期刊 ID 变化或弹窗打开后，防抖自动拉取邮箱账号 */
        scheduleBatchImportAccountsFetch() {
            this.clearBatchImportAccountsFetchTimer();
            this._batchImportAccountsFetchTimer = setTimeout(() => {
                this._batchImportAccountsFetchTimer = null;
                if (!this.batchImportVisible) return;
                const jid = String(this.batchImportJournalId || '').trim();
                if (!jid) {
                    this.batchImportAccounts = [];
                    return;
                }
                this.fetchBatchImportAccounts(true);
            }, 400);
        },
        async fetchBatchImportAccounts(silentEmpty) {
            const jid = String(this.batchImportJournalId || '').trim();
            if (!jid) {
                if (!silentEmpty) {
                    this.$message.warning(this.$t('autoPromotion.factoryBatchImportNeedJournalForAccounts'));
                }
                this.batchImportAccounts = [];
                return;
            }
            this.batchImportAccountsLoading = true;
            try {
                const res = await this.$api.post('api/email_client/getAccounts', { journal_id: jid });
                let list = [];
                if (res && Number(res.code) === 0) {
                    if (Array.isArray(res.data)) list = res.data;
                    else if (res.data && Array.isArray(res.data.list)) list = res.data.list;
                }
                this.batchImportAccounts = list;
                if (!list.length && !silentEmpty) {
                    this.$message.info(this.$t('autoPromotion.factoryBatchImportNoAccounts'));
                }
            } catch (e) {
                console.error(e);
                this.batchImportAccounts = [];
                this.$message.error(this.$t('autoPromotion.factoryBatchImportAccountsFail'));
            } finally {
                this.batchImportAccountsLoading = false;
            }
        },
        batchImportAccountDisplay(acc) {
            if (!acc) return '-';
            return acc.smtp_user || acc.account || '-';
        },
        defaultFactoryBatchImportSample() {
            return (
                '[\n' +
                '  {\n' +
                '    "type": "1",\n' +
                '    "expert_type": "5",\n' +
                '    "email_ids": "12",\n' +
                '    "send_count": "1",\n' +
                '    "fetch_ids": "1,2",\n' +
                '    "target_partitions": "1,2",\n' +
                '    "target_country_ids": "",\n' +
                '    "start_promotion": "0"\n' +
                '  }\n' +
                ']\n'
            );
        },
        /** 将对话框顶部的期刊 / 模板 / 样式 ID 合并进单条 payload（输入框非空则覆盖 JSON） */
        applyBatchImportDialogOverrides(payload) {
            const jid = String(this.batchImportJournalId || '').trim();
            const tid = String(this.batchImportTemplateId || '').trim();
            const sid = String(this.batchImportStyleId || '').trim();
            const fetchOv = String(this.batchImportFetchIdsOverride || '').trim();
            if (jid) payload.journal_id = jid;
            if (tid) payload.template_id = tid;
            if (sid) payload.style_id = sid;
            if (fetchOv) payload.fetch_ids = fetchOv;
        },
        /** 与 PromotionFactoryTaskDialog.submitFactory 提交体一致；可选简写 zones / countries / email_id_list */
        normalizePromotionFactoryRow(row) {
            if (!row || typeof row !== 'object') return {};
            const o = { ...row };
            const partitionMap = { Partition1: '1', Partition2: '2', Partition3: '3' };
            const countryMap = { country_china: '239', country_india: '228' };
            if (Array.isArray(o.zones) && (o.target_partitions == null || String(o.target_partitions).trim() === '')) {
                o.target_partitions = o.zones
                    .map((z) => partitionMap[String(z)])
                    .filter(Boolean)
                    .join(',');
                delete o.zones;
            }
            if (Array.isArray(o.countries) && (o.target_country_ids == null || String(o.target_country_ids).trim() === '')) {
                o.target_country_ids = o.countries
                    .map((c) => countryMap[String(c)])
                    .filter(Boolean)
                    .join(',');
                delete o.countries;
            }
            if (Array.isArray(o.email_id_list)) {
                o.email_ids = o.email_id_list.map((id) => String(id)).join(',');
                delete o.email_id_list;
            }
            if (Array.isArray(o.email_ids)) {
                o.email_ids = o.email_ids.map((id) => String(id)).join(',');
            }
            const keys = [
                'journal_id',
                'type',
                'expert_type',
                'email_ids',
                'send_count',
                'template_id',
                'style_id',
                'fetch_ids',
                'target_partitions',
                'target_country_ids',
                'start_promotion'
            ];
            const out = {};
            keys.forEach((k) => {
                if (o[k] === undefined) return;
                out[k] = o[k] === null || o[k] === '' ? '' : String(o[k]);
            });
            if (out.start_promotion === undefined || out.start_promotion === '') out.start_promotion = '0';
            if (out.fetch_ids === undefined) out.fetch_ids = '';
            if (out.target_partitions === undefined) out.target_partitions = '';
            if (out.target_country_ids === undefined) out.target_country_ids = '';
            return out;
        },
        /** 将上方期刊 / 模板 / 样式 / 领域 /（可选）邮箱列表写入下方 JSON 数组的每一条 */
        syncBatchImportUiToJson() {
            let rows;
            try {
                rows = JSON.parse(this.batchImportText || '[]');
            } catch (e) {
                this.$message.error(this.$t('autoPromotion.factoryBatchImportBadJson'));
                return;
            }
            if (!Array.isArray(rows)) {
                this.$message.warning(this.$t('autoPromotion.factoryBatchImportEmpty'));
                return;
            }
            if (!rows.length) {
                rows = [
                    {
                        type: '1',
                        expert_type: '5',
                        email_ids: '',
                        send_count: '1',
                        fetch_ids: '',
                        target_partitions: '1,2',
                        target_country_ids: '',
                        start_promotion: '0'
                    }
                ];
            }
            const jid = String(this.batchImportJournalId || '').trim();
            const tid = String(this.batchImportTemplateId || '').trim();
            const sid = String(this.batchImportStyleId || '').trim();
            const fetchOv = String(this.batchImportFetchIdsOverride || '').trim();
            const includeEmails = this.batchImportSyncIncludeEmails !== false;
            const emailIdsFromAccounts =
                includeEmails && this.batchImportAccounts && this.batchImportAccounts.length
                    ? this.batchImportAccounts
                          .map((a) => (a && a.j_email_id != null ? String(a.j_email_id) : ''))
                          .filter(Boolean)
                          .join(',')
                    : null;
            const next = rows.map((row) => {
                const o = row && typeof row === 'object' ? { ...row } : {};
                if (jid) o.journal_id = jid;
                if (tid) o.template_id = tid;
                if (sid) o.style_id = sid;
                if (fetchOv) o.fetch_ids = fetchOv;
                if (emailIdsFromAccounts) o.email_ids = emailIdsFromAccounts;
                return o;
            });
            this.batchImportText = JSON.stringify(next, null, 2) + '\n';
            this.$message.success(this.$t('autoPromotion.factoryBatchImportJsonFromUiOk'));
        },
        /** 用下方 JSON 首条回显上方表单（并尝试加载领域勾选、触发邮箱拉取） */
        async syncBatchImportJsonToUiFirst() {
            let rows;
            try {
                rows = JSON.parse(this.batchImportText || '[]');
            } catch (e) {
                this.$message.error(this.$t('autoPromotion.factoryBatchImportBadJson'));
                return;
            }
            if (!Array.isArray(rows) || !rows.length) {
                this.$message.warning(this.$t('autoPromotion.factoryBatchImportEmpty'));
                return;
            }
            const first = rows[0] && typeof rows[0] === 'object' ? rows[0] : {};
            const jid =
                first.journal_id != null && String(first.journal_id).trim() !== ''
                    ? String(first.journal_id).trim()
                    : first.journalId != null && String(first.journalId).trim() !== ''
                      ? String(first.journalId).trim()
                      : '';
            if (jid) this.batchImportJournalId = jid;
            this.batchImportTemplateId =
                first.template_id != null && String(first.template_id).trim() !== '' ? String(first.template_id).trim() : '';
            this.batchImportStyleId =
                first.style_id != null && String(first.style_id).trim() !== '' ? String(first.style_id).trim() : '';
            this.batchImportFetchIdsOverride =
                first.fetch_ids != null && String(first.fetch_ids).trim() !== '' ? String(first.fetch_ids).trim() : '';
            if (jid) {
                await this.loadBatchImportAvailableFields();
            } else {
                this.batchImportAvailableFields = [];
                this.batchImportSelectedFieldIds = [];
            }
            this.syncBatchImportSelectionFromFetchIdsString();
            this.scheduleBatchImportAccountsFetch();
            this.$message.success(this.$t('autoPromotion.factoryBatchImportUiFromJsonOk'));
        },
        validatePromotionFactoryPayload(p, index) {
            const req = ['journal_id', 'type', 'expert_type', 'email_ids', 'send_count', 'template_id', 'style_id'];
            for (let i = 0; i < req.length; i++) {
                const k = req[i];
                if (p[k] == null || String(p[k]).trim() === '') {
                    return this.$t('autoPromotion.factoryBatchImportMissing', { index: index + 1, field: k });
                }
            }
            if (String(p.expert_type) === '5') {
                if (!String(p.fetch_ids || '').trim()) {
                    return this.$t('autoPromotion.factoryBatchImportNeedFetch', { index: index + 1 });
                }
                if (!String(p.target_partitions || '').trim() && !String(p.target_country_ids || '').trim()) {
                    return this.$t('autoPromotion.factoryBatchImportNeedZone', { index: index + 1 });
                }
            }
            return '';
        },
        async runPromotionFactoryBatchImport() {
            let rows;
            try {
                rows = JSON.parse(this.batchImportText || '[]');
            } catch (e) {
                this.$message.error(this.$t('autoPromotion.factoryBatchImportBadJson'));
                return;
            }
            if (!Array.isArray(rows) || !rows.length) {
                this.$message.warning(this.$t('autoPromotion.factoryBatchImportEmpty'));
                return;
            }
            this.batchImporting = true;
            let ok = 0;
            let fail = 0;
            const errLines = [];
            try {
                for (let i = 0; i < rows.length; i++) {
                    const payload = this.normalizePromotionFactoryRow(rows[i]);
                    this.applyBatchImportDialogOverrides(payload);
                    const ve = this.validatePromotionFactoryPayload(payload, i);
                    if (ve) {
                        fail++;
                        errLines.push(ve);
                        continue;
                    }
                    try {
                        const res = await this.$api.post('api/promotion_factory/add', payload);
                        if (res && Number(res.code) === 0) {
                            ok++;
                        } else {
                            fail++;
                            errLines.push(
                                this.$t('autoPromotion.factoryBatchImportRowFail', {
                                    index: i + 1,
                                    msg: (res && res.msg) || this.$t('autoPromotion.factorySubmitFailed')
                                })
                            );
                        }
                    } catch (e) {
                        console.error(e);
                        fail++;
                        errLines.push(this.$t('autoPromotion.factoryBatchImportRowNetwork', { index: i + 1 }));
                    }
                }
                this.$message.success(this.$t('autoPromotion.factoryBatchImportDone', { ok, fail }));
                if (errLines.length) {
                    this.$notify({
                        title: this.$t('autoPromotion.factoryBatchImportErrorsTitle'),
                        message: errLines.slice(0, 8).join('\n'),
                        type: fail && !ok ? 'error' : 'warning',
                        duration: 12000
                    });
                }
                this.batchImportVisible = false;
                await this.fetchPromotionJournals();
            } finally {
                this.batchImporting = false;
            }
        },
        // --- 逻辑部分完全保留你原有的实现 ---
    openTemplateSelector() {
            this.templateDialogInitialStyleId =
                this.wizardConfig && this.wizardConfig.defaultStyleId ? String(this.wizardConfig.defaultStyleId) : '';
            this.templateDialogInitialTemplateId =
                this.wizardConfig && this.wizardConfig.defaultTemplateId ? String(this.wizardConfig.defaultTemplateId) : '';
      this.showTemplateDialog = true;
    },
    closeAllDialogs() {
      this.showWizardDialog = false;
      this.showTemplateDialog = false;
      this.detailVisible = false;
    },
        openFactoryTaskDialogForTask(journal, taskCard) {
            this.factoryDialogInitialJournalId = journal && journal.journal_id != null ? String(journal.journal_id) : '';
            this.factoryDialogInitialTask = taskCard && taskCard.rawTask ? { ...taskCard.rawTask } : null;
            this.showFactoryTaskDialog = true;
        },
        openFactoryTaskDialogForJournal(journal) {
            this.factoryDialogInitialJournalId = journal && journal.journal_id != null ? String(journal.journal_id) : '';
            this.factoryDialogInitialTask = null;
            this.showFactoryTaskDialog = true;
        },
        async onFactoryTaskSwitchChange(journal, taskCard, nextEnabled) {
            const taskId =
                taskCard && taskCard.taskId
                    ? String(taskCard.taskId)
                    : taskCard && taskCard.rawTask && taskCard.rawTask.promotion_factory_id != null
                    ? String(taskCard.rawTask.promotion_factory_id)
                    : '';
            if (!taskId) return;
            this.$set(taskCard, 'switchLoading', true);
            try {
                const res = await this.$api.post('api/promotion_factory/changePromotionAct', {
                    promotion_factory_id: taskId,
                    start_promotion: nextEnabled ? '1' : '0'
                });
                if (res && Number(res.code) === 0) {
                    this.$set(taskCard, 'enabled', !!nextEnabled);
                    if (taskCard.rawTask) {
                        this.$set(taskCard.rawTask, 'state', nextEnabled ? '1' : '0');
                        this.$set(taskCard.rawTask, 'start_promotion', nextEnabled ? '1' : '0');
                    }
                    return;
                }
                this.$message.error((res && res.msg) || this.$t('autoPromotion.saveFailed'));
                await this.loadFactoryTaskSummaryByJournal(journal, localStorage.getItem('U_id') || '');
            } catch (e) {
                this.$message.error(this.$t('autoPromotion.saveFailed'));
                await this.loadFactoryTaskSummaryByJournal(journal, localStorage.getItem('U_id') || '');
            } finally {
                this.$set(taskCard, 'switchLoading', false);
            }
        },
        getFactoryTaskActionText(taskCard) {
            return taskCard && taskCard.enabled ? this.$t('autoPromotion.goManagePlan') : this.$t('autoPromotion.startPlan');
        },
        async handleFactoryTaskAction(journal, taskCard) {
            if (!journal || !taskCard) return;
            const taskId = taskCard.taskId != null ? String(taskCard.taskId) : '';
            this.promotionUpdating = true;
            this.promotionUpdatingJournalId = journal.journal_id;
            this.promotionUpdatingTaskId = taskId;
            try {
                if (!taskCard.enabled && taskId) {
                    const res = await this.$api.post('api/promotion_factory/changePromotionAct', {
                        promotion_factory_id: taskId,
                        start_promotion: '1'
                    });
                    if (!res || Number(res.code) !== 0) {
                        this.$message.error((res && res.msg) || this.$t('autoPromotion.saveFailed'));
                        return;
                    }
                    this.$set(taskCard, 'enabled', true);
                    if (taskCard.rawTask) {
                        this.$set(taskCard.rawTask, 'state', '1');
                        this.$set(taskCard.rawTask, 'start_promotion', '1');
                    }
                }
                this.openDetail(journal, taskCard);
            } finally {
                this.promotionUpdating = false;
                this.promotionUpdatingJournalId = '';
                this.promotionUpdatingTaskId = '';
            }
        },
    _parseJournalDetail(data) {
      const journalInfo = data.journal || data || {};
      const tpl = journalInfo.template || data.template || {};
      const style = journalInfo.style || data.style || {};
      const tplId = String(journalInfo.default_template_id || '0');
      const styleId = String(journalInfo.default_style_id || '0');
      return {
        title: journalInfo.title || journalInfo.journal_title || '',
        templateId: tplId,
        styleId: styleId,
        templateName: tpl.name || tpl.title || journalInfo.default_template_name || (tplId !== '0' ? `模板#${tplId}` : ''),
        styleName: style.name || style.title || journalInfo.default_style_name || (styleId !== '0' ? `风格#${styleId}` : ''),
        html: `${style.header_html || ''}${tpl.body_html || ''}${style.footer_html || ''}`,
        enabled: String(journalInfo.start_promotion || '0') === '1',
        initialized: tplId !== '0' && styleId !== '0'
      };
    },
    async fetchPromotionJournals() {
      this.loading = true;
      try {
                const username = localStorage.getItem('U_name') || '';
        const userId = localStorage.getItem('U_id') || '';
                const res = await this.$api.post('api/Article/getJournal', { username: username });
                let raw = [];
                if (res && Number(res.code) === 0 && res.data) {
                    if (Array.isArray(res.data.journals)) {
                        raw = res.data.journals;
                    } else if (Array.isArray(res.data)) {
                        raw = res.data;
                    }
                } else if (Array.isArray(res)) {
                    raw = res;
                }
                if (!Array.isArray(raw) || !raw.length) {
                    this.allJournals = [];
                    return;
                }
                const journalIdsForTpl = raw
                    .map((item) => item.journal_id || item.id)
                    .filter((id) => id != null && String(id).trim() !== '');
                await this.prefetchFactoryTemplateNameMapsOnce(journalIdsForTpl);
                this.allJournals = await Promise.all(
                    raw.map(async (item) => {
            const journalId = item.journal_id || item.id;
            let journalObj = {
              journal_id: journalId,
                            abbr: item.abbr || '',
                            journal_icon: item.journal_icon || '',
              title: item.title || item.journal_title || item.name || `Journal ${journalId}`,
              solicit: {
                                enabled: false,
                                initialized: false,
                                templateId: '0',
                                styleId: '0',
                templateName: '',
                styleName: '',
                html: ''
                            },
                            
                            country_scope_label:'',
                            factoryTask: {
                                count: 0,
                                type: '',
                                typeLabel: this.$t('autoPromotion.autoSolicit')
                            },
                            factoryTasks: []
                        };
                        await Promise.all([
                            // this.refreshJournalByDetail(journalObj),
                            this.loadFactoryTaskSummaryByJournal(journalObj, userId)
                        ]);
            return journalObj;
                    })
                );
      } catch (e) {
        this.$message.error(this.$t('autoPromotion.loadListFailed'));
      } finally {
        this.loading = false;
      }
    },
        async loadFactoryTaskSummaryByJournal(journal, userId) {
            if (!journal || !journal.journal_id) return;
            try {
                const res = await this.$api.post('api/promotion_factory/getList', {
                    journal_id: String(journal.journal_id),
                    user_id: String(userId || ''),
                    state: '-1'
                });
                const payload = (res && res.data) || {};
                const list = this.findArray(payload) || this.findArray(res) || [];
                const totalRaw = payload.total != null ? payload.total : res && res.total != null ? res.total : list.length;
                const total = Number(totalRaw) || 0;
                let latest = null;
                if (Array.isArray(list) && list.length) {
                    latest = list
                        .slice()
                        .sort((a, b) => Number(b.ctime || b.create_time || b.time || 0) - Number(a.ctime || a.create_time || a.time || 0))[0];
                }
          
                const normalizedTasks = (Array.isArray(list) ? list : []).map((task, idx) => {
                    const type = task && task.type != null ? String(task.type) : '';
                    const fieldCount = this.getFactoryTaskFieldCount(task);
                    const startPromotion =
                        task && task.start_promotion != null && String(task.start_promotion).trim() !== ''
                            ? String(task.start_promotion)
                            : null;
                    const state = task && task.state != null && String(task.state).trim() !== '' ? String(task.state) : null;
                    const enabled = String(startPromotion != null ? startPromotion : state != null ? state : '0') === '1';
                    return {
                        cardKey: `${journal.journal_id}_${task.promotion_factory_id || idx}`,
                        taskId: task.promotion_factory_id || '',
                        type: type,
                        typeLabel: this.mapFactoryTaskTypeLabel(type),
                        expertType: task && task.expert_type != null ? String(task.expert_type) : '',
                        expertTypeLabel: this.mapFactoryExpertTypeLabel(task && task.expert_type != null ? String(task.expert_type) : ''),
                        enabled: enabled,
                        switchLoading: false,
                        initialized: true,
                        templateName: this.getFactoryTemplateName(task, journal.journal_id),
                        fieldCount: fieldCount,
                        fieldCountText: String(fieldCount),
                        countryScopeLabel: task.country_scope_label || '-',
                        createdAtText: this.formatTaskCreateTime(task),
                        totalCount: total,
                        showCount: idx === 0 && total > 0,
                        rawTask: task
                    };
                });
                const firstTask = normalizedTasks.length ? normalizedTasks[0] : null;
                this.$set(journal, 'factoryTasks', normalizedTasks);
                this.$set(journal, 'factoryTask', {
                    count: total,
                    type: firstTask ? firstTask.type : '',
                    typeLabel: firstTask ? firstTask.typeLabel : this.$t('autoPromotion.autoSolicit'),
                    fieldCount: firstTask ? firstTask.fieldCount : 0
                });
            } catch (e) {
                this.$set(journal, 'factoryTasks', []);
                this.$set(journal, 'factoryTask', {
                    count: 0,
                    type: '',
                    typeLabel: this.$t('autoPromotion.autoSolicit'),
                    fieldCount: 0
                });
            }
        },
        /**
         * 全页只请求一次 listTemplatesAll，再按期刊写入 factoryTemplateNameMap。
         * 若列表项带 journal_id 则按期刊拆分；否则用同一份 id->name 映射到当前页各期刊（与原先「每刊各请求一次」相比只发 1 次 HTTP）。
         */
        async prefetchFactoryTemplateNameMapsOnce(journalIds) {
            const ids = [...new Set((journalIds || []).map((id) => String(id).trim()).filter(Boolean))];
            if (!ids.length) return;
            try {
                const res = await this.$api.post('api/mail_template/listTemplatesAll', { journal_id: ids[0] });
                const payload = (res && res.data) || {};
                const list = this.findArray(payload) || this.findArray(res) || [];
                const flat = {};
                const byJournal = {};
                (Array.isArray(list) ? list : []).forEach((item) => {
                    const id = item && (item.template_id != null ? item.template_id : item.id);
                    if (id == null) return;
                    const name = String(item.title || item.name || '').trim();
                    if (!name) return;
                    const tid = String(id);
                    flat[tid] = name;
                    const jid =
                        item.journal_id != null
                            ? String(item.journal_id).trim()
                            : item.journalId != null
                              ? String(item.journalId).trim()
                              : item.j_id != null
                                ? String(item.j_id).trim()
                                : '';
                    if (jid) {
                        if (!byJournal[jid]) byJournal[jid] = {};
                        byJournal[jid][tid] = name;
                    }
                });
                const perItemJournal = Object.keys(byJournal).length > 0;
                ids.forEach((jid) => {
                    const m = perItemJournal ? byJournal[jid] || {} : { ...flat };
                    this.$set(this.factoryTemplateNameMap, jid, m);
                });
            } catch (e) {
                console.error(e);
            }
        },
        getFactoryTemplateName(task, journalId) {
            const tplId = task && task.template_id != null ? String(task.template_id) : '';
            const journalMap = this.factoryTemplateNameMap[String(journalId || '')] || {};
            if (tplId && journalMap[tplId]) return journalMap[tplId];
            if (task && task.template_name) return task.template_name;
            return tplId ? `Template #${tplId}` : 'No Template Configured';
        },
        getFactoryTaskFieldCount(task) {
            if (!task) return 0;
            if (task.fetch_fields && typeof task.fetch_fields === 'object') {
                const keys = Object.keys(task.fetch_fields).filter(Boolean);
                if (keys.length) return keys.length;
            }
            const fetchIds = String(task.fetch_ids || '')
                .split(',')
                .map((s) => String(s).trim())
                .filter(Boolean);
            return fetchIds.length;
        },
        formatTaskCreateTime(task) {
            if (!task || typeof task !== 'object') return '';
            const raw = task.ctime || task.create_time || task.created_at || task.time || '';
            if (raw == null || String(raw).trim() === '') return '';
            const n = Number(raw);
            let dt = null;
            if (!isNaN(n) && n > 0) {
                dt = new Date(n > 1e12 ? n : n * 1000);
            } else {
                dt = new Date(String(raw));
            }
            if (!dt || isNaN(dt.getTime())) return String(raw);
            const pad = (v) => String(v).padStart(2, '0');
            return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())} ${pad(dt.getHours())}:${pad(
                dt.getMinutes()
            )}:${pad(dt.getSeconds())}`;
        },
        mapFactoryTaskTypeLabel(type) {
            const t = String(type || '');
            if (t === '1') return this.$t('autoPromotion.factoryScenarioSolicit');
            if (t === '2') return this.$t('autoPromotion.factoryScenarioPromoteCitation');
            if (t === '3') return this.$t('autoPromotion.factoryScenarioGeneralThanks');
            if (t === '4') return this.$t('autoPromotion.autoSolicit');
            return this.$t('autoPromotion.autoSolicit');
        },
        mapFactoryExpertTypeLabel(expertType) {
            const t = String(expertType || '').trim();
            if (t === '1') return this.$t('autoPromotion.factoryExpertChief');
            if (t === '2') return this.$t('autoPromotion.factoryExpertBoard');
            if (t === '3') return this.$t('autoPromotion.factoryExpertYoungBoard');
            if (t === '4') return this.$t('autoPromotion.factoryExpertAuthor');
            if (t === '5') return this.$t('autoPromotion.factoryExpertDb');
            if (t === '6') return this.$t('autoPromotion.factoryExpertYoungBoardBefore2025');
            if (t === '7') return this.$t('autoPromotion.factoryExpertAuthorBefore2025');
            return '-';
        },
        getJournalDisplayTasks(journal) {
            const list = journal && Array.isArray(journal.factoryTasks) ? journal.factoryTasks : [];
            return list;
        },
        getFactoryTaskTypeLabel(journal) {
            if (journal && journal.factoryTask && journal.factoryTask.typeLabel) {
                return journal.factoryTask.typeLabel;
            }
            return this.$t('autoPromotion.autoSolicit');
        },
    async refreshAll() {
      this.templateNameMap = {};
            this.factoryTemplateNameMap = {};
      this.allJournals = [];
      await this.fetchPromotionJournals();
    },
    async refreshJournalByDetail(journal) {
      if (!journal || !journal.journal_id) return;
      try {
                const res = await this.$api.post('api/email_client/getPromotionJournalDetail', { journal_id: String(journal.journal_id) });
        const detail = this._parseJournalDetail(res.data);
                this.$set(this.templateNameMap, 'j_' + journal.journal_id, detail);
        this.$set(journal, 'title', detail.title || journal.title);
                this.$set(journal, 'solicit', { ...(journal.solicit || {}), ...detail });
      } catch (e) {
                console.error(e);
            }
        },
    isSolicitConfigured(journal) {
      const s = journal && journal.solicit ? journal.solicit : {};
      return !!(s.initialized && s.templateId !== '0' && s.styleId !== '0');
    },
    getSolicitActionText(journal) {
      if (!this.isSolicitConfigured(journal)) return this.$t('autoPromotion.goConfig');
            return journal.solicit.enabled ? this.$t('autoPromotion.goManagePlan') : this.$t('autoPromotion.startPlan');
    },
    findArray(obj) {
      if (Array.isArray(obj)) return obj;
      const keys = ['list', 'fields', 'rows', 'items', 'data', 'result'];
      for (const k of keys) {
                if (obj && Array.isArray(obj[k])) return obj[k];
      }
      return null;
    },
    async loadPromotionFields(journalId) {
      this.fieldsLoading = true;
      try {
        const availableRes = await this.$api.post('api/email_client/getAvailableFields', { journal_id: String(journalId) });
                let availableArr = this.findArray(availableRes.data || availableRes) || [];
                this.availableFields = availableArr.map((item, idx) => ({
                    id: String(item.expert_fetch_id || item.fetch_id || item.id || idx),
                    label: item.field || item.title || item.name || 'Field'
                }));
                this.availableCountries = [...this.availableFields];
        const selectedRes = await this.$api.post('api/email_client/getJournalPromotionFields', { journal_id: String(journalId) });
                const selectedPayload = selectedRes.data || selectedRes || {};
        let selectedArr = this.findArray(selectedPayload);
        if (selectedArr) {
                    this.selectedFieldIds = selectedArr.map((it) => String(it.expert_fetch_id || it.fetch_id || it.id || it));
                }
                if (selectedPayload.country_fetch_ids) {
                    this.selectedCountryIds = selectedPayload.country_fetch_ids.split(',').filter(Boolean);
                }
      } catch (e) {
                console.error(e);
      }
      this.fieldsLoading = false;
    },
    async savePromotionFieldsNow() {
            await this.$api.post(
                'api/email_client/setJournalPromotionFields',
                this.journalPromotionFieldsPayload(this.wizardJournal.journal_id)
            );
        this.$message.success(this.$t('autoPromotion.fieldsSaved'));
        },
        journalPromotionFieldsPayload(journalId) {
            return {
                journal_id: String(journalId),
                fetch_ids: (this.selectedFieldIds || []).join(','),
                country_fetch_ids: (this.selectedCountryIds || []).join(',')
            };
        },
      
        openWizardForJournal(journal) {
      this.wizardJournal = journal;
      const s = journal.solicit || {};
            this.wizardConfig = { defaultTemplateId: s.templateId, defaultStyleId: s.styleId, enabled: !!s.enabled };
            this.selectedTemplateName = s.templateName;
            this.selectedStyleName = s.styleName;
            this.selectedTemplateThumbHtml = `<div style="zoom:0.18;">${s.html || ''}</div>`;
            this.loadPromotionFields(journal.journal_id);
      this.showWizardDialog = true;
    },
    handleTemplateApply(payload) {
      this.wizardConfig.defaultTemplateId = String(payload.template_id);
      this.wizardConfig.defaultStyleId = String(payload.style_id);
            this.selectedTemplateName = payload.template.name;
            this.selectedTemplateThumbHtml = `<div style="zoom:0.18;">${payload.html || ''}</div>`;
      this.showTemplateDialog = false;
    },
    async saveWizardConfig() {
      this.saving = true;
      try {
        await this.$api.post('api/email_client/setDefaultPromotion', {
          journal_id: String(this.wizardJournal.journal_id),
          default_template_id: this.wizardConfig.defaultTemplateId,
          default_style_id: this.wizardConfig.defaultStyleId,
          start_promotion: this.wizardConfig.enabled ? '1' : '0',
                    user_id: localStorage.getItem('U_id')
        });
        await this.refreshJournalByDetail(this.wizardJournal);
        this.showWizardDialog = false;
      } catch (e) {
                this.$message.error('Save failed');
      } finally {
        this.saving = false;
      }
    },
        openDetail(journal,taskCard) {
            const taskId = taskCard && taskCard.taskId != null ? String(taskCard.taskId) : '';
      this.$router.push({
        path: '/autoPromotionLogs',
                query: {
                    journal_id: String(journal.journal_id),
                    promotion_factory_id: taskId
                }
      });
    },
    async handleSwitch(journal, type, nextVal) {
      if (!this.isSolicitConfigured(journal)) {
        this.$set(journal.solicit, 'enabled', false);
        return;
      }
        this.promotionUpdating = true;
            try {
        await this.$api.post('api/email_client/setDefaultPromotion', {
          journal_id: String(journal.journal_id),
          default_template_id: String(journal.solicit.templateId),
          default_style_id: String(journal.solicit.styleId),
          start_promotion: nextVal ? '1' : '0',
                    user_id: localStorage.getItem('U_id')
        });
      } catch (e) {
        this.$set(journal.solicit, 'enabled', !nextVal);
      } finally {
        this.promotionUpdating = false;
      }
    }
  },
  beforeDestroy() {
    this.clearBatchImportAccountsFetchTimer();
  }
};
</script>

<style scoped>
/* 容器 */
.auto-promo-container {
    padding: 0 10px 6px;
    min-height: 100vh;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.journal-list-wrapper {
    max-width: 1600px;
    margin: 0 auto;
}

/* 顶部 */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 4px;
}

/* 期刊分组区间 */
.journal-group-section {
    margin-bottom: 20px;
}

.journal-main-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    padding-left: 2px;
}

.brand-icon {
    width: 22px;
    height: 22px;
    background: #3a579a;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    font-size: 15px;
}

.journal-title-text {
    font-size: 15px;
    font-weight: 700;
    color: #1a1a1a;
    margin: 0;
    margin-left: 6px;
}

.active-badge {
    font-size: 12px;
    font-weight: 800;
    color: #6a737d;
    letter-spacing: 0.5px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.pulse-dot {
    width: 8px;
    height: 8px;
    background-color: #409eff;
    border-radius: 50%;
    box-shadow: 0 0 0 rgba(64, 158, 255, 0.4);
    animation: pulse 2s infinite;
}

@keyframes pulse {
    0% {
        transform: scale(0.95);
        box-shadow: 0 0 0 0 rgba(64, 158, 255, 0.7);
    }
    70% {
        transform: scale(1);
        box-shadow: 0 0 0 6px rgba(64, 158, 255, 0);
    }
    100% {
        transform: scale(0.95);
        box-shadow: 0 0 0 0 rgba(64, 158, 255, 0);
    }
}
.journal-brand {
    display: flex;
    align-items: center;
}
/* 卡片网格布局 */
.cards-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: 12px;
}

.no-task-tip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 14px 12px;
    border: 1px dashed #dcdfe6;
    border-radius: 8px;
    color: #909399;
    font-size: 13px;
    background: #fafafa;
}

.no-task-create-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
}

/* 统计卡片基础样式 */
.stat-card {
    background: #ffffff;
    border-radius: 8px;
    border: 1px solid #e1e4e8;
    padding: 10px;
    transition: all 0.2s ease;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.stat-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
}

.stat-card.is-active {
    border-top: 3px solid #67c23a;
}

.stat-card.is-stopped {
    border-top: 3px solid #909399;
}

.stat-card.is-disabled-style {
    opacity: 0.8;
    background: #fafbfc;
}

/* 卡片内部头部 */
.card-inner-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 8px;
    width: 100%;
    box-sizing: border-box;
    gap: 8px;
}

.card-label-area {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: nowrap;
    flex: 1;
    min-width: 0;
    overflow: hidden;
}

.card-main-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 700;
    color: #1a1a1a;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
}

.config-inline-btn {
    flex-shrink: 0;
    white-space: nowrap;
}

.scene-task-count {
    font-size: 12px;
    color: #909399;
    font-weight: 600;
}

.status-tag {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    font-weight: 700;
    margin-top: 0px;
    margin-left: 0;
    flex-shrink: 0;
    white-space: nowrap;
}

.is-active .status-tag {
    color: #52c41a;
}
.is-stopped .status-tag {
    color: #909399;
}

.status-tag .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    margin-right: 6px;
}

/* 内容区：与底部按钮同宽对齐 */
.card-content {
    flex: 1;
    width: 100%;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
}

.template-preview-box {
    background: #f1f4f9;
    border: 1px solid #e1e4e8;
    border-radius: 6px;
    padding: 3px 8px;
    /* display: flex;
    align-items: center; */
    gap: 0px;
    margin-bottom: 8px;
    color: #409eff;
    width: 100%;
    box-sizing: border-box;
}

.meta-row {
    display: flex;
    align-items: center;
    min-width: 0;
}

.template-preview-box.is-empty {
    color: #909399;
    font-style: italic;
}

.tpl-name {
    font-size: 12px;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tpl-name-single-line {
    flex: 1;
    min-width: 0;
}

/* 统计项 */
.stats-container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-bottom: 8px;
}

.stat-box {
    background: #f8f9fa;
    padding: 6px 8px;
    border-radius: 4px;
    border: 1px solid #f0f1f2;
}

.stat-label {
    display: block;
    font-size: 10px;
    font-weight: 800;
    color: #6a737d;
    margin-bottom: 4px;
    letter-spacing: 0.4px;
}

.stat-value {
    font-size: 13px;
    font-weight: 700;
    color: #24292e;
    display: flex;
    align-items: center;
    gap: 4px;
}

.stat-value i {
    font-size: 15px;
    color: #52c41a;
}
.stat-value.warning i {
    color: #e6a23c;
}

/* 底部按钮区 */
.card-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.task-card-footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 16px;
}

.promo-history-spacer {
    flex: 0 0 auto;
    min-width: 0;
}

.promo-history-link {
    flex: 0 1 auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    font-size: 11px;
    font-weight: 600;
    color: #409eff;
    cursor: pointer;
    line-height: 1.3;
    text-align: left;
}

.promo-history-link i {
    font-size: 12px;
}

.promo-history-link:hover {
    color: #66b1ff;
    text-decoration: underline;
}

.task-create-time {
    flex-shrink: 0;
    font-size: 11px;
    color: #909399;
    text-align: right;
}

.action-main-btn {
    flex: 1;
    height: 24px;
    background: #eaf3ff;
    color: #409eff;
    border: 1px solid #d7e7ff;
    border-radius: 4px;
    font-weight: 600;
    font-size: 12px;
    line-height: 22px;
    cursor: pointer;
    transition: all 0.2s;
}

.action-main-btn:hover {
    background: #dcecff;
    border-color: #c4ddff;
    color: #2f89ea;
}

.is-active .action-main-btn {
    background: #3dbb6a;
    color: #ffffff;
    border-color: #3dbb6a;
}

.is-active .action-main-btn:hover {
    background: #31a85b;
    border-color: #31a85b;
    color: #ffffff;
}

.action-main-btn.secondary {
    background: transparent;
    border: 2px solid #1a1a1a;
    color: #1a1a1a;
}

.action-edit-btn {
    width: 30px;
    height: 30px;
    background: white;
    border: 1px solid #e1e4e8;
    border-radius: 6px;
    font-size: 14px;
    cursor: pointer;
    color: #6a737d;
    display: flex;
    align-items: center;
    justify-content: center;
}

.action-edit-btn:hover {
    background: #f6f8fa;
    color: #409eff;
    border-color: #409eff;
}

/* 临时批量导入 JSON 对话框 */
.factory-batch-import-dialog .el-dialog__body {
    padding-top: 12px;
}

.batch-import-layout {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    min-height: 0;
}

.batch-import-journal-rail {
    flex: 0 0 74px;
    width: 74px;
    padding: 8px 8px 8px 0;
    border-right: 1px solid #ebeef5;
    align-self: stretch;
    max-height: 72vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.batch-import-journal-scroll {
    flex: 1;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.batch-import-rail-title {
    font-size: 11px;
    font-weight: 600;
    color: #606266;
    margin-bottom: 6px;
    text-align: center;
    line-height: 1.25;
    flex-shrink: 0;
}

.batch-import-journal-rail .batch-import-journal-empty {
    text-align: center;
    font-size: 10px;
    color: #909399;
    padding: 4px 0 8px;
    line-height: 1.3;
}

.batch-import-journal-rail .batch-import-journal-grid--rail {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 0;
    flex: none;
    min-height: 0;
    overflow: visible;
    padding-right: 0;
}

.batch-import-journal-rail .batch-import-journal-card {
    width: 35px;
}

.batch-import-journal-rail .batch-import-mini-cover-wrapper {
    width: 35px;
    height: 45px;
}

.batch-import-journal-rail .batch-import-mini-badge {
    width: 14px;
    height: 14px;
    font-size: 8px;
    top: -3px;
    right: -3px;
}

.batch-import-journal-rail .batch-import-mini-abbr {
    font-size: 9px;
    margin-top: 4px;
    max-width: 35px;
}

.batch-import-journal-rail .batch-import-journal-card.is-active .batch-import-mini-cover {
    transform: translateY(-1px);
}

.batch-import-journal-id-row--rail {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    padding-top: 4px;
    border-top: 1px solid #ebeef5;
}

.batch-import-journal-id-row--rail .batch-import-id-label {
    font-size: 11px;
}

.batch-import-journal-id-row--rail .batch-import-journal-id-input {
    max-width: none;
    width: 100%;
    min-width: 0;
}

.batch-import-main {
    flex: 1;
    min-width: 0;
}

.batch-import-hint-compact {
    font-size: 12px;
    line-height: 1.45;
    color: #909399;
    margin: 0 0 12px;
}

.batch-import-hint {
    font-size: 12px;
    line-height: 1.55;
    color: #606266;
    margin: 0 0 10px;
    white-space: pre-wrap;
}

.batch-import-common-tip {
    font-size: 12px;
    line-height: 1.5;
    color: #909399;
    margin: 0 0 10px;
}

.batch-import-common-fields {
    margin-bottom: 12px;
}

.batch-import-journal-block {
    margin-bottom: 14px;
}

.batch-import-journal-panel {
    background: #f8fafc;
    border: 1px solid #ebf0f5;
    border-radius: 8px;
    padding: 12px;
}

.batch-import-journal-empty {
    font-size: 12px;
    color: #909399;
    padding: 8px 0;
}

.batch-import-journal-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 12px;
}

.batch-import-journal-card {
    width: 70px;
    text-align: center;
    cursor: pointer;
}

.batch-import-mini-cover-wrapper {
    position: relative;
    width: 70px;
    height: 90px;
}

.batch-import-mini-cover {
    width: 100%;
    height: 100%;
    border-radius: 4px;
    border: 2px solid transparent;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    transition: 0.25s;
}

.batch-import-journal-card.is-active .batch-import-mini-cover {
    border-color: #409eff;
    transform: translateY(-2px);
}

.batch-import-mini-badge {
    position: absolute;
    top: -6px;
    right: -6px;
    background: #67c23a;
    color: #fff;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    font-size: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
}

.batch-import-image-slot {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    background: #f5f7fa;
    color: #c0c4cc;
}

.batch-import-mini-abbr {
    font-size: 11px;
    margin-top: 6px;
    color: #606266;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.batch-import-mini-abbr.is-active {
    color: #409eff;
    font-weight: 600;
}

.batch-import-journal-id-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.batch-import-id-label {
    font-size: 12px;
    color: #606266;
    font-weight: 600;
    flex-shrink: 0;
}

.batch-import-journal-id-input {
    flex: 1;
    min-width: 160px;
    max-width: 360px;
}

.batch-import-fetch-block {
    margin-bottom: 12px;
    padding: 10px 12px;
    background: #fafafa;
    border: 1px solid #eef0f3;
    border-radius: 6px;
}

.batch-import-field-row-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 6px;
}

.batch-import-fetch-count {
    font-size: 12px;
    color: #909399;
    flex: 1;
    min-width: 80px;
}

.batch-import-fetch-tip {
    font-size: 12px;
    color: #c27a00;
    margin: 0 0 8px;
    line-height: 1.45;
}

.batch-import-field-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
}

.batch-import-field-search {
    flex: 1;
    min-width: 180px;
    max-width: 100%;
}

.batch-import-field-empty {
    font-size: 12px;
    color: #909399;
    padding: 8px 4px;
}

.batch-import-field-checkbox-wrap {
    max-height: 160px;
    overflow: auto;
    margin-bottom: 8px;
    padding: 8px;
    background: #fff;
    border: 1px solid #ebeef5;
    border-radius: 4px;
}

.batch-import-field-checkbox-wrap .el-checkbox {
    display: block;
    margin-right: 0;
    margin-bottom: 6px;
}

.batch-import-fetch-text-label {
    margin-top: 4px;
    margin-bottom: 4px;
}

.factory-batch-import-dialog .batch-import-fetch-textarea >>> textarea {
    font-family: Consolas, 'Courier New', monospace;
    font-size: 12px;
    line-height: 1.45;
}

.batch-import-field-label {
    font-size: 12px;
    color: #606266;
    margin-bottom: 4px;
    font-weight: 600;
}

.batch-import-accounts-table {
    margin-bottom: 12px;
}

.batch-import-json-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 10px;
}

.batch-import-sync-email-check {
    margin-right: 0;
}

.batch-import-json-toolbar-tip {
    font-size: 12px;
    color: #909399;
    flex: 1;
    min-width: 200px;
    line-height: 1.45;
}

.factory-batch-import-dialog .batch-import-textarea >>> textarea {
    font-family: Consolas, 'Courier New', monospace;
    font-size: 12px;
    line-height: 1.45;
}

/* 响应式 */
@media (max-width: 1200px) {
    .cards-grid {
        grid-template-columns: 1fr;
    }
}
</style>
