<template>
  <div class="app-container rules-page">
    <!-- 筛选 -->
    <el-card v-show="showSearch" class="page-card" shadow="never">
      <div class="card-head">
        <span class="card-head__title">筛选条件</span>
        <span v-if="activeFilterCount" class="card-head__extra">{{ activeFilterCount }} 项</span>
      </div>
      <el-form ref="queryForm" :model="queryParams" class="query-form" label-position="top" @submit.native.prevent>
        <el-row :gutter="16">
          <el-col :xs="24" :sm="12" :md="8" :lg="6">
            <el-form-item label="任务名称" prop="batchId">
              <el-select
                v-model="queryParams.batchId"
                placeholder="请选择或搜索任务"
                clearable
                filterable
                size="small"
                class="full-width"
              >
                <el-option
                  v-for="task in taskList"
                  :key="task.batchId"
                  :label="task.batchName"
                  :value="task.batchId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :xs="24" :sm="12" :md="8" :lg="6">
            <el-form-item label="节假日屏蔽" prop="ruleOne">
              <el-radio-group v-model="queryParams.ruleOne" size="small" class="filter-btns">
                <el-radio-button label="">全部</el-radio-button>
                <el-radio-button label="0">不屏蔽</el-radio-button>
                <el-radio-button label="1">屏蔽</el-radio-button>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :xs="24" :sm="12" :md="8" :lg="6">
            <el-form-item label="每周屏蔽天" prop="ruleThree">
              <el-select v-model="queryParams.ruleThree" placeholder="全部星期" clearable size="small" class="full-width">
                <el-option
                  v-for="item in weekdayOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :xs="24" :sm="12" :md="8" :lg="6">
            <el-form-item label="状态" prop="status">
              <el-radio-group v-model="queryParams.status" size="small" class="filter-btns">
                <el-radio-button label="">全部</el-radio-button>
                <el-radio-button label="0">启用</el-radio-button>
                <el-radio-button label="1">禁用</el-radio-button>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <div class="query-form__actions">
          <el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
          <el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
        </div>
      </el-form>
    </el-card>

    <!-- 列表 -->
    <el-card class="page-card" shadow="never">
      <div class="rule-notice">
        <div class="rule-notice__head">
          <i class="el-icon-info rule-notice__icon" />
          <span class="rule-notice__title">规则说明</span>
          <span v-if="!ruleNoticeExpanded" class="rule-notice__brief">
            未绑定规则时使用兜底规则（ID=0）；校验顺序：节假日 → 特殊日期 → 每周 → 每天时段
          </span>
          <el-button type="text" size="mini" class="rule-notice__toggle" @click="ruleNoticeExpanded = !ruleNoticeExpanded">
            {{ ruleNoticeExpanded ? '收起' : '查看详情' }}
            <i :class="ruleNoticeExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" />
          </el-button>
        </div>
        <div v-show="ruleNoticeExpanded" class="rule-notice__body">
          <ol class="rule-notice__list">
            <li>如果外呼任务未指定绑定规则，默认使用兜底 ID 为 <strong>0</strong> 的规则。</li>
            <li>校验优先级：先校验<strong>节假日（P1）</strong>，再校验<strong>特殊日期（P2）</strong>，再校验<strong>每周（P3）</strong>，最后校验<strong>每天时段（P4）</strong>。</li>
            <li>同一个外呼任务不能重复录入屏蔽规则。</li>
            <li>如果外呼任务状态为<strong>禁用</strong>，则绑定规则不生效，采用兜底规则（ID=0）进行限制。</li>
          </ol>
        </div>
      </div>

      <div class="table-toolbar">
        <div class="table-toolbar__left">
          <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['aiSipCall:rules:add']">新增</el-button>
          <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate()" v-hasPermi="['aiSipCall:rules:edit']">修改</el-button>
          <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="deleteDisabled" @click="handleDelete" v-hasPermi="['aiSipCall:rules:remove']">删除</el-button>
          <el-button type="warning" plain icon="el-icon-download" size="mini" :loading="exportLoading" @click="handleExport" v-hasPermi="['aiSipCall:rules:export']">导出</el-button>
          <el-button type="info" plain icon="el-icon-video-play" size="mini" :loading="syncRuleLoading" :disabled="syncRuleLoading || syncReportLoadingVisible || syncReportDialog.visible" @click="handleSyncRule">立即执行规则</el-button>
        </div>
        <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
      </div>

      <el-table
        ref="rulesTable"
        v-loading="loading"
        :data="rulesList"
        border
        stripe
        size="small"
        class="rules-table"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="46" align="center" header-align="center" fixed="left" />
        <el-table-column label="ID" prop="id" width="56" align="center" header-align="center" />
        <el-table-column label="绑定任务" min-width="12%" align="center" header-align="center">
          <template slot-scope="{ row }">
            <div v-if="isFallbackRule(row)" class="cell-fallback">
              <span class="fallback-badge"><i class="el-icon-s-flag" />兜底规则</span>
            </div>
            <div v-else class="cell-tags">
              <el-tooltip
                v-for="(name, i) in row._tasks"
                :key="i"
                :content="name"
                :disabled="name.length <= 15"
                placement="top"
              >
                <el-tag size="mini" type="primary" effect="plain" class="cell-tag cell-tag--task">{{ name }}</el-tag>
              </el-tooltip>
              <span v-if="!row._tasks.length" class="cell-empty">未绑定</span>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="屏蔽规则" min-width="42%" align="center" header-align="center" class-name="rule-col">
          <template slot-scope="{ row }">
            <div class="rule-list">
              <div class="rule-list__item">
                <span class="rule-priority rule-priority--1">P1</span>
                <span class="rule-list__label"><i class="el-icon-sunny" />节假日</span>
                <span class="rule-list__content">
                  <el-tag :type="isRuleOneBlocked(row) ? 'warning' : 'success'" size="mini" effect="plain" class="rule-val-tag">
                    {{ isRuleOneBlocked(row) ? '法定节假日屏蔽' : '不屏蔽' }}
                  </el-tag>
                </span>
              </div>
              <div class="rule-list__item">
                <span class="rule-priority rule-priority--2">P2</span>
                <span class="rule-list__label"><i class="el-icon-calendar" />特殊日期</span>
                <span class="rule-list__content">
                  <template v-if="row._dates.length">
                    <span v-for="(date, i) in row._dates" :key="'d' + i" class="rule-chip rule-chip--date">{{ date }}</span>
                  </template>
                  <span v-else class="rule-chip rule-chip--none">不限制</span>
                </span>
              </div>
              <div class="rule-list__item">
                <span class="rule-priority rule-priority--3">P3</span>
                <span class="rule-list__label"><i class="el-icon-date" />每周</span>
                <span class="rule-list__content">
                  <template v-if="row._weekdays.length">
                    <span v-for="(label, i) in row._weekdays" :key="'w' + i" class="rule-chip rule-chip--week">{{ label }}</span>
                  </template>
                  <span v-else class="rule-chip rule-chip--none">不限制</span>
                </span>
              </div>
              <div class="rule-list__item">
                <span class="rule-priority rule-priority--4">P4</span>
                <span class="rule-list__label"><i class="el-icon-time" />每天时段</span>
                <span class="rule-list__content">
                  <template v-if="row._times.length">
                    <span v-for="(slot, i) in row._times" :key="'t' + i" class="rule-chip rule-chip--time">{{ formatRangeLabel(slot) }}</span>
                  </template>
                  <span v-else class="rule-chip rule-chip--none">不限制</span>
                </span>
              </div>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="状态" min-width="8%" align="center" header-align="center">
          <template slot-scope="{ row }">
            <span :class="['status-badge', isStatusEnabled(row) ? 'status-badge--on' : 'status-badge--off']">
              <i class="status-badge__dot" />
              {{ isStatusEnabled(row) ? '启用' : '禁用' }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="可删" min-width="8%" align="center" header-align="center">
          <template slot-scope="{ row }">
            <span :class="['protect-badge', isRowProtected(row) ? 'protect-badge--deny' : 'protect-badge--allow']">
              <i :class="isRowProtected(row) ? 'el-icon-lock' : 'el-icon-unlock'" />
              {{ isRowProtected(row) ? '受保护' : '可删除' }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="备注" prop="remark" min-width="10%" align="center" header-align="center" show-overflow-tooltip />
        <el-table-column label="操作" width="120" align="center" header-align="center" fixed="right">
          <template slot-scope="{ row }">
            <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(row)" v-hasPermi="['aiSipCall:rules:edit']">修改</el-button>
            <el-button size="mini" type="text" icon="el-icon-delete" :disabled="!canDeleteRule(row)" @click="handleDelete(row)" v-hasPermi="['aiSipCall:rules:remove']">删除</el-button>
          </template>
        </el-table-column>
      </el-table>

      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </el-card>

    <!-- 新增 / 修改 -->
    <el-dialog
      :title="title"
      :visible.sync="open"
      width="1180px"
      top="4vh"
      append-to-body
      :close-on-click-modal="false"
      custom-class="rules-dialog"
      @close="handleDialogClose"
    >
      <div class="rules-dialog-layout">
        <div class="rules-dialog-layout__form">
      <el-form ref="form" :model="form" :rules="dialogFormRules" label-width="0" class="dialog-form">
        <div v-if="!isTaskNameLocked" ref="batchIdListSection" class="form-block form-block--dense">
          <div class="form-block__title form-block__title--dense"><i class="el-icon-link" />绑定任务</div>
          <el-form-item prop="batchIdList" class="task-name-item">
            <div class="field-label field-label--required">外呼任务</div>
            <el-select
              v-model="form.batchIdList"
              multiple
              filterable
              clearable
              placeholder="请选择外呼任务（可多选）"
              class="full-width batch-task-select"
              :class="{ 'batch-task-select--empty': !selectedTaskItems.length }"
              popper-class="batch-task-select-dropdown"
              @change="handleBatchIdListChange"
            >
              <el-option
                v-for="task in taskList"
                :key="task.batchId"
                :label="task.batchName"
                :value="Number(task.batchId)"
              />
            </el-select>
            <div v-if="selectedTaskItems.length" class="selected-task-list selected-task-list--compact">
              <div
                v-for="item in selectedTaskItems"
                :key="item.batchId"
                class="selected-task-item"
              >
                <span class="selected-task-item__text" :title="item.batchName">{{ item.batchName }}</span>
                <i class="el-icon-close selected-task-item__close" @click="removeSelectedTask(item.batchId)" />
              </div>
            </div>
          </el-form-item>
        </div>

        <div class="form-block form-block--rules form-block--dense">
          <div class="rules-dialog-head">
            <span class="form-block__title form-block__title--dense"><i class="el-icon-setting" />屏蔽规则</span>
            <span class="rules-dialog-head__priority">P1 节假日 → P2 特殊日期 → P3 每周 → P4 每天时段</span>
          </div>

          <div ref="ruleOneSection" class="rule-inline">
            <span class="rule-priority rule-priority--1">P1</span>
            <span class="rule-inline__label field-label--required"><i class="el-icon-sunny" />节假日</span>
            <el-form-item prop="ruleOne" class="rule-inline__field">
              <el-radio-group v-model="form.ruleOne" size="mini" class="rule-radio-btns">
                <el-radio-button label="0">不屏蔽</el-radio-button>
                <el-radio-button label="1">法定节假日屏蔽</el-radio-button>
              </el-radio-group>
            </el-form-item>
          </div>

          <div class="rules-matrix">
              <div ref="specialDatesSection" class="rules-matrix__p2 rule-setting-card rule-setting-card--dense">
                <div class="rule-setting-card__head">
                  <span class="rule-priority rule-priority--2">P2</span>
                  <span class="rule-setting-card__label"><i class="el-icon-calendar" />特殊日期</span>
                  <span class="field-optional">选填</span>
                  <div class="rule-setting-card__actions">
                    <el-button type="text" icon="el-icon-plus" size="mini" @click="addSpecialDate">添加</el-button>
                    <el-button
                      type="text"
                      icon="el-icon-delete"
                      size="mini"
                      class="clear-all-btn"
                      :disabled="!hasFilledSpecialDates"
                      @click="clearSpecialDates"
                    >清空</el-button>
                  </div>
                </div>
                <div class="rule-setting-card__body rule-setting-card__body--dense">
                  <div class="time-slot-list time-slot-list--dense">
                    <div v-for="(item, index) in form.specialDateRanges" :key="'d' + index" class="time-slot-card time-slot-card--dense time-slot-card--date-range">
                      <span class="time-slot-card__index">{{ index + 1 }}</span>
                      <el-date-picker
                        v-model="item.dateRange"
                        type="daterange"
                        format="yyyy-MM-dd"
                        value-format="yyyy-MM-dd"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        size="mini"
                        clearable
                        class="date-range-picker"
                        @change="handleSpecialDateRangeChange(item)"
                      />
                      <el-button
                        type="text"
                        icon="el-icon-delete"
                        class="dynamic-del"
                        :disabled="form.specialDateRanges.length <= 1"
                        @click="removeSpecialDate(index)"
                      />
                    </div>
                  </div>
                </div>
              </div>

              <div ref="timeSlotsSection" class="rules-matrix__p4 rule-setting-card rule-setting-card--dense">
                <div class="rule-setting-card__head">
                  <span class="rule-priority rule-priority--4">P4</span>
                  <span class="rule-setting-card__label"><i class="el-icon-time" />每天时段</span>
                  <span class="field-optional">选填</span>
                  <div class="rule-setting-card__actions">
                    <el-button type="text" icon="el-icon-plus" size="mini" @click="addTimeSlot">添加</el-button>
                    <el-button
                      type="text"
                      icon="el-icon-delete"
                      size="mini"
                      class="clear-all-btn"
                      :disabled="!hasFilledTimeSlots"
                      @click="clearTimeSlots"
                    >清空</el-button>
                  </div>
                </div>
                <div class="rule-setting-card__body rule-setting-card__body--dense">
                  <div class="time-slot-list time-slot-list--dense time-slot-list--horizontal">
                    <div v-for="(item, index) in form.timeSlots" :key="'t' + index" class="time-slot-card time-slot-card--dense time-slot-card--inline">
                      <span class="time-slot-card__index">{{ index + 1 }}</span>
                      <el-time-picker
                        v-model="item.start"
                        format="HH:mm"
                        value-format="HH:mm"
                        placeholder="开始"
                        size="mini"
                        clearable
                        class="time-slot-picker"
                        :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }"
                        @change="handleTimeSlotStartChange(item)"
                        @blur="handleTimeSlotsBlur"
                      />
                      <span class="time-slot-sep">-</span>
                      <el-time-picker
                        v-model="item.end"
                        format="HH:mm"
                        value-format="HH:mm"
                        placeholder="结束"
                        size="mini"
                        clearable
                        class="time-slot-picker"
                        :picker-options="getEndPickerOptions(item.start)"
                        @change="handleTimeSlotEndChange(item)"
                        @blur="handleTimeSlotsBlur"
                      />
                      <el-button
                        type="text"
                        icon="el-icon-delete"
                        class="dynamic-del"
                        :disabled="form.timeSlots.length <= 1"
                        @click="removeTimeSlot(index)"
                      />
                    </div>
                  </div>
                </div>
              </div>

              <div ref="weekdaySection" class="rules-matrix__p3 rule-setting-card rule-setting-card--dense">
                <div class="rule-setting-card__head">
                  <span class="rule-priority rule-priority--3">P3</span>
                  <span class="rule-setting-card__label"><i class="el-icon-date" />每周屏蔽</span>
                  <span class="field-optional">选填</span>
                  <span v-if="selectedWeekdayLabels.length" class="weekday-head-count">已选 {{ selectedWeekdayLabels.length }} 天</span>
                </div>
                <el-form-item prop="weekdayList" class="rule-setting-card__body rule-setting-card__body--dense">
                  <el-checkbox-group v-model="form.weekdayList" class="weekday-checkboxes">
                    <el-checkbox-button v-for="item in weekdayOptions" :key="item.value" :label="item.value">{{ item.label }}</el-checkbox-button>
                  </el-checkbox-group>
                </el-form-item>
              </div>

              <div class="rules-matrix__remark rule-setting-card rule-setting-card--dense rule-setting-card--remark">
                <div class="rule-setting-card__head">
                  <span class="rule-setting-card__label"><i class="el-icon-document" />备注</span>
                  <span class="field-optional">选填</span>
                </div>
                <div class="rule-setting-card__body rule-setting-card__body--dense rule-setting-card__body--remark">
                  <el-input
                    v-model="form.remark"
                    type="textarea"
                    :rows="4"
                    maxlength="500"
                    show-word-limit
                    placeholder="请输入备注信息"
                    resize="none"
                  />
                </div>
              </div>

            <div ref="statusSection" class="rules-matrix__footer">
              <div class="rules-matrix__footer-left">
                <span class="rules-matrix__footer-label field-label--required">规则状态</span>
                <el-form-item prop="status" class="rules-matrix__footer-field">
                  <el-radio-group v-model="form.status" size="mini" class="rule-radio-btns">
                    <el-radio-button label="0">启用</el-radio-button>
                    <el-radio-button label="1">禁用</el-radio-button>
                  </el-radio-group>
                </el-form-item>
              </div>
            </div>
          </div>
        </div>
      </el-form>
        <div class="rules-dialog-form-actions">
          <el-button @click="cancel" :disabled="submitLoading">取 消</el-button>
          <el-button type="primary" :loading="submitLoading" :disabled="previewHasBlockingIssues" @click="submitForm">确 定</el-button>
        </div>
        </div>

        <aside class="rules-dialog-layout__preview">
          <div class="rule-preview-panel">
            <div class="rule-preview-panel__header">
              <i class="el-icon-view" />
              <span>规则预览</span>
            </div>

            <div class="rule-preview-panel__body">
              <div v-if="!isFormFallbackRule" class="preview-section">
                <div v-if="previewTaskNames.length" class="preview-section__title">
                  <i class="el-icon-link" />绑定任务
                  <span class="preview-section__count">{{ previewTaskNames.length }} 项</span>
                </div>
                <div v-if="previewTaskNames.length" class="preview-section__chips preview-section__chips--scroll">
                  <el-tag
                    v-for="(name, i) in previewTaskNames"
                    :key="'pt' + i"
                    size="mini"
                    type="primary"
                    effect="plain"
                    class="preview-chip preview-chip--task"
                    :title="name"
                  >{{ name }}</el-tag>
                </div>
                <div v-else class="preview-rule-item preview-rule-item--inline">
                  <span class="preview-rule-item__name"><i class="el-icon-link" />绑定任务</span>
                  <span class="rule-chip rule-chip--none preview-default-chip">{{ previewDefaultText.task }}</span>
                </div>
              </div>

              <div class="preview-section preview-section--rules">
                <div class="preview-section__title">
                  <i class="el-icon-setting" />屏蔽规则
                </div>

                <div class="preview-rule-item preview-rule-item--inline">
                  <span class="rule-priority rule-priority--1">P1</span>
                  <span class="preview-rule-item__name"><i class="el-icon-sunny" />节假日</span>
                  <span
                    :class="[
                      'preview-rule-status',
                      form.ruleOne === '1' ? 'preview-rule-status--block' : 'preview-rule-status--allow'
                    ]"
                  >{{ form.ruleOne === '1' ? '屏蔽' : '不屏蔽' }}</span>
                </div>

                <div :class="['preview-rule-item', { 'preview-rule-item--inline': !previewFilledSpecialDates.length }]">
                  <div class="preview-rule-item__row">
                    <span class="rule-priority rule-priority--2">P2</span>
                    <span class="preview-rule-item__name"><i class="el-icon-calendar" />特殊日期</span>
                    <span v-if="previewFilledSpecialDates.length" class="preview-rule-item__count">{{ previewFilledSpecialDates.length }} 段</span>
                    <span v-else class="rule-chip rule-chip--none preview-default-chip">{{ previewDefaultText.unlimited }}</span>
                  </div>
                  <div v-if="previewFilledSpecialDates.length" class="preview-rule-item__body preview-rule-item__body--scroll">
                    <span
                      v-for="(date, i) in previewFilledSpecialDates"
                      :key="'pd' + i"
                      class="rule-chip rule-chip--date"
                    >{{ date }}</span>
                  </div>
                </div>

                <div :class="['preview-rule-item', { 'preview-rule-item--inline': !selectedWeekdayLabels.length }]">
                  <div class="preview-rule-item__row">
                    <span class="rule-priority rule-priority--3">P3</span>
                    <span class="preview-rule-item__name"><i class="el-icon-date" />每周</span>
                    <span v-if="selectedWeekdayLabels.length" class="preview-rule-item__count">{{ selectedWeekdayLabels.length }} 天</span>
                    <span v-else class="rule-chip rule-chip--none preview-default-chip">{{ previewDefaultText.unlimited }}</span>
                  </div>
                  <div v-if="selectedWeekdayLabels.length" class="preview-rule-item__body preview-rule-item__body--week">
                    <span
                      v-for="(label, i) in selectedWeekdayLabels"
                      :key="'pw' + i"
                      class="rule-chip rule-chip--week preview-weekday-chip"
                    >{{ label }}</span>
                  </div>
                </div>

                <div :class="['preview-rule-item', { 'preview-rule-item--inline': !previewFilledTimeSlots.length }]">
                  <div class="preview-rule-item__row">
                    <span class="rule-priority rule-priority--4">P4</span>
                    <span class="preview-rule-item__name"><i class="el-icon-time" />每天时段</span>
                    <span v-if="previewFilledTimeSlots.length" class="preview-rule-item__count">{{ previewFilledTimeSlots.length }} 段</span>
                    <span v-else class="rule-chip rule-chip--none preview-default-chip">{{ previewDefaultText.unlimited }}</span>
                  </div>
                  <div v-if="previewFilledTimeSlots.length" class="preview-rule-item__body preview-time-grid">
                    <span
                      v-for="(slot, i) in previewFilledTimeSlots"
                      :key="'pts' + i"
                      class="rule-chip rule-chip--time preview-time-grid__item"
                    >{{ slot }}</span>
                  </div>
                </div>
              </div>

              <div class="preview-section preview-section--meta">
                <div class="preview-rule-item preview-rule-item--inline preview-rule-item--status">
                  <span class="preview-rule-item__name">规则状态</span>
                  <span :class="['status-badge', form.status === '0' ? 'status-badge--on' : 'status-badge--off']">
                    <i class="status-badge__dot" />
                    {{ form.status === '0' ? '启用' : '禁用' }}
                  </span>
                </div>
              </div>
            </div>

            <div class="rule-preview-panel__footer">
              <div v-if="previewNoticeList.length" class="preview-notices">
                <div
                  v-for="(notice, i) in previewNoticeList"
                  :key="'notice' + i"
                  :class="['preview-notice', 'preview-notice--' + notice.type]"
                >
                  <i :class="notice.icon" />
                  <span>{{ notice.text }}</span>
                </div>
              </div>
              <div v-else class="preview-notices preview-notices--ok">
                <i class="el-icon-circle-check" />
                <span>当前配置校验通过</span>
              </div>
            </div>
          </div>
        </aside>
      </div>
    </el-dialog>

    <!-- 立即执行规则 - 加载中 -->
    <el-dialog
      title="执行规则"
      :visible.sync="syncReportLoadingVisible"
      width="420px"
      append-to-body
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      custom-class="sync-report-loading-dialog"
    >
      <div class="sync-report-loading">
        <i class="el-icon-loading sync-report-loading__icon" />
        <p class="sync-report-loading__text">正在执行，生成报告中...</p>
        <p class="sync-report-loading__hint">请稍候，执行期间无法关闭</p>
      </div>
    </el-dialog>

    <!-- 立即执行规则 - 执行报告 -->
    <el-dialog
      title="执行报告"
      :visible.sync="syncReportDialog.visible"
      width="680px"
      append-to-body
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      custom-class="sync-report-dialog"
    >
      <div class="sync-report-content">
        <pre v-if="syncReportText" class="sync-report-pre">{{ syncReportText }}</pre>
        <div v-else class="sync-report-empty">暂无执行报告内容</div>
      </div>
      <div slot="footer">
        <el-button type="primary" @click="closeSyncReportDialog">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { listRules, getRules, delRules, addRules, updateRules, exportRules, checkBatchIds, syncRule } from '@/api/aiSipCall/aiSipAutoCallRoles'
import { listAiSipCallTask } from '@/api/aiSipCall/aiSipCallTask'

const PREVIEW_DEFAULT_TEXT = {
  task: '暂无绑定任务',
  unlimited: '不限制'
}

const WEEKDAY_OPTIONS = [
  { label: '周一', value: '1' },
  { label: '周二', value: '2' },
  { label: '周三', value: '3' },
  { label: '周四', value: '4' },
  { label: '周五', value: '5' },
  { label: '周六', value: '6' },
  { label: '周日', value: '7' }
]

const WEEKDAY_MAP = WEEKDAY_OPTIONS.reduce((map, item) => {
  map[item.value] = item.label
  return map
}, {})

const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/

function timeToMinutes(time) {
  if (!time) return 0
  const [h, m] = time.split(':').map(Number)
  return h * 60 + m
}

function parseTimeRanges(str) {
  if (!str) return []
  return String(str).split(',').map(s => s.trim()).filter(Boolean).map(segment => {
    const [start, end] = segment.split('-').map(v => (v || '').trim())
    return { start, end }
  }).filter(slot => slot.start && slot.end)
}

function serializeTimeRanges(slots) {
  return (slots || [])
    .filter(s => s.start && s.end)
    .sort((a, b) => timeToMinutes(a.start) - timeToMinutes(b.start))
    .map(s => `${s.start}-${s.end}`)
    .join(',')
}

function validateTimeSlotsList(slots) {
  if (!slots || !slots.length) return ''
  for (let i = 0; i < slots.length; i++) {
    const { start, end } = slots[i]
    if ((start && !end) || (!start && end)) {
      return `第 ${i + 1} 个时段请填写完整`
    }
    if (!start || !end) continue
    if (timeToMinutes(start) >= timeToMinutes(end)) {
      return `第 ${i + 1} 个时段开始时间须早于结束时间`
    }
  }
  const filled = slots.filter(s => s.start && s.end)
  const keys = filled.map(s => `${s.start}-${s.end}`)
  if (new Set(keys).size !== keys.length) return '存在重复的屏蔽时段'
  const sorted = [...filled].sort((a, b) => timeToMinutes(a.start) - timeToMinutes(b.start))
  for (let i = 1; i < sorted.length; i++) {
    if (timeToMinutes(sorted[i].start) < timeToMinutes(sorted[i - 1].end)) {
      return '每天屏蔽时段存在交集，请调整'
    }
  }
  return ''
}

function isValidDateStr(dateStr) {
  if (!DATE_PATTERN.test(dateStr)) return false
  const [y, m, d] = dateStr.split('-').map(Number)
  const date = new Date(y, m - 1, d)
  return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d
}

function parseSpecialDates(str) {
  if (!str) return []
  return String(str).split(',').map(s => s.trim()).filter(Boolean)
}

function nextDayStr(dateStr) {
  const d = new Date(dateStr)
  d.setDate(d.getDate() + 1)
  const y = d.getFullYear()
  const m = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')
  return `${y}-${m}-${day}`
}

function mergeConsecutiveDateRanges(dates) {
  const sorted = [...new Set(dates)].filter(isValidDateStr).sort()
  if (!sorted.length) return []
  const ranges = []
  let start = sorted[0]
  let end = sorted[0]
  for (let i = 1; i < sorted.length; i++) {
    if (sorted[i] === nextDayStr(end)) {
      end = sorted[i]
    } else {
      ranges.push({ start, end })
      start = sorted[i]
      end = sorted[i]
    }
  }
  ranges.push({ start, end })
  return ranges
}

function expandDateRange(start, end) {
  const dates = []
  const cur = new Date(start)
  const last = new Date(end)
  cur.setHours(0, 0, 0, 0)
  last.setHours(0, 0, 0, 0)
  if (cur.getTime() > last.getTime()) return dates
  while (cur.getTime() <= last.getTime()) {
    const y = cur.getFullYear()
    const m = String(cur.getMonth() + 1).padStart(2, '0')
    const d = String(cur.getDate()).padStart(2, '0')
    dates.push(`${y}-${m}-${d}`)
    cur.setDate(cur.getDate() + 1)
  }
  return dates
}

function parseLegacyDateRanges(str) {
  if (!str) return []
  return String(str).split(';').map(s => s.trim()).filter(Boolean).map(part => {
    const [start, end] = part.split(',').map(v => (v || '').trim())
    return { start, end }
  }).filter(r => r.start && r.end)
}

function serializeSpecialDateRanges(ranges) {
  const allDates = []
  ;(ranges || []).filter(r => r.start && r.end).forEach(r => {
    if (r.start === r.end) {
      allDates.push(r.start)
    } else {
      allDates.push(...expandDateRange(r.start, r.end))
    }
  })
  return [...new Set(allDates)].sort().join(',')
}

function validateSpecialDateRanges(ranges) {
  if (!ranges || !ranges.length) return ''
  for (let i = 0; i < ranges.length; i++) {
    const { start, end } = ranges[i]
    if ((start && !end) || (!start && end)) {
      return `第 ${i + 1} 个日期请填写完整`
    }
    if (!start || !end) continue
    if (!isValidDateStr(start) || !isValidDateStr(end)) {
      return `第 ${i + 1} 个日期格式不正确，请使用 yyyy-MM-dd`
    }
    if (start > end) {
      return `第 ${i + 1} 个日期开始不能晚于结束`
    }
  }
  const filled = ranges.filter(r => r.start && r.end)
  const keys = filled.map(r => `${r.start}-${r.end}`)
  if (new Set(keys).size !== keys.length) return '存在重复的特殊日期'
  const sorted = [...filled].sort((a, b) => a.start.localeCompare(b.start))
  for (let i = 1; i < sorted.length; i++) {
    if (sorted[i].start <= sorted[i - 1].end) {
      return '特殊日期存在交集，请调整'
    }
  }
  return ''
}

function normalizeSpecialDatesInput(str) {
  if (!str) return []
  if (String(str).includes(';')) {
    return parseLegacyDateRanges(str)
  }
  return mergeConsecutiveDateRanges(parseSpecialDates(str))
}

function parseIdList(ids) {
  if (ids == null || ids === '') return []
  if (Array.isArray(ids)) return ids.map(Number).filter(n => !isNaN(n))
  return String(ids).split(',').map(s => s.trim()).filter(Boolean).map(Number).filter(n => !isNaN(n))
}

function createSpecialDateItem(start = '', end = '') {
  return {
    start,
    end,
    dateRange: start && end ? [start, end] : null
  }
}

function dedupeSpecialDateRanges(ranges) {
  const seen = new Set()
  const result = []
  ;(ranges || []).forEach(range => {
    if (!range.start || !range.end) {
      result.push(range)
      return
    }
    const key = `${range.start}-${range.end}`
    if (seen.has(key)) return
    seen.add(key)
    result.push(range)
  })
  return result
}

function formatSyncReportText(data) {
  if (data == null) return ''
  if (typeof data === 'string') return data
  if (typeof data === 'object') {
    try {
      return JSON.stringify(data, null, 2)
    } catch (e) {
      return String(data)
    }
  }
  return String(data)
}

export default {
  name: 'aiSipAutoCallRoles',
  data() {
    return {
      loading: true,
      exportLoading: false,
      syncRuleLoading: false,
      syncReportLoadingVisible: false,
      syncReportDialog: {
        visible: false,
        data: null
      },
      taskList: [],
      taskMap: {},
      ids: [],
      single: true,
      deleteDisabled: true,
      selectedRows: [],
      showSearch: true,
      ruleNoticeExpanded: false,
      total: 0,
      rulesList: [],
      title: '',
      open: false,
      submitLoading: false,
      timeSlotError: '',
      dateError: '',
      batchDuplicateWarning: '',
      batchCheckSeq: 0,
      weekdayOptions: WEEKDAY_OPTIONS,
      previewDefaultText: PREVIEW_DEFAULT_TEXT,
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        batchId: null,
        ruleOne: '',
        ruleThree: '',
        status: ''
      },
      form: {},
      formRules: {
        batchIdList: [{ type: 'array', required: true, min: 1, message: '请选择任务名称', trigger: 'change' }],
        ruleOne: [{ required: true, message: '请选择节假日是否屏蔽', trigger: 'change' }],
        status: [{ required: true, message: '请选择状态', trigger: 'change' }]
      }
    }
  },
  computed: {
    activeFilterCount() {
      const q = this.queryParams
      let count = 0
      if (q.batchId != null && q.batchId !== '') count++
      if (q.ruleOne !== '' && q.ruleOne != null) count++
      if (q.ruleThree !== '' && q.ruleThree != null) count++
      if (q.status !== '' && q.status != null) count++
      return count
    },
    /** 修改兜底规则或受保护规则时锁定任务绑定 */
    isTaskNameLocked() {
      return this.form.id != null && this.isRowProtected(this.form)
    },
    dialogFormRules() {
      const rules = { ...this.formRules }
      if (this.isTaskNameLocked) {
        delete rules.batchIdList
      }
      return rules
    },
    selectedTaskItems() {
      return (this.form.batchIdList || []).map(id => ({
        batchId: Number(id),
        batchName: this.getTaskName(id)
      }))
    },
    selectedWeekdayLabels() {
      const values = [...new Set(this.form.weekdayList || [])]
      return values
        .sort((a, b) => Number(a) - Number(b))
        .map(v => WEEKDAY_MAP[v] || `周${v}`)
    },
    isFormFallbackRule() {
      return this.isFallbackRule(this.form)
    },
    previewTaskNames() {
      if (this.isTaskNameLocked && this.form.batchNames) {
        return String(this.form.batchNames).split(/[,、]/).map(s => s.trim()).filter(Boolean)
      }
      return this.selectedTaskItems.map(item => item.batchName)
    },
    previewFilledSpecialDates() {
      return (this.form.specialDateRanges || [])
        .filter(r => r.start && r.end)
        .map(r => `${r.start} ~ ${r.end}`)
    },
    previewFilledTimeSlots() {
      return (this.form.timeSlots || [])
        .filter(s => s.start && s.end)
        .map(s => `${s.start}-${s.end}`)
    },
    hasFilledSpecialDates() {
      return (this.form.specialDateRanges || []).some(r => r.start || r.end)
    },
    hasFilledTimeSlots() {
      return (this.form.timeSlots || []).some(s => s.start || s.end)
    },
    previewHasSecondaryRules() {
      return this.previewFilledSpecialDates.length > 0
        || this.selectedWeekdayLabels.length > 0
        || this.previewFilledTimeSlots.length > 0
    },
    previewNoticeList() {
      const notices = []
      if (this.dateError) {
        notices.push({ type: 'error', icon: 'el-icon-circle-close', text: this.dateError })
      }
      if (this.timeSlotError) {
        notices.push({ type: 'error', icon: 'el-icon-circle-close', text: this.timeSlotError })
      }
      if (this.batchDuplicateWarning) {
        notices.push({ type: 'warning', icon: 'el-icon-warning-outline', text: this.batchDuplicateWarning })
      }
      if (this.form.ruleOne === '0' && !this.previewHasSecondaryRules) {
        notices.push({ type: 'warning', icon: 'el-icon-warning-outline', text: '节假日不屏蔽时，P2/P3/P4 至少需配置一项' })
      }
      return notices
    },
    previewHasBlockingIssues() {
      const missingSecondaryRules = String(this.form.ruleOne) === '0' && !this.previewHasSecondaryRules
      return !!(this.dateError || this.timeSlotError || this.batchDuplicateWarning || missingSecondaryRules)
    },
    syncReportText() {
      return formatSyncReportText(this.syncReportDialog.data)
    }
  },
  created() {
    this.loadTaskList()
    this.getList()
  },
  mounted() {
    window.addEventListener('resize', this.layoutTable)
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.layoutTable)
  },
  methods: {
    layoutTable() {
      this.$nextTick(() => {
        this.$refs.rulesTable && this.$refs.rulesTable.doLayout()
      })
    },
    getTaskName(batchId) {
      const task = this.taskMap[Number(batchId)]
      return task ? task.batchName : `任务#${batchId}`
    },
    loadTaskList() {
      listAiSipCallTask({ taskType: 1, status: 0, pageNum: 1, pageSize: 500 }).then(response => {
        this.taskList = response.rows || []
        const map = {}
        this.taskList.forEach(t => { map[Number(t.batchId)] = t })
        this.taskMap = map
      }).catch(() => {
        this.taskList = []
        this.taskMap = {}
      })
    },
    enrichRow(row) {
      const tasks = row.batchNames
        ? String(row.batchNames).split(/[,、]/).map(s => s.trim()).filter(Boolean)
        : parseIdList(row.batchIds).map(id => this.getTaskName(id))
      const weekdays = row.ruleThree
        ? String(row.ruleThree).split(',').map(v => v.trim()).filter(Boolean).map(v => WEEKDAY_MAP[v] || `周${v}`)
        : []
      const dateRanges = normalizeSpecialDatesInput(row.ruleTwo)
      return {
        ...row,
        _tasks: tasks,
        _weekdays: weekdays,
        _dates: dateRanges.map(r => (r.start === r.end ? r.start : `${r.start} ~ ${r.end}`)),
        _times: parseTimeRanges(row.ruleFour)
      }
    },
    buildQueryParams() {
      const params = { ...this.queryParams }
      if (params.ruleOne === '') params.ruleOne = null
      if (params.ruleThree === '') params.ruleThree = null
      if (params.status === '') params.status = null
      return params
    },
    getEndPickerOptions(start) {
      if (!start) {
        return { selectableRange: '00:00:00 - 23:59:59' }
      }
      const [h, m] = start.split(':').map(Number)
      let totalMin = h * 60 + m + 1
      if (totalMin >= 24 * 60) {
        return { selectableRange: '23:59:00 - 23:59:59' }
      }
      const eh = Math.floor(totalMin / 60)
      const em = totalMin % 60
      const minTime = `${String(eh).padStart(2, '0')}:${String(em).padStart(2, '0')}:00`
      return { selectableRange: `${minTime} - 23:59:59` }
    },
    handleTimeSlotStartChange(item) {
      this.timeSlotError = ''
      if (item.start && item.end && timeToMinutes(item.end) <= timeToMinutes(item.start)) {
        item.end = ''
      }
    },
    handleTimeSlotEndChange() {
      this.normalizeTimeSlots()
    },
    sortTimeSlotsInPlace() {
      const slots = this.form.timeSlots || []
      const complete = slots.filter(s => s.start && s.end)
        .sort((a, b) => timeToMinutes(a.start) - timeToMinutes(b.start))
      const incomplete = slots.filter(s => !s.start || !s.end)
      this.form.timeSlots = [...complete, ...incomplete]
    },
    normalizeTimeSlots() {
      this.sortTimeSlotsInPlace()
      this.timeSlotError = validateTimeSlotsList(this.form.timeSlots)
    },
    handleSpecialDateRangeChange(item) {
      this.dateError = ''
      if (item.dateRange && item.dateRange.length === 2) {
        item.start = item.dateRange[0]
        item.end = item.dateRange[1]
      } else {
        item.start = ''
        item.end = ''
        item.dateRange = null
      }
      this.normalizeSpecialDates()
    },
    sortSpecialDateRangesInPlace() {
      const ranges = this.form.specialDateRanges || []
      const complete = ranges.filter(r => r.start && r.end)
        .sort((a, b) => a.start.localeCompare(b.start))
      const incomplete = ranges.filter(r => !r.start || !r.end)
      this.form.specialDateRanges = dedupeSpecialDateRanges([...complete, ...incomplete])
    },
    normalizeSpecialDates() {
      const ranges = this.form.specialDateRanges || []
      const hasAny = ranges.some(r => r.start || r.end)
      if (!hasAny) {
        this.dateError = ''
        return
      }
      this.sortSpecialDateRangesInPlace()
      this.dateError = validateSpecialDateRanges(this.form.specialDateRanges)
    },
    handleTimeSlotsBlur() {
      this.normalizeTimeSlots()
    },
    handleBatchIdListChange() {
      this.$refs.form && this.$refs.form.validateField('batchIdList')
      this.checkBatchIdsDuplicate()
    },
    removeSelectedTask(batchId) {
      this.form.batchIdList = (this.form.batchIdList || []).filter(id => Number(id) !== Number(batchId))
      this.handleBatchIdListChange()
    },
    /** 校验所选任务是否已绑定其他屏蔽规则 */
    checkBatchIdsDuplicate() {
      const batchIdList = this.form.batchIdList || []
      const seq = ++this.batchCheckSeq
      if (!batchIdList.length) {
        this.batchDuplicateWarning = ''
        return Promise.resolve(true)
      }
      const excludeId = this.form.id != null ? this.form.id : null
      return checkBatchIds(batchIdList, excludeId).then(res => {
        if (seq !== this.batchCheckSeq) return false
        const duplicates = Array.isArray(res.data) ? res.data : []
        if (duplicates.length) {
          this.batchDuplicateWarning = `以下任务已存在屏蔽规则：${duplicates.join('、')}`
          return false
        }
        this.batchDuplicateWarning = ''
        return true
      }).catch(() => {
        if (seq !== this.batchCheckSeq) return false
        this.batchDuplicateWarning = '任务重复校验失败，请稍后重试'
        return false
      })
    },
    addSpecialDate() {
      if (this.dateError) {
        this.scrollToFormSection('specialDatesSection')
        return
      }
      this.form.specialDateRanges.push(createSpecialDateItem())
    },
    removeSpecialDate(index) {
      if (this.form.specialDateRanges.length > 1) {
        this.form.specialDateRanges.splice(index, 1)
        this.normalizeSpecialDates()
      }
    },
    clearSpecialDates() {
      if (!this.hasFilledSpecialDates) return
      this.$confirm('确定清空所有特殊日期配置吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.form.specialDateRanges = [createSpecialDateItem()]
        this.dateError = ''
      }).catch(() => {})
    },
    addTimeSlot() {
      if (this.timeSlotError) {
        this.scrollToFormSection('timeSlotsSection')
        return
      }
      this.form.timeSlots.push({ start: '', end: '' })
    },
    removeTimeSlot(index) {
      if (this.form.timeSlots.length > 1) {
        this.form.timeSlots.splice(index, 1)
        this.normalizeTimeSlots()
      }
    },
    clearTimeSlots() {
      if (!this.hasFilledTimeSlots) return
      this.$confirm('确定清空所有每天时段配置吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.form.timeSlots = [{ start: '', end: '' }]
        this.timeSlotError = ''
      }).catch(() => {})
    },
    getDefaultForm() {
      return {
        id: null,
        batchIdList: [],
        batchIds: null,
        batchNames: null,
        weekdayList: [],
        specialDateRanges: [createSpecialDateItem()],
        timeSlots: [{ start: '', end: '' }],
        ruleOne: '0',
        isAllowDel: '0',
        status: '0',
        remark: null
      }
    },
    fillFormFromData(data) {
      const batchIdList = parseIdList(data.batchIds)
      const weekdayList = data.ruleThree ? String(data.ruleThree).split(',').map(s => s.trim()).filter(Boolean) : []
      const timeSlots = parseTimeRanges(data.ruleFour)
      const specialDateRanges = normalizeSpecialDatesInput(data.ruleTwo)
      this.form = {
        id: data.id,
        batchIdList,
        batchIds: data.batchIds,
        batchNames: data.batchNames,
        weekdayList,
        specialDateRanges: specialDateRanges.length
          ? specialDateRanges.map(r => createSpecialDateItem(r.start, r.end))
          : [createSpecialDateItem()],
        timeSlots: timeSlots.length ? timeSlots : [{ start: '', end: '' }],
        ruleOne: data.ruleOne != null ? String(data.ruleOne) : '0',
        isAllowDel: data.isAllowDel != null ? String(data.isAllowDel) : '0',
        status: data.status != null ? String(data.status) : '0',
        remark: data.remark
      }
      this.timeSlotError = ''
      this.dateError = ''
      this.batchDuplicateWarning = ''
      this.$nextTick(() => {
        this.$refs.form && this.$refs.form.clearValidate('batchIdList')
        if (batchIdList.length && !this.isTaskNameLocked) {
          this.checkBatchIdsDuplicate()
        }
      })
    },
    buildBatchNames(batchIdList) {
      return (batchIdList || []).map(id => this.getTaskName(id)).join('、')
    },
    buildSubmitPayload() {
      const ruleTwo = serializeSpecialDateRanges(this.form.specialDateRanges) || null
      const ruleThree = (this.form.weekdayList || []).slice().sort().join(',') || null
      const ruleFour = serializeTimeRanges(this.form.timeSlots)
      // 兜底规则 / 受保护规则：保持原有绑定，禁止前端改写；其他规则按当前选择提交
      const batchIds = this.isTaskNameLocked
        ? this.form.batchIds
        : (this.form.batchIdList || []).slice().sort((a, b) => a - b).join(',')
      const batchNames = this.isTaskNameLocked
        ? this.form.batchNames
        : this.buildBatchNames(this.form.batchIdList)

      return {
        id: this.form.id,
        batchIds,
        batchNames,
        ruleOne: this.form.ruleOne,
        ruleTwo: ruleTwo || null,
        ruleThree: ruleThree || null,
        ruleFour: ruleFour || null,
        isAllowDel: this.form.isAllowDel != null ? this.form.isAllowDel : '0',
        status: this.form.status,
        remark: this.form.remark
      }
    },
    validateShieldRules() {
      if (String(this.form.ruleOne) !== '0') return true
      const hasWeek = this.form.weekdayList && this.form.weekdayList.length > 0
      const hasDate = this.form.specialDateRanges.some(r => r.start && r.end)
      const hasTime = this.form.timeSlots.some(r => r.start && r.end)
      if (!hasWeek && !hasDate && !hasTime) {
        this.$message.warning('节假日不屏蔽时，特殊日期、每周或每天时段至少需配置一项')
        this.scrollToFormSection('weekdaySection')
        return false
      }
      return true
    },
    scrollToFormSection(refName) {
      this.$nextTick(() => {
        const ref = this.$refs[refName]
        const el = ref && ref.$el ? ref.$el : ref
        if (el && el.scrollIntoView) {
          el.scrollIntoView({ behavior: 'smooth', block: 'center' })
        }
      })
    },
    scrollToFirstFormError(invalidFields) {
      if (!invalidFields) return
      const props = Object.keys(invalidFields)
      if (!props.length) return
      const prop = props[0]
      const sectionMap = {
        batchIdList: 'batchIdListSection',
        ruleOne: 'ruleOneSection',
        status: 'statusSection'
      }
      if (sectionMap[prop]) {
        this.scrollToFormSection(sectionMap[prop])
        return
      }
      const form = this.$refs.form
      if (form && form.fields) {
        const field = form.fields.find(f => f.prop === prop)
        if (field && field.$el) {
          field.$el.scrollIntoView({ behavior: 'smooth', block: 'center' })
        }
      }
    },
    formatRangeLabel(range) {
      if (!range || !range.start || !range.end) return ''
      return `${range.start}-${range.end}`
    },
    isFallbackRule(row) {
      return row && (row.id === 0 || row.id === '0')
    },
    isRuleOneBlocked(row) {
      return row && (row.ruleOne === 1 || row.ruleOne === '1')
    },
    isStatusEnabled(row) {
      return row && (row.status === 0 || row.status === '0')
    },
    isDeleteAllowed(row) {
      return row && row.isAllowDel !== 1 && row.isAllowDel !== '1'
    },
    isRowProtected(row) {
      return this.isFallbackRule(row) || !this.isDeleteAllowed(row)
    },
    canDeleteRule(row) {
      return row && !this.isRowProtected(row)
    },
    getList() {
      this.loading = true
      listRules(this.buildQueryParams()).then(response => {
        this.rulesList = (response.rows || []).map(row => this.enrichRow(row))
        this.total = response.total
      }).catch(() => {
        this.rulesList = []
        this.total = 0
      }).finally(() => {
        this.loading = false
        this.layoutTable()
      })
    },
    cancel() {
      this.open = false
      this.reset()
    },
    reset() {
      this.batchCheckSeq += 1
      this.form = this.getDefaultForm()
      this.timeSlotError = ''
      this.dateError = ''
      this.batchDuplicateWarning = ''
      this.resetForm('form')
    },
    handleQuery() {
      this.queryParams.pageNum = 1
      this.getList()
    },
    resetQuery() {
      this.resetForm('queryForm')
      this.queryParams.batchId = null
      this.queryParams.ruleOne = ''
      this.queryParams.ruleThree = ''
      this.queryParams.status = ''
      this.handleQuery()
    },
    handleSelectionChange(selection) {
      this.selectedRows = selection
      this.ids = selection.map(item => item.id)
      this.single = selection.length !== 1
      this.deleteDisabled = !selection.length || selection.some(item => !this.canDeleteRule(item))
    },
    handleAdd() {
      this.reset()
      this.open = true
      this.title = '新增外呼屏蔽规则'
    },
    handleUpdate(row) {
      this.reset()
      const id = row && Object.prototype.hasOwnProperty.call(row, 'id') ? row.id : (Array.isArray(this.ids) ? this.ids[0] : this.ids)
      if (id == null || id === '') {
        this.$message.warning('请选择要修改的数据')
        return
      }
      getRules(id).then(response => {
        this.fillFormFromData(response.data || {})
        this.open = true
        this.title = '修改外呼屏蔽规则'
      }).catch(() => {})
    },
    submitForm() {
      if (this.submitLoading) return
      if (this.previewHasBlockingIssues) {
        if (this.batchDuplicateWarning) {
          this.scrollToFormSection('batchIdListSection')
        } else if (this.dateError) {
          this.scrollToFormSection('specialDatesSection')
        } else if (this.timeSlotError) {
          this.scrollToFormSection('timeSlotsSection')
        } else {
          this.scrollToFormSection('weekdaySection')
        }
        return
      }
      this.normalizeSpecialDates()
      this.normalizeTimeSlots()
      if (this.dateError || this.timeSlotError) {
        this.scrollToFormSection(this.dateError ? 'specialDatesSection' : 'timeSlotsSection')
        return
      }
      this.$refs.form.validate((valid, invalidFields) => {
        if (!valid) {
          this.scrollToFirstFormError(invalidFields)
          return
        }
        if (!this.validateShieldRules()) return

        this.timeSlotError = validateTimeSlotsList(this.form.timeSlots)
        if (this.timeSlotError) {
          this.scrollToFormSection('timeSlotsSection')
          return
        }
        this.dateError = validateSpecialDateRanges(this.form.specialDateRanges)
        if (this.dateError) {
          this.scrollToFormSection('specialDatesSection')
          return
        }

        const duplicateCheck = this.isTaskNameLocked
          ? Promise.resolve(true)
          : this.checkBatchIdsDuplicate()
        duplicateCheck.then(passed => {
          if (!passed) {
            this.scrollToFormSection('batchIdListSection')
            return
          }
          const payload = this.buildSubmitPayload()
          this.submitLoading = true
          const request = payload.id != null ? updateRules(payload) : addRules(payload)
          request.then(() => {
            this.msgSuccess(payload.id != null ? '修改成功' : '新增成功')
            this.open = false
            this.getList()
          }).catch(() => {}).finally(() => {
            this.submitLoading = false
          })
        }).catch(() => {})
      })
    },
    handleDelete(row) {
      const targets = row && row.id != null
        ? [row]
        : this.selectedRows.filter(item => this.ids.includes(item.id))

      if (!targets.length) {
        this.$message.warning('请选择要删除的数据')
        return
      }
      if (targets.some(item => !this.canDeleteRule(item))) {
        if (targets.some(item => this.isFallbackRule(item))) {
          this.$message.warning('兜底规则（ID=0）不可删除')
          return
        }
        this.$message.warning('所选规则包含受保护、不允许删除的数据')
        return
      }

      const ids = targets.map(item => item.id).join(',')
      this.$confirm(`是否确认删除编号为「${ids}」的规则？`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => delRules(ids)).then(() => {
        this.getList()
        this.msgSuccess('删除成功')
      }).catch(() => {})
    },
    handleExport() {
      this.$confirm('是否确认按当前筛选条件导出外呼屏蔽规则？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.exportLoading = true
        return exportRules(this.buildQueryParams())
      }).then(response => {
        this.download(response.msg)
      }).finally(() => {
        this.exportLoading = false
      }).catch(() => {})
    },
    handleSyncRule() {
      if (this.syncRuleLoading || this.syncReportLoadingVisible) return
      this.$confirm(
        '将对全部外呼屏蔽规则执行一次同步，与当前筛选条件和列表选中无关。执行期间可能影响外呼任务，请确认后再继续。',
        '立即执行规则',
        {
          confirmButtonText: '确定执行',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        this.syncReportLoadingVisible = true
        this.syncRuleLoading = true
        return syncRule()
      }).then(res => {
        if (!res) return
        if (res.code === 200) {
          this.syncReportDialog.data = res.data
          this.syncReportDialog.visible = true
        } else {
          this.$message.error(res.msg || '规则执行失败')
        }
      }).catch(err => {
        if (err === 'cancel' || err === 'close') return
        this.$message.error((err && err.msg) || '规则执行失败，请稍后重试')
      }).finally(() => {
        this.syncReportLoadingVisible = false
        this.syncRuleLoading = false
      })
    },
    closeSyncReportDialog() {
      this.syncReportDialog.visible = false
      this.syncReportDialog.data = null
    },
    handleDialogClose() {
      this.batchCheckSeq += 1
      this.timeSlotError = ''
      this.dateError = ''
      this.batchDuplicateWarning = ''
      this.submitLoading = false
    }
  }
}
</script>

<style scoped>
.rules-page {
  padding-bottom: 8px;
}

.page-card {
  margin-bottom: 12px;
  border: 1px solid #ebeef5;
  border-radius: 6px;
}

.page-card >>> .el-card__body {
  padding: 16px 20px;
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.card-head__title {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}

.card-head__extra {
  font-size: 12px;
  color: #909399;
}

.full-width {
  width: 100%;
}

.query-form >>> .el-form-item {
  margin-bottom: 8px;
}

.query-form >>> .el-form-item__label {
  padding-bottom: 4px;
  line-height: 1.2;
  font-size: 13px;
  color: #606266;
}

.filter-btns {
  display: flex;
  width: 100%;
}

.filter-btns >>> .el-radio-button {
  flex: 1;
}

.filter-btns >>> .el-radio-button__inner {
  width: 100%;
  padding: 8px 0;
}

.query-form__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 8px;
  margin-top: 4px;
  border-top: 1px solid #f0f2f5;
}

.table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.rule-notice {
  margin-bottom: 12px;
  padding: 12px 14px;
  background: #f4f8ff;
  border: 1px solid #d9ecff;
  border-radius: 6px;
}

.rule-notice__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  line-height: 1.5;
}

.rule-notice__icon {
  flex-shrink: 0;
  font-size: 16px;
  color: #409eff;
}

.rule-notice__title {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: #303133;
}

.rule-notice__brief {
  flex: 1;
  min-width: 200px;
  font-size: 12px;
  color: #606266;
}

.rule-notice__toggle {
  flex-shrink: 0;
  margin-left: auto;
  padding: 0;
  font-size: 12px;
}

.rule-notice__body {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed #d9ecff;
}

.rule-notice__list {
  margin: 0;
  padding-left: 20px;
  font-size: 13px;
  color: #606266;
  line-height: 1.8;
}

.rule-notice__list li {
  margin-bottom: 4px;
}

.rule-notice__list li:last-child {
  margin-bottom: 0;
}

.rule-notice__list strong {
  color: #303133;
  font-weight: 600;
}

.table-toolbar__left {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.sync-report-content {
  max-height: 420px;
  overflow: auto;
  padding: 4px 2px;
}

.sync-report-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 28px 16px 24px;
  text-align: center;
}

.sync-report-loading__icon {
  font-size: 42px;
  color: #409eff;
  margin-bottom: 16px;
}

.sync-report-loading__text {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 600;
  color: #303133;
}

.sync-report-loading__hint {
  margin: 0;
  font-size: 12px;
  color: #909399;
}

.sync-report-pre {
  margin: 0;
  padding: 12px 14px;
  background: #f8fafc;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  font-family: Consolas, Monaco, 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.7;
  color: #303133;
  white-space: pre-wrap;
  word-break: break-word;
}

.sync-report-empty {
  padding: 28px 12px;
  text-align: center;
  font-size: 13px;
  color: #c0c4cc;
}

.rules-table >>> .el-table__body .cell,
.rules-table >>> .el-table__header .cell {
  text-align: center;
}

/* 居中列：让自定义内容在单元格内水平居中 */
.rules-table >>> td.is-center .cell {
  display: flex;
  justify-content: center;
  align-items: center;
}

.rules-table >>> td.rule-col .cell {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 8px 10px;
}

.rule-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
  max-width: 100%;
  text-align: left;
}

.rule-list__item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: linear-gradient(90deg, #fafbfc 0%, #fff 100%);
  border: 1px solid #ebeef5;
  border-radius: 4px;
  line-height: 1.4;
}

.rule-priority {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 18px;
  padding: 0 4px;
  border-radius: 3px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  line-height: 1;
}

.rule-priority--1 { background: #e6a23c; }
.rule-priority--2 { background: #f56c6c; }
.rule-priority--3 { background: #409eff; }
.rule-priority--4 { background: #909399; }

.rule-list__label {
  flex-shrink: 0;
  width: 72px;
  font-size: 12px;
  font-weight: 600;
  color: #606266;
  white-space: nowrap;
}

.rule-list__label i {
  margin-right: 3px;
  color: #909399;
  font-size: 12px;
}

.rule-list__content {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.rule-val-tag {
  margin: 0;
}

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
}

.status-badge__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.status-badge--on {
  color: #67c23a;
  background: #f0f9eb;
  border: 1px solid #e1f3d8;
}

.status-badge--on .status-badge__dot {
  background: #67c23a;
  box-shadow: 0 0 4px rgba(103, 194, 58, 0.6);
}

.status-badge--off {
  color: #909399;
  background: #f4f4f5;
  border: 1px solid #e9e9eb;
}

.status-badge--off .status-badge__dot {
  background: #c0c4cc;
}

.protect-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
}

.protect-badge--allow {
  color: #409eff;
  background: #ecf5ff;
  border: 1px solid #d9ecff;
}

.protect-badge--deny {
  color: #e6a23c;
  background: #fdf6ec;
  border: 1px solid #faecd8;
}

.cell-fallback {
  display: flex;
  justify-content: center;
}

.fallback-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  color: #e6a23c;
  background: linear-gradient(135deg, #fdf6ec 0%, #fef0e6 100%);
  border: 1px solid #f5dab1;
  border-radius: 4px;
}

.fallback-badge i {
  font-size: 14px;
}

.rule-chip {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
  line-height: 20px;
  white-space: nowrap;
}

.rule-chip--week {
  background: #f4f4f5;
  color: #606266;
  border: 1px solid #e9e9eb;
}

.rule-chip--date {
  background: #fdf6ec;
  color: #b88230;
  border: 1px solid #faecd8;
}

.rule-chip--time {
  background: #ecf5ff;
  color: #409eff;
  border: 1px solid #d9ecff;
  font-family: Consolas, Monaco, monospace;
}

.rule-chip--none {
  background: #f0f9eb;
  color: #67c23a;
  border: 1px solid #e1f3d8;
}

.cell-tags {
  width: 100%;
  max-width: 100%;
  padding: 2px 0;
  line-height: 1.6;
}

.cell-tag {
  display: inline-block;
  padding: 0 8px;
  margin: 2px;
  border-radius: 3px;
  font-size: 12px;
  line-height: 22px;
  white-space: nowrap;
  vertical-align: middle;
  max-width: 100%;
  box-sizing: border-box;
}

.cell-tags .el-tooltip {
  display: inline-block;
  vertical-align: middle;
  max-width: calc(100% - 4px);
}

/* 至少显示 15 个汉字，超出再省略 */
.rules-table >>> .cell-tag--task.el-tag {
  max-width: 100%;
  min-width: 15em;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}

.cell-empty {
  color: #c0c4cc;
  font-size: 12px;
}

.dialog-form {
  overflow: visible;
  padding-right: 0;
}

.form-block--dense {
  margin-bottom: 8px;
  padding: 10px 12px 8px;
}

.form-block__title--dense {
  margin-bottom: 8px;
}

.rules-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.rules-dialog-head__priority {
  font-size: 11px;
  color: #909399;
  white-space: nowrap;
}

.rule-inline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
  padding: 8px 10px;
  background: #fff;
  border: 1px solid #ebeef5;
  border-radius: 6px;
}

.rule-inline__label {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: #606266;
}

.rule-inline__label i {
  margin-right: 4px;
  color: #909399;
}

.rule-inline__field {
  margin-bottom: 0 !important;
}

.rules-matrix {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
  grid-template-areas:
    "p2 p3"
    "p4 remark"
    "footer footer";
  gap: 10px;
  align-items: stretch;
}

.rules-matrix__p2 {
  grid-area: p2;
}

.rules-matrix__p3 {
  grid-area: p3;
}

.rules-matrix__p4 {
  grid-area: p4;
}

.rules-matrix__remark {
  grid-area: remark;
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

.rules-matrix__footer {
  grid-area: footer;
  padding: 8px 12px;
  background: linear-gradient(90deg, #f8fafc 0%, #fff 100%);
  border: 1px solid #ebeef5;
  border-radius: 6px;
}

.rules-matrix__footer-left {
  display: flex;
  align-items: center;
  gap: 14px;
}

.rules-matrix__footer-label {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: #606266;
}

.rules-matrix__footer-field {
  margin-bottom: 0 !important;
}

.rules-matrix .rule-setting-card {
  margin-bottom: 0;
}

.rules-dialog-layout__form {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.rules-dialog-form-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  padding: 10px 0 2px;
  margin-top: 4px;
}

.rule-setting-card--remark {
  display: flex;
  flex-direction: column;
}

.rule-setting-card__body--remark {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.rule-setting-card__body--remark >>> .el-textarea,
.rule-setting-card__body--remark >>> .el-textarea__inner {
  height: 100%;
  min-height: 108px;
}

.rule-setting-card--dense {
  margin-bottom: 8px;
}

.rule-setting-card__body--dense {
  padding: 8px 10px !important;
}

.rule-setting-card--dense .rule-setting-card__head {
  padding: 6px 10px;
}

.weekday-head-count {
  margin-left: auto;
  font-size: 11px;
  color: #409eff;
  font-weight: 500;
}

.time-slot-list--dense {
  gap: 6px;
  margin-bottom: 0;
}

.time-slot-list--horizontal {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

.time-slot-card--dense {
  padding: 6px 8px;
  gap: 5px;
}

.time-slot-card--inline {
  flex: 0 0 auto;
}

.time-slot-card--dense .time-slot-card__index {
  width: 18px;
  height: 18px;
  line-height: 18px;
  font-size: 11px;
}

.selected-task-list--compact {
  margin-top: 6px;
  max-height: 48px;
  overflow-y: auto;
}

.dialog-form >>> .el-form-item {
  margin-bottom: 0;
}

.form-block {
  margin-bottom: 10px;
  padding: 12px 14px 10px;
  background: #fafbfc;
  border: 1px solid #ebeef5;
  border-radius: 8px;
}

.form-block--rules {
  padding-bottom: 8px;
}

.form-block:last-child {
  margin-bottom: 0;
}

.form-block__title {
  margin-bottom: 6px;
  padding-left: 10px;
  font-size: 14px;
  font-weight: 600;
  color: #303133;
  line-height: 1.4;
  border-left: 3px solid #409eff;
}

.form-block__title i {
  margin-right: 6px;
  color: #409eff;
}

.rule-setting-card {
  margin-bottom: 8px;
  background: #fff;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  overflow: hidden;
}

.rule-setting-card:last-child {
  margin-bottom: 0;
}

.rule-setting-card__head {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  padding: 8px 12px;
  background: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
}

.rule-setting-card__actions {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
}

.clear-all-btn {
  color: #f56c6c !important;
}

.clear-all-btn.is-disabled {
  color: #c0c4cc !important;
}

.rules-dialog-layout {
  display: flex;
  align-items: stretch;
  gap: 14px;
}

.rules-dialog-layout__preview {
  flex-shrink: 0;
  width: 380px;
  align-self: stretch;
}

.rule-preview-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: linear-gradient(180deg, #f8fafc 0%, #fff 100%);
  border: 1px solid #e4e7ed;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
}

.rule-preview-panel__header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  font-size: 13px;
  font-weight: 600;
  color: #303133;
  background: linear-gradient(135deg, #ecf5ff 0%, #f0f9ff 100%);
  border-bottom: 1px solid #d9ecff;
}

.rule-preview-panel__header i {
  font-size: 15px;
  color: #409eff;
}

.rule-preview-panel__body {
  flex: 1;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 4px 10px;
}

.rule-preview-panel__footer {
  flex-shrink: 0;
  padding: 4px 10px 5px;
  background: #fafbfc;
  border-top: 1px solid #ebeef5;
}

.preview-section {
  margin-bottom: 5px;
  padding-bottom: 5px;
  border-bottom: 1px solid #f0f2f5;
}

.preview-section:last-child {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
}

.preview-section--rules {
  padding-bottom: 3px;
}

.preview-section--meta {
  padding-top: 0;
}

.preview-section__title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
  font-size: 12px;
  font-weight: 600;
  color: #606266;
}

.preview-section__title i {
  color: #909399;
}

.preview-section__count,
.preview-rule-item__count {
  margin-left: auto;
  font-size: 11px;
  font-weight: 500;
  color: #909399;
}

.preview-section__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.preview-section__chips--scroll {
  max-height: 72px;
  overflow-y: auto;
}

.preview-chip--task {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.preview-rule-item {
  margin-bottom: 4px;
  padding: 4px 8px;
  background: #fff;
  border: 1px solid #ebeef5;
  border-radius: 8px;
}

.preview-rule-item:last-child {
  margin-bottom: 0;
}

.preview-rule-item--inline {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 24px;
}

.preview-rule-item--inline.preview-rule-item--status {
  gap: 10px;
}

.preview-rule-item__row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 24px;
}

.preview-rule-item--inline .preview-rule-item__row {
  width: auto;
  flex: 1;
  min-width: 0;
}

.preview-rule-item__name {
  font-size: 12px;
  font-weight: 600;
  color: #606266;
  white-space: nowrap;
}

.preview-rule-item__name i {
  margin-right: 4px;
  color: #909399;
}

.preview-rule-status {
  display: inline-flex;
  align-items: center;
  padding: 1px 8px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 12px;
  white-space: nowrap;
}

.preview-rule-status--block {
  color: #e6a23c;
  background: #fdf6ec;
  border: 1px solid #faecd8;
}

.preview-rule-status--allow {
  color: #67c23a;
  background: #f0f9eb;
  border: 1px solid #e1f3d8;
}

.preview-rule-item__body {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 3px;
  padding: 2px 0 0 32px;
  box-sizing: border-box;
  width: 100%;
  align-items: center;
  align-content: flex-start;
  overflow-x: hidden;
}

.preview-default-chip {
  flex-shrink: 0;
}

.preview-rule-item__body--scroll {
  max-height: 88px;
  overflow-y: auto;
  overflow-x: hidden;
}

.preview-rule-item__body--week {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: 4px;
  max-height: none;
  overflow: hidden;
}

.preview-weekday-chip {
  flex: 1 1 0;
  min-width: 0;
  padding: 2px 0;
  font-size: 11px;
  text-align: center;
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview-rule-item__body.preview-time-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px;
  flex-wrap: unset;
  max-height: 140px;
  overflow-y: auto;
  overflow-x: hidden;
  align-items: stretch;
}

.preview-time-grid__item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  white-space: nowrap;
  font-size: 12px;
  padding: 2px 6px;
}

.preview-notices {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.preview-notices--ok {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  font-size: 12px;
  color: #67c23a;
  background: #f0f9eb;
  border: 1px solid #e1f3d8;
  border-radius: 6px;
}

.preview-notice {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.5;
  border-radius: 6px;
}

.preview-notice i {
  flex-shrink: 0;
  margin-top: 2px;
}

.preview-notice--error {
  color: #f56c6c;
  background: #fef0f0;
  border: 1px solid #fde2e2;
}

.preview-notice--warning {
  color: #e6a23c;
  background: #fdf6ec;
  border: 1px solid #faecd8;
}

.time-slot-card--date-range {
  width: 100%;
}

.date-range-picker {
  flex: 1;
  min-width: 0;
  max-width: calc(100% - 52px);
}

.date-range-picker.el-range-editor.el-input__inner {
  width: 100%;
}

.rule-setting-card__head .rule-priority {
  margin-right: 2px;
}

.rule-setting-card__head .field-optional {
  margin-left: auto;
}

.rule-setting-card__head .rule-setting-card__actions ~ .field-optional,
.rule-setting-card__head .field-optional:has(+ .rule-setting-card__actions) {
  margin-left: 0;
}

.rule-setting-card__head .el-button {
  margin-left: 4px;
}

.field-label {
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 500;
  color: #606266;
}

.field-label--required::after {
  content: '*';
  margin-left: 4px;
  color: #f56c6c;
  font-weight: 600;
}

.field-optional {
  margin-left: 6px;
  padding: 1px 6px;
  font-size: 11px;
  font-weight: normal;
  color: #909399;
  background: #f4f4f5;
  border-radius: 3px;
}

.rule-setting-card__label {
  font-size: 13px;
  font-weight: 600;
  color: #606266;
}

.rule-setting-card__label i {
  margin-right: 6px;
  color: #909399;
}

.rule-setting-card__body {
  padding: 10px 12px;
}

.rule-setting-card__body >>> .el-form-item__content {
  line-height: normal;
}

.rule-radio-btns {
  display: inline-flex;
}

.weekday-checkboxes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  width: 100%;
}

.weekday-checkboxes >>> .el-checkbox-button {
  width: 100%;
  margin-left: 0 !important;
}

.weekday-checkboxes >>> .el-checkbox-button + .el-checkbox-button {
  margin-left: 0 !important;
}

.weekday-checkboxes >>> .el-checkbox-button__inner {
  width: 100%;
  min-width: 0;
  padding: 8px 6px;
  font-size: 12px;
  border-radius: 6px !important;
  border: 2px solid #dcdfe6 !important;
  box-shadow: none !important;
  transition: all 0.2s ease;
}

.weekday-checkboxes >>> .el-checkbox-button.is-checked .el-checkbox-button__inner {
  background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
  border-color: #409eff !important;
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.4) !important;
  transform: translateY(-1px);
}

.weekday-checkboxes >>> .el-checkbox-button.is-checked .el-checkbox-button__inner:hover {
  color: #fff;
}

.time-slot-picker {
  width: 100px;
}

.time-slot-picker.el-date-editor.el-input {
  width: 100px;
}

.time-slot-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 4px;
}

.time-slot-card {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  background: #f5f7fa;
  border: 1px dashed #dcdfe6;
  border-radius: 6px;
}

.time-slot-card__index {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  line-height: 22px;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  color: #409eff;
  background: #ecf5ff;
  border-radius: 50%;
}

.dialog-form >>> .el-form-item__error {
  padding-top: 4px;
}

/* 任务名称选择 */
.task-name-item >>> .el-form-item__content {
  line-height: normal;
}

.batch-task-select >>> .el-input__inner {
  min-height: 36px;
  line-height: 36px;
}

.batch-task-select--empty >>> .el-input__inner {
  height: 36px !important;
}

.batch-task-select >>> .el-select__tags {
  max-height: 84px;
  overflow: hidden;
  flex-wrap: wrap;
}

.batch-task-select >>> .el-select__tags .el-tag {
  max-width: 140px;
}

.batch-task-select >>> .el-select__tags .el-tag:nth-child(n + 4) {
  display: none;
}

.batch-task-select >>> .el-select__input {
  margin-left: 6px;
}

.selected-task-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.selected-task-item {
  position: relative;
  max-width: 100%;
  padding: 6px 22px 6px 10px;
  background: #f5f9ff;
  border: 1px solid #d9ecff;
  border-radius: 4px;
  font-size: 13px;
  color: #409eff;
  line-height: 1.5;
  word-break: break-all;
}

.selected-task-item__text {
  display: inline-block;
}

.selected-task-item__close {
  position: absolute;
  top: 1px;
  right: 1px;
  padding: 2px;
  font-size: 12px;
  color: #a0cfff;
  cursor: pointer;
  line-height: 1;
}

.selected-task-item__close:hover {
  color: #f56c6c;
}

.time-slot-sep {
  flex-shrink: 0;
  font-size: 14px;
  font-weight: 600;
  color: #909399;
}

.dynamic-del {
  flex-shrink: 0;
  color: #f56c6c;
}
</style>

<style>
.rules-dialog .el-dialog__header {
  padding: 12px 20px 8px;
}

.rules-dialog .el-dialog__body {
  padding: 10px 20px 12px;
  overflow: visible;
}

.rules-dialog .el-dialog__footer {
  display: none;
}

.batch-task-select-dropdown.el-select-dropdown .el-select-dropdown__wrap {
  max-height: 280px;
}

.sync-report-loading-dialog .el-dialog__header {
  padding: 14px 20px 10px;
}

.sync-report-loading-dialog .el-dialog__body {
  padding: 0 20px 20px;
}

.sync-report-dialog .el-dialog__header {
  padding: 14px 20px 10px;
}

.sync-report-dialog .el-dialog__body {
  padding: 10px 20px;
}

.sync-report-dialog .el-dialog__footer {
  padding: 8px 20px 16px;
}
</style>
