<template>
  <div>
    <h3>预算管理</h3>
    <div v-if="noMap" style="padding:60px 0;text-align:center;color:#999;">
      <p style="font-size:16px;margin-bottom:12px;">暂无已发布的战略地图</p>
      <p style="font-size:13px;">预算编制需要基于已发布的战略地图，请先在【战略地图】中创建并发布</p>
      <el-button type="primary" style="margin-top:16px;" @click="$router.push('/maps')">去创建战略地图</el-button>
    </div>
    <div v-else>
    <el-tabs v-model="activeTab" style="margin-top:16px;">
      <el-tab-pane label="预算录入" name="input">
        <div style="margin-bottom:8px;font-size:12px;color:#909399;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
          选择战略地图
          <el-select v-model="selectedMapId" placeholder="选择已发布的战略地图" style="width:260px;" @change="onMapSelect">
            <el-option v-for="m in publishedMaps" :key="m.id" :label="m.title" :value="m.id" />
          </el-select>
          <el-tag v-if="selectedMapId" size="small" type="success">已选择</el-tag>
          <span style="color:#999;">共 {{ total }} 条KPI</span>
        </div>
        <div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;align-items:center;">
          <el-select v-model="filterYear" placeholder="年份" style="width:100px;" @change="loadBudget"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select>
          <el-select v-model="filterVersion" placeholder="版本" style="width:180px;" @change="loadBudget" clearable>
            <el-option v-for="v in versionsList" :key="v.version" :label="v.version + (v.status === 'active' ? ' (当前)' : '')" :value="v.version" />
          </el-select>
          <el-select v-model="filterMonth" placeholder="月份" style="width:90px;" @change="loadBudget"><el-option label="全年" :value="0" /><el-option v-for="m in 12" :key="m" :label="`${m}月`" :value="m" /></el-select>
          <el-input v-model="searchKpi" placeholder="搜索KPI名称" clearable style="width:200px;" @clear="loadBudget" @keyup.enter="loadBudget" />
          <el-button type="primary" @click="loadBudget">查询</el-button>
          <el-button type="success" @click="showAddBudget = true">+ 新增预算</el-button>
          <el-button @click="showDecompose = true" :disabled="!filterYear">年度分解</el-button>
          <el-divider direction="vertical" />
          <el-radio-group v-model="budgetViewMode" size="small" @change="onViewModeChange"><el-radio-button value="list">KPI列表</el-radio-button><el-radio-button value="summary">维度汇总</el-radio-button></el-radio-group>
          <el-switch v-model="batchEditMode" active-text="批量编辑" inactive-text="逐行编辑" @change="onBatchEditToggle" />
          <el-button v-if="batchEditMode" type="primary" @click="batchSaveAll" :loading="batchSavingAll" :disabled="changedRows.length === 0">批量保存 ({{ changedRows.length }})</el-button>
          <el-button v-if="batchEditMode" @click="batchCancelAll">取消</el-button>
          <el-divider direction="vertical" />
          <div style="display:flex;align-items:center;gap:6px;font-size:12px;">
            <span style="color:#909399;">预算模式：</span>
            <el-switch 
              v-model="budgetMode" 
              active-value="rolling" 
              inactive-value="fixed"
              active-text="滚动预算" 
              inactive-text="固定预算"
              @change="onBudgetModeChange"
            />
            <el-tag v-if="budgetMode === 'rolling'" size="small" type="warning" effect="plain">滚动12月</el-tag>
            <el-tag v-else size="small" type="info" effect="plain">固定年度</el-tag>
          </div>
          <el-button v-if="budgetMode === 'rolling'" size="small" type="warning" @click="doRollForward" :loading="rollingForward">延展</el-button>
        </div>
        <template v-if="budgetViewMode === 'list'">
        <el-table :data="budgetList" v-loading="loading" border stripe size="small" style="width:100%;" :row-class-name="rowClass" :empty-text="filterYear ? '暂无数据，在行内编辑填值后保存即可创建' : '请先选择年份'">
          <el-table-column type="index" label="#" width="40" />
          <el-table-column prop="kpi_code" label="KPI编码" width="120" />
          <el-table-column prop="kpi_name" label="KPI名称" min-width="160" />
          <el-table-column prop="dimension" label="维度" width="80"><template #default="{ row }"><el-tag size="small" :type="dimTag(row.dimension)">{{ dimLabel(row.dimension) }}</el-tag></template></el-table-column>
          <el-table-column prop="period" label="期间" width="90" />
          <el-table-column prop="budget_value" label="预算值" width="170">
            <template #default="{ row }"><el-input-number v-if="row._editing" v-model="row._editValue" :min="0" :precision="row.precision || 0" :step="row.step || 1" controls-position="right" style="width:155px;" @change="onEditChange(row)" /><span v-else>{{ formatNumber(row.budget_value, row.unit) }}</span></template>
          </el-table-column>
          <el-table-column prop="unit" label="单位" width="60" />
          <!-- 行动方案列（非财务维度显示） -->
          <el-table-column label="行动方案" width="120">
            <template #default="{ row }">
              <span v-if="row.dimension === 'finance'" style="color:#ccc;font-size:12px;">—</span>
              <el-button v-else size="small" link type="primary" @click="showActionPlans(row)">
                {{ row._actionCount != null ? `${row._actionCount}项` : '关联' }}
              </el-button>
            </template>
          </el-table-column>
            <el-table-column label="操作" width="160" fixed="right">
            <template #default="{ row }">
              <el-button v-if="!batchEditMode && !row._editing" size="small" @click="startEdit(row)">编辑</el-button>
              <template v-else-if="!batchEditMode"><el-button size="small" type="primary" @click="saveEdit(row)" :loading="row._saving">保存</el-button><el-button size="small" @click="cancelEdit(row)">取消</el-button></template>
              <el-button v-if="!batchEditMode && row.id" size="small" type="danger" plain @click="doDelete(row)">删除</el-button>
              <el-tag v-else-if="!batchEditMode" size="small" type="info" effect="plain" style="cursor:pointer" @click="startEdit(row)">待创建</el-tag>
            </template>
          </el-table-column>
        </el-table>
        </template>
        <template v-if="budgetViewMode === 'summary'">
          <el-row :gutter="16" style="margin-bottom:16px;">
            <el-col :span="6" v-for="card in summaryCards" :key="card.key">
              <el-card shadow="hover" :body-style="{ borderLeft: '4px solid ' + card.color }">
                <div style="text-align:center;"><div style="font-size:14px;margin-bottom:4px;">{{ card.icon }} {{ card.name }}</div><div style="font-size:24px;font-weight:600;color:#333;">{{ formatNumber(card.totalBudget) }}</div><div style="font-size:11px;color:#999;margin-top:4px;">{{ card.kpiCount }}个KPI · 占比{{ card.ratio }}%</div></div>
              </el-card>
            </el-col>
          </el-row>
          <el-table :data="dimSummaryRows" border stripe size="small" style="width:100%;">
            <el-table-column prop="dimension" label="维度" width="100"><template #default="{ row }"><el-tag size="small" :type="dimTag(row.dimension)">{{ dimLabel(row.dimension) }}</el-tag></template></el-table-column>
            <el-table-column prop="kpiCount" label="KPI数" width="80" />
            <el-table-column prop="totalBudget" label="预算总额" width="140"><template #default="{ row }">{{ formatNumber(row.totalBudget) }}</template></el-table-column>
            <el-table-column prop="ratio" label="占比" width="100"><template #default="{ row }"><el-progress :percentage="row.ratio" :stroke-width="12" text-inside /></template></el-table-column>
            <el-table-column prop="avgPerKpi" label="KPI均值" width="140"><template #default="{ row }">{{ formatNumber(row.avgPerKpi) }}</template></el-table-column>
          </el-table>
        </template>
      </el-tab-pane>

      <el-tab-pane label="年度分解" name="decompose">
        <el-card>
          <template #header><div style="display:flex;justify-content:space-between;align-items:center;"><span>年度预算 → 月度自动分解</span><el-button type="primary" @click="doDecompose" :loading="decomposing">执行分解</el-button></div></template>
          <el-form :model="decomposeForm" label-width="120px" style="max-width:500px;">
            <el-form-item label="年份"><el-select v-model="decomposeForm.year" style="width:150px;"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select></el-form-item>
            <el-form-item label="分解方式"><el-radio-group v-model="decomposeForm.method"><el-radio value="equal">均分（1/12）</el-radio><el-radio value="weighted">按历史权重</el-radio></el-radio-group></el-form-item>
          </el-form>
          <el-alert v-if="decomposeResult" :title="decomposeResult" type="success" show-icon :closable="false" style="margin-top:16px;" />
          <el-table v-if="decomposeDetails.length > 0" :data="decomposeDetails" border stripe size="small" style="width:100%;margin-top:12px;" max-height="300">
            <el-table-column prop="kpi_code" label="KPI编码" width="120" /><el-table-column prop="kpi_name" label="KPI名称" min-width="150" />
            <el-table-column prop="annual_budget" label="年度预算" width="120"><template #default="{ row }">{{ formatNumber(row.annual_budget) }}</template></el-table-column>
            <el-table-column prop="method" label="方式" width="80" /><el-table-column prop="monthly_count" label="月数" width="60" />
          </el-table>
        </el-card>
      </el-tab-pane>

      <el-tab-pane label="战略预算编制" name="strategy">
        <div v-if="!selectedMap" style="padding:40px 0;text-align:center;color:#999;"><p>请先在预算录入中选择战略地图</p></div>
        <template v-else>
          <div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;align-items:center;">
            <span style="font-weight:500;">{{ selectedMap.title }}</span><el-tag size="small" type="success">已发布</el-tag>
            <el-select v-model="strategyFilterYear" placeholder="年份" style="width:100px;" @change="loadStrategyBudget"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select>
            <el-select v-model="strategyFilterMonth" placeholder="月份" style="width:90px;" @change="loadStrategyBudget"><el-option label="全年" :value="0" /><el-option v-for="m in 12" :key="m" :label="`${m}月`" :value="m" /></el-select>
            <el-button type="primary" size="small" @click="loadStrategyBudget">刷新</el-button>
            <el-divider direction="vertical" /><span style="font-size:13px;color:#666;">已选 {{ strategySelectedKpis.length }} 个KPI</span>
            <el-button size="small" @click="strategyBatchSetSame">统一设值</el-button>
            <el-input-number v-model="strategyBatchValue" :min="0" controls-position="right" style="width:120px;" size="small" />
            <el-button v-if="strategySelectedKpis.length > 0" size="small" type="primary" :loading="strategyBatchSaving" @click="strategyBatchSave">批量保存</el-button>
          </div>
          <div v-for="dim in strategyTree" :key="dim.key" class="strategy-dim-block">
            <div class="strategy-dim-header" :style="{ borderLeftColor: dim.color }">
              <span class="strategy-dim-icon">{{ dim.icon }}</span><span class="strategy-dim-name">{{ dim.name }}</span>
              <span class="strategy-dim-summary">（{{ dim.kpiCount }}个KPI · 预算合计：{{ formatNumber(dim.totalBudget) }}）</span>
              <el-button size="small" link style="margin-left:auto;" @click="strategySelectDim(dim.key)">全选</el-button>
            </div>
            <div v-for="obj in dim.objectives" :key="obj.name" class="strategy-obj-block">
              <div class="strategy-obj-header"><span class="strategy-obj-name">{{ obj.name }}</span></div>
              <el-table :data="obj.kpis" border stripe size="small" style="width:100%;" :show-header="false" @selection-change="(sel: any[]) => onStrategyKpiSelect(sel, dim.key)">
                <el-table-column type="selection" width="36" />
                <el-table-column prop="kpi_code" label="编码" width="110" /><el-table-column prop="kpi_name" label="KPI名称" min-width="160" />
                <el-table-column label="战略目标" width="110"><template #default="{ row }"><span style="color:#e6a23c;font-size:12px;">{{ row.target != null ? formatNumber(row.target, row.unit) : '-' }}</span></template></el-table-column>
                <el-table-column label="预算值" width="190">
                  <template #default="{ row }">
                    <el-input-number v-model="row._editValue" :min="0" :precision="row.precision || 0" :step="row.step || 1" controls-position="right" style="width:150px;" @change="row._changed = (row._editValue !== (row.budget_value ?? 0))" />
                    <span style="font-size:12px;color:#999;margin-left:2px;">{{ row.unit }}</span>
                    <el-button v-if="row._changed" size="small" type="primary" link @click="saveStrategyKpi(row)" :loading="row._saving" style="margin-left:4px;">保存</el-button>
                  </template>
                </el-table-column>
                <el-table-column prop="period" label="期间" width="90" /><el-table-column prop="version" label="版本" width="60" />
              </el-table>
            </div>
          </div>
        </template>
      </el-tab-pane>

      <el-tab-pane label="版本管理" name="versions">
        <div style="display:flex;gap:12px;margin-bottom:16px;align-items:center;">
          <el-select v-model="versionYear" placeholder="年份" style="width:120px;"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select>
          <el-button type="primary" @click="loadVersions">刷新</el-button>
        </div>
        <el-table :data="versionsList" v-loading="versionsLoading" border stripe size="small" style="width:100%;">
          <el-table-column prop="version" label="版本" width="90"><template #default="{ row }"><el-tag :type="row.version === currentVersion ? 'success' : 'info'" size="small">{{ row.version }}</el-tag></template></el-table-column>
          <el-table-column prop="approval_status" label="状态" width="90">
            <template #default="{ row }"><el-tag v-if="row.approval_status === 'approved'" type="success" size="small">已批准</el-tag><el-tag v-else-if="row.approval_status === 'submitted'" type="warning" size="small">待审批</el-tag><el-tag v-else-if="row.approval_status === 'rejected'" type="danger" size="small">已驳回</el-tag><el-tag v-else type="info" size="small">草稿</el-tag></template>
          </el-table-column>
          <el-table-column prop="plan_count" label="预算条目数" width="100" />
          <el-table-column prop="total_budget" label="预算总额" width="130"><template #default="{ row }">{{ formatNumber(row.total_budget) }}</template></el-table-column>
          <el-table-column prop="last_updated" label="最后更新" min-width="150" />
          <el-table-column label="操作" width="200" fixed="right">
            <template #default="{ row }">
              <el-button v-if="row.approval_status === 'draft'" size="small" @click="submitVersion(row)">提交审批</el-button>
              <template v-if="row.approval_status === 'submitted'"><el-button size="small" type="success" @click="approveVersion(row, 'approved')">批准</el-button><el-button size="small" type="danger" @click="approveVersion(row, 'rejected')">驳回</el-button></template>
              <el-button v-if="row.approval_status === 'rejected'" size="small" @click="resubmitVersion(row)">重新提交</el-button>
              <el-checkbox v-model="row._selected" @change="onVersionSelect(row)" :disabled="selectedVersions.length >= 2 && !row._selected" style="margin-left:8px;" />
            </template>
          </el-table-column>
        </el-table>
        <div v-if="selectedVersions.length === 2" style="margin-top:16px;"><el-button type="primary" @click="loadVersionDiff" :loading="diffLoading">对比 {{ selectedVersions[0].version }} vs {{ selectedVersions[1].version }}</el-button></div>
      </el-tab-pane>

      <el-tab-pane label="预算执行" name="execution">
        <div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;align-items:center;">
          <el-select v-model="execFilterYear" placeholder="年份" style="width:100px;" @change="loadExecutionReport"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select>
          <el-select v-model="execFilterMonth" placeholder="截止月份" style="width:110px;" @change="loadExecutionReport"><el-option v-for="m in 12" :key="m" :label="`至${m}月`" :value="m" /></el-select>
          <el-select v-model="execFilterDim" placeholder="维度" clearable style="width:110px;" @change="loadExecutionReport"><el-option label="财务" value="finance" /><el-option label="客户" value="customer" /><el-option label="内部流程" value="process" /><el-option label="学习成长" value="learning" /></el-select>
          <el-button type="primary" @click="loadExecutionReport">刷新</el-button>
          <el-button type="warning" :loading="syncingCash" @click="syncCashPlans">⇄ 同步现金流计划</el-button>
        </div>
        <el-row :gutter="16" style="margin-bottom:16px;">
          <el-col :span="6" v-for="card in execSummaryCards" :key="card.label"><el-card shadow="hover"><div style="text-align:center;"><div style="font-size:12px;color:#999;">{{ card.label }}</div><div style="font-size:22px;font-weight:600;margin-top:4px;" :style="{color: card.color}">{{ card.value }}</div></div></el-card></el-col>
        </el-row>
        <el-table :data="execReport" v-loading="execLoading" border stripe size="small" style="width:100%;">
          <el-table-column prop="kpi_code" label="编码" width="110" /><el-table-column prop="kpi_name" label="KPI名称" min-width="140" />
          <el-table-column prop="dimension" label="维度" width="80"><template #default="{ row }"><el-tag size="small" :type="dimTag(row.dimension)">{{ dimLabel(row.dimension) }}</el-tag></template></el-table-column>
          <el-table-column prop="budget_value" label="预算值" width="130"><template #default="{ row }">{{ formatNumber(row.budget_value, row.unit) }}</template></el-table-column>
          <!-- 战略目标 vs 预算（2026-08-27: 允许不同, 差异可见可解释） -->
          <el-table-column prop="strategic_target" label="战略目标" width="120"><template #default="{ row }"><span :style="{color: row.target_gap_level === 'high' ? '#f56c6c' : row.target_gap_level === 'medium' ? '#e6a23c' : '#606266'}">{{ formatNumber(row.strategic_target, row.unit) }}</span></template></el-table-column>
          <el-table-column label="目标差异" width="110"><template #default="{ row }">
            <span v-if="row.target_gap_pct != null" :style="{color: row.target_gap_level === 'high' ? '#f56c6c' : row.target_gap_level === 'medium' ? '#e6a23c' : '#67c23a', fontWeight:600}">{{ row.target_gap_pct > 0 ? '+' : '' }}{{ row.target_gap_pct }}%</span>
            <el-tooltip v-if="row.target_gap_level === 'high'" content="预算偏离战略目标超20%，需说明原因" placement="top"><span style="margin-left:4px;cursor:help;">⚠️</span></el-tooltip>
            <span v-else style="color:#c0c4cc;">-</span>
          </template></el-table-column>
          <el-table-column prop="actual_value" label="实际值" width="130"><template #default="{ row }">{{ formatNumber(row.actual_value, row.unit) }}</template></el-table-column>
          <el-table-column prop="deviation_value" label="偏差" width="130"><template #default="{ row }"><span :style="{color: row.deviation_value > 0 ? '#f56c6c' : row.deviation_value < 0 ? '#67c23a' : '#999'}">{{ row.deviation_value > 0 ? '+' : '' }}{{ formatNumber(row.deviation_value, row.unit) }}</span></template></el-table-column>
          <el-table-column label="执行率" width="100"><template #default="{ row }"><el-progress :percentage="row.execution_rate || 0" :status="row.execution_rate > 100 ? 'exception' : row.execution_rate > 80 ? 'warning' : 'success'" :stroke-width="16" :text-inside="true" /></template></el-table-column>
          <el-table-column label="预警" width="80"><template #default="{ row }"><el-tag v-if="row.alert_level === 'red'" size="small" type="danger">严重</el-tag><el-tag v-else-if="row.alert_level === 'yellow'" size="small" type="warning">关注</el-tag><el-tag v-else size="small" type="success">正常</el-tag></template></el-table-column>
        </el-table>

        <!-- P2-⑥ 现金流分类规则 + 待分类队列 -->
        <el-row :gutter="16" style="margin-top:20px;">
          <el-col :span="12">
            <el-card shadow="never">
              <template #header>
                <div style="display:flex;justify-content:space-between;align-items:center;">
                  <span><strong>现金流分类规则</strong> <span style="font-size:12px;color:#999;">（KPI→receive/pay 可维护）</span></span>
                  <el-button type="primary" size="small" @click="showClassifyRuleDialog = true">+ 新建规则</el-button>
                </div>
              </template>
              <el-table :data="cashClassifyRules" border stripe size="small" style="width:100%;" max-height="240">
                <el-table-column prop="kpi_code" label="KPI编码" width="110"><template #default="{ row }">{{ row.kpi_code || row.kpi_code_pattern || '--' }}</template></el-table-column>
                <el-table-column prop="kpi_name" label="名称/关键词" min-width="110"><template #default="{ row }">{{ row.kpi_name || row.kpi_code_pattern || '--' }}</template></el-table-column>
                <el-table-column label="类型" width="80">
                  <template #default="{ row }"><el-tag size="small" :type="row.plan_type === 'receive' ? 'success' : 'danger'">{{ row.plan_type === 'receive' ? '收' : '付' }}</el-tag></template>
                </el-table-column>
                <el-table-column prop="priority" label="优先级" width="70" />
                <el-table-column label="操作" width="80" fixed="right">
                  <template #default="{ row }"><el-button size="small" link type="danger" @click="deleteClassifyRule(row)">删除</el-button></template>
                </el-table-column>
              </el-table>
            </el-card>
          </el-col>
          <el-col :span="12">
            <el-card shadow="never">
              <template #header>
                <div style="display:flex;justify-content:space-between;align-items:center;">
                  <span><strong>待分类队列</strong> <span style="font-size:12px;color:#999;">（无法判别的KPI不再静默跳过）</span></span>
                  <el-badge :value="unclassifiedCount" :hidden="unclassifiedCount === 0" type="danger"><el-button size="small" @click="loadCashClassify">刷新</el-button></el-badge>
                </div>
              </template>
              <el-table :data="unclassifiedRows" border stripe size="small" style="width:100%;" max-height="240">
                <el-table-column prop="kpi_name" label="KPI" min-width="120" />
                <el-table-column prop="period" label="期间" width="80" />
                <el-table-column prop="budget_value" label="预算值" width="90"><template #default="{ row }">{{ formatNumber(row.budget_value) }}</template></el-table-column>
                <el-table-column label="操作" width="140" fixed="right">
                  <template #default="{ row }">
                    <el-button size="small" link type="success" @click="classifyUnclassified(row, 'receive')">收</el-button>
                    <el-button size="small" link type="danger" @click="classifyUnclassified(row, 'pay')">付</el-button>
                    <el-button size="small" link @click="ignoreUnclassified(row)">忽略</el-button>
                  </template>
                </el-table-column>
              </el-table>
              <div v-if="unclassifiedRows.length === 0" style="padding:16px 0;text-align:center;color:#999;font-size:13px;">暂无待分类KPI，同步现金流计划后未命中规则的KPI会出现在这里</div>
            </el-card>
          </el-col>
        </el-row>
      </el-tab-pane>

      <el-tab-pane label="编制方法" name="method">
        <div>
          <p style="font-size:14px;color:#606266;margin-bottom:16px;">选择预算编制方法 — CMA P1 预算编制（增量预算 vs 零基预算 vs 弹性预算）</p>

          <!-- 方法选择卡片 -->
          <div style="display:flex;gap:16px;margin-bottom:20px;flex-wrap:wrap;">
            <div v-for="m in budgetMethods" :key="m.id"
              :class="['method-card', { selected: selectedMethod === m.id, recommended: m.is_recommended }]"
              @click="selectedMethod = m.id; loadMethodDetail(m.id)"
              style="flex:1;min-width:220px;border:2px solid #e8e8e8;border-radius:10px;padding:16px;cursor:pointer;transition:all .2s;position:relative;">
              <div v-if="m.is_recommended" style="position:absolute;top:-10px;right:12px;background:#e6a23c;color:#fff;font-size:11px;padding:2px 10px;border-radius:8px;">推荐</div>
              <div style="font-size:12px;color:#999;">{{ m.name_en }}</div>
              <div style="font-size:18px;font-weight:600;margin:6px 0;">{{ m.name }}</div>
              <div style="font-size:13px;color:#606266;margin-bottom:8px;">{{ m.pros }} · {{ m.cons }}</div>
              <div v-if="m.result_value != null" style="font-size:24px;font-weight:700;color:#409eff;">{{ formatNumber(m.result_value) }}<span style="font-size:14px;font-weight:400;color:#999;"> 万</span></div>
              <div v-if="m.savings" style="font-size:12px;color:#67c23a;">节省 {{ formatNumber(m.savings) }} 万</div>
              <div v-if="m.variance" style="font-size:12px;" :style="{ color: m.variance > 0 ? '#f56c6c' : '#67c23a' }">差异 {{ m.variance > 0 ? '+' : '' }}{{ formatNumber(m.variance) }} 万</div>
            </div>
          </div>

          <!-- 方法详情 -->
          <el-card v-if="selectedMethodDetail" shadow="never" style="margin-bottom:12px;">
            <template #header>
              <div style="display:flex;justify-content:space-between;align-items:center;">
                <span><strong>{{ selectedMethodDetail.name }}</strong> <span style="font-size:12px;color:#999;">{{ selectedMethodDetail.name_en }}</span></span>
                <el-tag v-if="selectedMethodDetail.is_recommended" type="warning" effect="dark">推荐</el-tag>
              </div>
            </template>
            <p style="margin:0 0 8px;color:#606266;">{{ selectedMethodDetail.detail }}</p>
            <div style="display:flex;gap:20px;flex-wrap:wrap;">
              <div><span style="color:#909399;">优点：</span>{{ selectedMethodDetail.pros }}</div>
              <div><span style="color:#909399;">缺点：</span>{{ selectedMethodDetail.cons }}</div>
            </div>
            <div v-if="selectedMethodDetail.result_value != null" style="margin-top:12px;padding:10px 14px;background:#f0f9ff;border-radius:6px;display:flex;align-items:center;gap:12px;">
              <span style="color:#909399;">预算结果：</span>
              <span style="font-size:22px;font-weight:700;color:#409eff;">{{ formatNumber(selectedMethodDetail.result_value) }} 万</span>
            </div>
          </el-card>

          <div style="display:flex;gap:8px;margin-top:16px;">
            <el-button v-if="selectedMethod" type="primary" @click="confirmMethod">确认选择（{{ selectedMethodName }}）</el-button>
            <el-button @click="refreshMethodComparison">刷新计算</el-button>
          </div>

          <!-- P2-① 零基逐项论证 -->
          <el-card shadow="never" style="margin-top:20px;">
            <template #header>
              <div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;">
                <span><strong>零基逐项论证</strong> <span style="font-size:12px;color:#999;">（真零基：逐项输入基准/论证/建议值 → 生成预算）</span></span>
                <div style="display:flex;gap:8px;align-items:center;">
                  <el-select v-model="zbbKpiId" placeholder="选择KPI" filterable style="width:200px;" size="small" @change="loadZeroBasedItems">
                    <el-option v-for="k in zbbKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
                  </el-select>
                  <el-select v-model="zbbPeriod" placeholder="期间" style="width:100px;" size="small" @change="loadZeroBasedItems">
                    <el-option v-for="p in zbbPeriodOptions" :key="p" :label="p" :value="p" />
                  </el-select>
                  <el-button type="primary" size="small" @click="addZeroBasedItem">+ 论证项</el-button>
                  <el-button type="warning" size="small" :loading="zbbGenerating" :disabled="!zbbKpiId || !zbbPeriod" @click="generateZeroBased">生成零基预算</el-button>
                </div>
              </div>
            </template>
            <el-alert v-if="zbbTotalProposed != null" :title="`论证项合计: ${formatNumber(zbbTotalProposed)} 万（Σ建议值）`" type="success" :closable="false" show-icon style="margin-bottom:10px;" />
            <el-table :data="zeroBasedItems" border stripe size="small" style="width:100%;">
              <el-table-column prop="item_name" label="费用科目" min-width="120" />
              <el-table-column prop="item_category" label="类别" width="100">
                <template #default="{ row }">
                  <el-tag size="small" :type="row.item_category === 'fixed' ? 'info' : row.item_category === 'variable' ? 'warning' : 'primary'">
                    {{ row.item_category === 'fixed' ? '固定' : row.item_category === 'variable' ? '变动' : '酌量' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="base_value" label="基准值" width="90"><template #default="{ row }">{{ formatNumber(row.base_value) }}</template></el-table-column>
              <el-table-column prop="justification" label="逐项论证理由" min-width="160" />
              <el-table-column prop="proposed_value" label="论证后金额" width="100"><template #default="{ row }"><b>{{ formatNumber(row.proposed_value) }}</b></template></el-table-column>
              <el-table-column prop="status" label="状态" width="80">
                <template #default="{ row }"><el-tag size="small" :type="row.status === 'approved' ? 'success' : 'info'">{{ row.status === 'approved' ? '已批准' : '草稿' }}</el-tag></template>
              </el-table-column>
              <el-table-column label="操作" width="100" fixed="right">
                <template #default="{ row }">
                  <el-button size="small" link type="primary" @click="editZeroBasedItem(row)">编辑</el-button>
                  <el-button size="small" link type="danger" @click="deleteZeroBasedItem(row)">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
            <div v-if="zeroBasedItems.length === 0" style="padding:20px 0;text-align:center;color:#999;font-size:13px;">请选择KPI和期间后录入逐项论证</div>
          </el-card>

          <!-- P2-② 派生规则配置 -->
          <el-card shadow="never" style="margin-top:16px;">
            <template #header>
              <div style="display:flex;justify-content:space-between;align-items:center;">
                <span><strong>派生规则配置</strong> <span style="font-size:12px;color:#999;">（apply-method 派生KPI时优先读规则，替代固定比例）</span></span>
                <el-button type="primary" size="small" @click="showDerivationRuleDialog = true">+ 新建规则</el-button>
              </div>
            </template>
            <el-table :data="derivationRules" border stripe size="small" style="width:100%;">
              <el-table-column prop="kpi_code" label="目标KPI编码" width="110" />
              <el-table-column prop="kpi_name" label="目标KPI" min-width="110" />
              <el-table-column prop="rule_type" label="规则类型" width="130">
                <template #default="{ row }">
                  <el-tag size="small" :type="row.rule_type === 'percentage_of' ? 'primary' : row.rule_type === 'incremental' ? 'warning' : 'info'">
                    {{ row.rule_type === 'percentage_of' ? '按来源比例' : row.rule_type === 'incremental' ? '增量' : '公式' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="参数" width="120">
                <template #default="{ row }"><span v-if="row.params">{{ (row.params.rate != null ? `比例 ${(row.params.rate * 100).toFixed(0)}%` : '') }}</span></template>
              </el-table-column>
              <el-table-column label="来源KPI" width="130">
                <template #default="{ row }"><span v-if="row.base_kpi_code">{{ row.base_kpi_code }}</span><span v-else style="color:#ccc;">--</span></template>
              </el-table-column>
              <el-table-column prop="formula_text" label="公式说明" min-width="160" />
              <el-table-column label="操作" width="80" fixed="right">
                <template #default="{ row }">
                  <el-button size="small" link type="danger" @click="deleteDerivationRule(row)">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-card>
        </div>
      </el-tab-pane>

      <el-tab-pane label="驱动因子预算" name="driver">
        <DriverFactorBudget />
      </el-tab-pane>

      <el-tab-pane label="实际值归集" name="collect">
        <el-row :gutter="16" style="margin-bottom:16px;">
          <el-col :span="6" v-for="c in collectCoverageCards" :key="c.label">
            <el-card shadow="hover"><div style="text-align:center;"><div style="font-size:12px;color:#999;">{{ c.label }}</div><div style="font-size:22px;font-weight:600;margin-top:4px;" :style="{color: c.color}">{{ c.value }}</div></div></el-card>
          </el-col>
        </el-row>
        <el-tabs v-model="collectTab" type="border-card" style="margin-top:4px;">
          <el-tab-pane label="取数映射" name="mappings">
            <div style="display:flex;gap:8px;margin-bottom:12px;align-items:center;flex-wrap:wrap;">
              <el-select v-model="collectKpiId" placeholder="选择KPI(可选)" filterable clearable style="width:220px;" @change="loadValueSources">
                <el-option v-for="k in zbbKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
              </el-select>
              <el-button type="primary" @click="showValueSourceDialog = true">+ 新建映射</el-button>
              <el-button type="warning" :loading="collectRunning" @click="runValueCollect">▶ 立即采集</el-button>
              <el-button @click="loadCollectData">刷新</el-button>
            </div>
            <el-table :data="valueSources" border stripe size="small" style="width:100%;">
              <el-table-column prop="kpi_code" label="KPI编码" width="100" />
              <el-table-column prop="kpi_name" label="KPI名称" min-width="120" />
              <el-table-column prop="source_table" label="源头表" width="160">
                <template #default="{ row }"><el-tag size="small">{{ row.source_table }}</el-tag></template>
              </el-table-column>
              <el-table-column prop="source_field" label="金额字段" width="110" />
              <el-table-column prop="aggregate" label="聚合" width="70" />
              <el-table-column label="过滤规则" width="140">
                <template #default="{ row }"><span style="font-size:12px;">{{ JSON.stringify(row.filter_rule || {}) }}</span></template>
              </el-table-column>
              <el-table-column prop="unit_conversion" label="倍率" width="60" />
              <el-table-column label="状态" width="80">
                <template #default="{ row }"><el-tag size="small" :type="row.status === 'active' ? 'success' : 'info'">{{ row.status === 'active' ? '启用' : '停用' }}</el-tag></template>
              </el-table-column>
              <el-table-column label="操作" width="160" fixed="right">
                <template #default="{ row }">
                  <el-button size="small" link type="primary" @click="testValueSource(row)">试跑</el-button>
                  <el-button size="small" link type="warning" @click="toggleValueSource(row)">{{ row.status === 'active' ? '停用' : '启用' }}</el-button>
                  <el-button size="small" link type="danger" @click="deleteValueSource(row)">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-tab-pane>

          <el-tab-pane label="实际值标签" name="values">
            <div style="display:flex;gap:8px;margin-bottom:12px;align-items:center;">
              <el-radio-group v-model="valueTagFilter" size="small">
                <el-radio-button value="auto_collect">已自动归集</el-radio-button>
                <el-radio-button value="manual">需人工确认</el-radio-button>
                <el-radio-button value="all">全部</el-radio-button>
              </el-radio-group>
              <span style="font-size:12px;color:#999;">对账页将按数据来源分类显示，自动归集数据可追溯采集批次</span>
            </div>
            <el-table :data="valueTagRows" border stripe size="small" style="width:100%;">
              <el-table-column prop="kpi_code" label="KPI编码" width="100" />
              <el-table-column prop="kpi_name" label="KPI名称" min-width="120" />
              <el-table-column prop="period" label="期间" width="90" />
              <el-table-column prop="actual_value" label="实际值" width="110"><template #default="{ row }">{{ formatNumber(row.actual_value) }}</template></el-table-column>
              <el-table-column label="来源" width="130">
                <template #default="{ row }">
                  <el-tag v-if="row.source_type === 'auto_collect'" size="small" type="success">已自动归集</el-tag>
                  <el-tag v-else-if="row.source_type === 'manual' || row.source_type === 'excel'" size="small" type="warning">需人工确认</el-tag>
                  <el-tag v-else size="small" type="info">{{ row.source_type || 'manual' }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="source_batch" label="采集批次" width="170"><template #default="{ row }"><span style="font-size:12px;">{{ row.source_batch || '--' }}</span></template></el-table-column>
              <el-table-column prop="remark" label="备注" min-width="150" />
            </el-table>
          </el-tab-pane>

          <el-tab-pane label="采集日志" name="logs">
            <div style="display:flex;gap:8px;margin-bottom:12px;align-items:center;">
              <el-select v-model="collectLogStatus" placeholder="状态" clearable style="width:110px;" size="small">
                <el-option label="成功" value="success" /><el-option label="失败" value="failed" />
              </el-select>
              <el-button size="small" type="primary" @click="loadCollectLogs">查询</el-button>
            </div>
            <el-table :data="collectLogs" border stripe size="small" style="width:100%;">
              <el-table-column prop="kpi_code" label="KPI编码" width="100" />
              <el-table-column prop="kpi_name" label="KPI名称" min-width="110" />
              <el-table-column prop="period" label="期间" width="90" />
              <el-table-column prop="source_table" label="源头表" width="140" />
              <el-table-column prop="collected_value" label="采集值" width="100"><template #default="{ row }">{{ formatNumber(row.collected_value) }}</template></el-table-column>
              <el-table-column label="状态" width="80">
                <template #default="{ row }"><el-tag size="small" :type="row.status === 'success' ? 'success' : 'danger'">{{ row.status === 'success' ? '成功' : '失败' }}</el-tag></template>
              </el-table-column>
              <el-table-column prop="message" label="说明" min-width="180" />
              <el-table-column prop="collected_at" label="采集时间" width="160" />
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </el-tab-pane>

      <el-tab-pane label="持续规划" name="rolling">
        <el-tabs v-model="rollingTab" type="border-card" style="margin-top:4px;">
          <el-tab-pane label="实际vs预测对比" name="comparison">
            <div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;align-items:center;">
              <el-select v-model="comparisonYear" placeholder="年份" style="width:100px;"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select>
              <el-select v-model="comparisonKpiId" placeholder="选择KPI(可选)" filterable clearable style="width:220px;" @change="loadComparison">
                <el-option v-for="k in comparisonKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
              </el-select>
              <el-button type="primary" @click="loadComparison">刷新</el-button>
              <el-tag v-if="budgetMode === 'rolling'" size="small" type="warning">滚动预算 - 当前月之后为预测值</el-tag>
              <el-tag v-else size="small" type="info">固定预算</el-tag>
            </div>

            <!-- 对比图表 -->
            <el-card v-if="comparisonData.length > 0" shadow="hover" style="margin-bottom:16px;">
              <div ref="comparisonChartRef" style="width:100%;height:360px;"></div>
            </el-card>

            <!-- 分界点指示 & 摘要 -->
            <el-row :gutter="16" style="margin-bottom:16px;" v-if="comparisonSummary">
              <el-col :span="6" v-for="s in comparisonSummary" :key="s.label">
                <el-card shadow="hover">
                  <div style="text-align:center;">
                    <div style="font-size:12px;color:#999;">{{ s.label }}</div>
                    <div style="font-size:20px;font-weight:600;margin-top:4px;" :style="{color: s.color}">{{ s.value }}</div>
                  </div>
                </el-card>
              </el-col>
            </el-row>

            <!-- 对比详情表格 -->
            <el-table :data="comparisonData" v-loading="comparisonLoading" border stripe size="small" style="width:100%;">
              <el-table-column prop="period" label="期间" width="100" />
              <el-table-column prop="budget_total" label="预算值" width="150"><template #default="{ row }">{{ formatNumber(row.budget_total) }}</template></el-table-column>
              <el-table-column prop="actual_total" label="实际值" width="150"><template #default="{ row }">{{ row.actual_total != null ? formatNumber(row.actual_total) : '--' }}</template></el-table-column>
              <el-table-column label="偏差率" width="130">
                <template #default="{ row }">
                  <el-tag v-if="row.deviation_rate != null" :type="Math.abs(row.deviation_rate) > 20 ? 'danger' : Math.abs(row.deviation_rate) > 10 ? 'warning' : 'success'" size="small">
                    {{ row.deviation_rate > 0 ? '+' : '' }}{{ row.deviation_rate }}%
                  </el-tag>
                  <span v-else style="color:#ccc;">--</span>
                </template>
              </el-table-column>
              <el-table-column label="类型标记" width="120">
                <template #default="{ row }">
                  <el-tag v-if="row.is_prediction" size="small" type="warning">预测值</el-tag>
                  <el-tag v-else-if="row.is_current_period" size="small" type="danger">当前期</el-tag>
                  <el-tag v-else size="small" type="success">已发生</el-tag>
                </template>
              </el-table-column>
            </el-table>
            <div v-if="comparisonData.length === 0 && !comparisonLoading" style="padding:40px 0;text-align:center;color:#999;">
              <p>暂无对比数据，请先录入预算和实际值</p>
            </div>
          </el-tab-pane>

          <el-tab-pane label="偏差告警" name="alerts">
            <div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;align-items:center;">
              <el-select v-model="alertFilterPeriod" placeholder="期间" style="width:120px;">
                <el-option v-for="p in alertPeriodOptions" :key="p" :label="p" :value="p" />
              </el-select>
              <el-select v-model="alertFilterLevel" placeholder="级别" clearable style="width:100px;">
                <el-option label="严重" value="critical" />
                <el-option label="警告" value="warning" />
              </el-select>
              <el-select v-model="alertFilterStatus" placeholder="状态" clearable style="width:100px;">
                <el-option label="未处理" value="open" />
                <el-option label="已解决" value="resolved" />
                <el-option label="已忽略" value="ignored" />
              </el-select>
              <el-button type="primary" @click="loadDeviationAlerts">查询</el-button>
              <el-button type="danger" @click="doDeviationCheck" :loading="deviationChecking">执行偏差检查 (超20%告警)</el-button>
            </div>

            <el-alert
              v-if="deviationAlertMessage"
              :title="deviationAlertMessage"
              type="info"
              show-icon
              :closable="true"
              style="margin-bottom:12px;"
              @close="deviationAlertMessage = ''"
            />

            <el-table :data="deviationAlerts" v-loading="alertLoading" border stripe size="small" style="width:100%;">
              <el-table-column prop="period" label="期间" width="90" />
              <el-table-column prop="kpi_code" label="KPI编码" width="100" />
              <el-table-column prop="kpi_name" label="KPI名称" min-width="140" />
              <el-table-column prop="budget_value" label="预算值" width="120"><template #default="{ row }">{{ formatNumber(row.budget_value) }}</template></el-table-column>
              <el-table-column prop="actual_value" label="实际值" width="120"><template #default="{ row }">{{ formatNumber(row.actual_value) }}</template></el-table-column>
              <el-table-column prop="deviation_rate" label="偏差率" width="110">
                <template #default="{ row }">
                  <el-tag :type="Math.abs(row.deviation_rate) > 50 ? 'danger' : 'warning'" size="small">
                    {{ row.deviation_rate > 0 ? '+' : '' }}{{ row.deviation_rate }}%
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="级别" width="80">
                <template #default="{ row }">
                  <el-tag :type="row.alert_level === 'critical' ? 'danger' : 'warning'" size="small">
                    {{ row.alert_level === 'critical' ? '严重' : '警告' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="suggestion" label="建议" min-width="180" />
              <el-table-column label="状态" width="90">
                <template #default="{ row }">
                  <el-tag v-if="row.status === 'open'" type="danger" size="small">未处理</el-tag>
                  <el-tag v-else-if="row.status === 'resolved'" type="success" size="small">已解决</el-tag>
                  <el-tag v-else type="info" size="small">已忽略</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="180" fixed="right">
                <template #default="{ row }">
                  <el-button size="small" type="primary" plain @click="showAlertAttribution(row)">归因</el-button>
                  <el-button v-if="row.status === 'open'" size="small" type="success" @click="resolveAlert(row)">解决</el-button>
                  <el-button v-else-if="row.status === 'resolved'" size="small" @click="reopenAlert(row)">重开</el-button>
                </template>
              </el-table-column>
            </el-table>
            <div v-if="deviationAlerts.length === 0 && !alertLoading" style="padding:40px 0;text-align:center;color:#999;">
              <p>暂无偏差预警，点击「执行偏差检查」扫描当前期间</p>
            </div>
          </el-tab-pane>

          <!-- 告警归因详情弹窗 (P1-③) -->
          <el-dialog v-model="showAttributionDialog" title="告警归因分析" width="720" append-to-body>
            <template v-if="attributionDetail">
              <el-descriptions :column="2" border size="small" style="margin-bottom:12px;">
                <el-descriptions-item label="KPI">{{ attributionDetail.kpi_name }}</el-descriptions-item>
                <el-descriptions-item label="期间">{{ attributionDetail.period }}</el-descriptions-item>
                <el-descriptions-item label="预算">{{ formatNumber(attributionDetail.budget_value) }}</el-descriptions-item>
                <el-descriptions-item label="实际">{{ formatNumber(attributionDetail.actual_value) }}</el-descriptions-item>
                <el-descriptions-item label="偏差率">
                  <el-tag :type="Math.abs(attributionDetail.deviation_rate) > 50 ? 'danger' : 'warning'" size="small">
                    {{ attributionDetail.deviation_rate > 0 ? '+' : '' }}{{ attributionDetail.deviation_rate }}%
                  </el-tag>
                </el-descriptions-item>
                <el-descriptions-item label="量价差">
                  <el-tag size="small" :type="attributionDetail.attribution?.variance_type === 'quantity_diff' ? 'warning' : attributionDetail.attribution?.variance_type === 'price_diff' ? 'danger' : 'info'">
                    {{ attributionDetail.attribution?.variance_type === 'quantity_diff' ? '量差' : attributionDetail.attribution?.variance_type === 'price_diff' ? '价差' : attributionDetail.attribution?.variance_type === 'mixed' ? '量价混合' : '--' }}
                  </el-tag>
                </el-descriptions-item>
              </el-descriptions>

              <!-- 趋势标识 -->
              <div v-if="attributionDetail.attribution?.trend?.anomaly" style="margin-bottom:12px;">
                <el-alert :title="attributionDetail.attribution.trend.message" type="warning" show-icon :closable="false">
                  <template #default>
                    <span style="font-size:12px;">{{ (attributionDetail.attribution.trend.periods || []).join(' → ') }}</span>
                  </template>
                </el-alert>
              </div>

              <!-- 子KPI维度拆解 -->
              <div v-if="attributionDetail.attribution?.dimensions?.length" style="margin-bottom:12px;">
                <div style="font-weight:600;font-size:13px;margin-bottom:6px;">📊 子KPI维度拆解（量差方向）</div>
                <el-table :data="attributionDetail.attribution.dimensions" border stripe size="small">
                  <el-table-column prop="kpi_name" label="子KPI" min-width="120" />
                  <el-table-column prop="weight" label="权重" width="70"><template #default="{ row }">{{ row.weight }}%</template></el-table-column>
                  <el-table-column prop="budget_value" label="预算" width="90"><template #default="{ row }">{{ formatNumber(row.budget_value) }}</template></el-table-column>
                  <el-table-column prop="actual_value" label="实际" width="90"><template #default="{ row }">{{ formatNumber(row.actual_value) }}</template></el-table-column>
                  <el-table-column label="差异率" width="100">
                    <template #default="{ row }">
                      <span v-if="row.deviation_rate != null" :style="{ color: row.deviation_rate > 0 ? '#f56c6c' : row.deviation_rate < 0 ? '#67c23a' : '#999' }">
                        {{ row.deviation_rate > 0 ? '+' : '' }}{{ row.deviation_rate }}%
                      </span>
                      <span v-else style="color:#ccc;">--</span>
                    </template>
                  </el-table-column>
                </el-table>
              </div>

              <!-- 科目明细拆解 -->
              <div v-if="attributionDetail.attribution?.subjects?.length" style="margin-bottom:12px;">
                <div style="font-weight:600;font-size:13px;margin-bottom:6px;">💡 科目明细拆解（价差方向）</div>
                <el-table :data="attributionDetail.attribution.subjects" border stripe size="small">
                  <el-table-column prop="subject_code" label="科目编码" width="90" />
                  <el-table-column prop="subject_name" label="科目" min-width="110" />
                  <el-table-column prop="amount_diff" label="发生额差" width="100"><template #default="{ row }">{{ formatNumber(row.amount_diff) }}</template></el-table-column>
                  <el-table-column prop="share_pct" label="占比" width="80"><template #default="{ row }">{{ row.share_pct }}%</template></el-table-column>
                </el-table>
              </div>

              <!-- 场景建议 -->
              <div v-if="attributionDetail.scenario" style="border:1px solid #e6a23c;border-radius:8px;padding:12px;background:#fdf6ec;">
                <div style="font-weight:600;font-size:13px;color:#e6a23c;margin-bottom:6px;">🎯 场景建议：{{ attributionDetail.scenario.title }}</div>
                <div style="font-size:13px;color:#606266;white-space:pre-line;">{{ attributionDetail.scenario.description }}</div>
                <div v-if="attributionDetail.scenario.action_template" style="margin-top:8px;font-size:12px;color:#606266;background:#fff;border-radius:6px;padding:8px 10px;white-space:pre-line;">
                  <strong>行动模板：</strong>{{ attributionDetail.scenario.action_template }}
                </div>
              </div>
              <div v-else style="border:1px solid #ebeef5;border-radius:8px;padding:12px;color:#999;font-size:13px;">
                暂无匹配的场景建议模板
              </div>
            </template>
            <template #footer><el-button @click="showAttributionDialog = false">关闭</el-button></template>
          </el-dialog>
        </el-tabs>
      </el-tab-pane>
    </el-tabs>

    <MyDialog v-model="showAddBudget" title="新增预算" :width="500">
      <el-form :model="addForm" label-width="80px">
        <el-form-item label="KPI"><el-select v-model="addForm.kpi_id" placeholder="搜索并选择KPI" filterable remote :remote-method="searchKpiForAdd" :loading="addKpiLoading" style="width:100%;" :teleported="false"><el-option v-for="k in addKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" /></el-select></el-form-item>
        <el-form-item label="年份"><el-select v-model="addForm.year" style="width:120px;"><el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" /></el-select></el-form-item>
        <el-form-item label="月份"><el-select v-model="addForm.month" style="width:120px;"><el-option v-for="m in 12" :key="m" :label="`${m}月`" :value="m" /></el-select></el-form-item>
        <el-form-item label="预算值"><el-input-number v-model="addForm.value" :min="0" :precision="0" style="width:200px;" /></el-form-item>
      </el-form>
      <template #footer><el-button @click="showAddBudget = false">取消</el-button><el-button type="primary" @click="doAddBudget" :loading="addSaving">创建</el-button></template>
    </MyDialog>

    <MyDialog v-model="showVersionDiff" title="版本差异对比" :width="900">
      <template v-if="diffData">
        <el-alert show-icon :closable="false" style="margin-bottom:12px;"><template #title><span>对比 {{ diffData.summary.version_a }} → {{ diffData.summary.version_b }}</span></template></el-alert>
        <el-table :data="diffData.diffs" border stripe size="small" max-height="500" style="width:100%;">
          <el-table-column prop="kpi_code" label="编码" width="110" /><el-table-column prop="kpi_name" label="名称" min-width="140" /><el-table-column prop="period" label="期间" width="90" />
          <el-table-column prop="old_value" label="旧值" width="110"><template #default="{ row }">{{ formatNumber(row.old_value) }}</template></el-table-column>
          <el-table-column prop="new_value" label="新值" width="110"><template #default="{ row }">{{ formatNumber(row.new_value) }}</template></el-table-column>
          <el-table-column prop="diff_value" label="差值" width="110"><template #default="{ row }"><span :style="{color: row.diff_value > 0 ? '#f56c6c' : row.diff_value < 0 ? '#67c23a' : '#999'}">{{ row.diff_value > 0 ? '+' : '' }}{{ formatNumber(row.diff_value) }}</span></template></el-table-column>
          <el-table-column prop="diff_rate" label="变动率" width="90"><template #default="{ row }"><el-tag v-if="row.diff_rate === 0" size="small" type="info">持平</el-tag><el-tag v-else :type="row.diff_rate > 0 ? 'danger' : 'success'" size="small">{{ row.diff_rate > 0 ? '+' : '' }}{{ row.diff_rate }}%</el-tag></template></el-table-column>
        </el-table>
      </template>
      <template #footer><el-button @click="showVersionDiff = false">关闭</el-button></template>
    </MyDialog>

    <!-- 行动方案关联弹窗 -->
    <MyDialog v-model="showActionDialog" :title="actionDialogTitle" :width="600">
      <template v-if="actionPlansForKpi.length > 0">
        <div style="margin-bottom:12px;font-size:13px;color:#666;">已关联 {{ actionPlansForKpi.length }} 项行动方案</div>
        <div v-for="plan in actionPlansForKpi" :key="plan.id" style="border:1px solid #e8e8e8;border-radius:8px;padding:12px;margin-bottom:8px;">
          <div style="display:flex;justify-content:space-between;align-items:flex-start;">
            <div>
              <div style="font-weight:500;">{{ plan.title }}</div>
              <div v-if="plan.description" style="font-size:12px;color:#888;margin-top:4px;">{{ plan.description }}</div>
            </div>
            <el-tag v-if="plan.status === 'completed'" type="success" size="small">已完成</el-tag>
            <el-tag v-else-if="plan.status === 'in_progress'" type="warning" size="small">进行中</el-tag>
            <el-tag v-else type="info" size="small">{{ plan.status }}</el-tag>
          </div>
          <div style="display:flex;gap:16px;margin-top:8px;font-size:12px;color:#999;">
            <span>负责人: {{ plan.assignee || '未指定' }}</span>
            <span v-if="plan.due_date">截止: {{ plan.due_date?.slice(0,10) }}</span>
            <span>优先级: {{ plan.priority }}</span>
          </div>
        </div>
      </template>
      <div v-else style="padding:40px 0;text-align:center;color:#999;">
        <p>暂无关联行动方案</p>
        <p style="font-size:12px;margin-top:4px;">非财务维度KPI需要配套行动方案才能将战略意图转化为实际行动</p>
      </div>
      <div style="margin-top:16px;display:flex;gap:8px;">
        <el-button type="primary" @click="goCreateActionPlan">+ 新建行动方案</el-button>
        <el-button @click="goActionPlansPage">去行动方案库查看</el-button>
      </div>
      <template #footer><el-button @click="showActionDialog = false">关闭</el-button></template>
    </MyDialog>

    <!-- P2-① 零基论证项编辑弹窗 -->
    <MyDialog v-model="showZbbItemDialog" :title="zbbItemForm.id ? '编辑论证项' : '新增论证项'" :width="520">
      <el-form :model="zbbItemForm" label-width="100px">
        <el-form-item label="费用科目"><el-input v-model="zbbItemForm.item_name" placeholder="如: 招待费" /></el-form-item>
        <el-form-item label="类别">
          <el-select v-model="zbbItemForm.item_category" style="width:100%;">
            <el-option label="固定" value="fixed" /><el-option label="变动" value="variable" /><el-option label="酌量" value="discretionary" />
          </el-select>
        </el-form-item>
        <el-form-item label="基准值"><el-input-number v-model="zbbItemForm.base_value" :min="0" :precision="2" style="width:200px;" /></el-form-item>
        <el-form-item label="论证理由"><el-input v-model="zbbItemForm.justification" type="textarea" :rows="2" placeholder="为何保留/削减/取消" /></el-form-item>
        <el-form-item label="论证后金额"><el-input-number v-model="zbbItemForm.proposed_value" :min="0" :precision="2" style="width:200px;" /></el-form-item>
        <el-form-item label="状态">
          <el-radio-group v-model="zbbItemForm.status"><el-radio value="draft">草稿</el-radio><el-radio value="approved">已批准</el-radio></el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer><el-button @click="showZbbItemDialog = false">取消</el-button><el-button type="primary" @click="saveZeroBasedItem">保存</el-button></template>
    </MyDialog>

    <!-- P2-② 派生规则新建弹窗 -->
    <MyDialog v-model="showDerivationRuleDialog" title="新建派生规则" :width="520">
      <el-form :model="derivationRuleForm" label-width="100px">
        <el-form-item label="目标KPI">
          <el-select v-model="derivationRuleForm.kpi_id" filterable style="width:100%;">
            <el-option v-for="k in zbbKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="规则类型">
          <el-select v-model="derivationRuleForm.rule_type" style="width:100%;">
            <el-option label="按来源KPI比例 (percentage_of)" value="percentage_of" />
            <el-option label="增量 (incremental)" value="incremental" />
            <el-option label="公式 (formula)" value="formula" />
          </el-select>
        </el-form-item>
        <el-form-item v-if="derivationRuleForm.rule_type === 'percentage_of'" label="来源KPI">
          <el-select v-model="derivationRuleForm.base_kpi_id" filterable style="width:100%;">
            <el-option v-for="k in zbbKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="比例(%)"><el-input-number v-model="derivationRuleForm.rate_pct" :min="0" :max="100" :precision="2" style="width:200px;" /></el-form-item>
        <el-form-item label="公式说明"><el-input v-model="derivationRuleForm.formula_text" placeholder="可读公式说明" /></el-form-item>
      </el-form>
      <template #footer><el-button @click="showDerivationRuleDialog = false">取消</el-button><el-button type="primary" @click="saveDerivationRule">保存</el-button></template>
    </MyDialog>

    <!-- P1-④ 取数映射新建弹窗 -->
    <MyDialog v-model="showValueSourceDialog" title="新建取数映射" :width="560">
      <el-form :model="valueSourceForm" label-width="100px">
        <el-form-item label="目标KPI">
          <el-select v-model="valueSourceForm.kpi_id" filterable style="width:100%;">
            <el-option v-for="k in zbbKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="源头表">
          <el-select v-model="valueSourceForm.source_table" style="width:100%;">
            <el-option label="网银凭证明细 voucher_details" value="voucher_details" />
            <el-option label="库存汇总 product_inventory" value="product_inventory" />
            <el-option label="库存明细 product_inventory_detail" value="product_inventory_detail" />
            <el-option label="收付款计划 cash_plans" value="cash_plans" />
          </el-select>
        </el-form-item>
        <el-form-item label="金额字段">
          <el-select v-model="valueSourceForm.source_field" style="width:100%;">
            <el-option v-for="f in ['credit_amount','debit_amount','amount','qty','out_amount','in_amount','end_amount']" :key="f" :label="f" :value="f" />
          </el-select>
        </el-form-item>
        <el-form-item label="聚合方式">
          <el-select v-model="valueSourceForm.aggregate" style="width:120px;"><el-option v-for="a in ['sum','avg','count','max','min']" :key="a" :label="a" :value="a" /></el-select>
        </el-form-item>
        <el-form-item label="方向过滤">
          <el-select v-model="valueSourceForm.direction" clearable placeholder="不限定" style="width:150px;">
            <el-option label="贷方(收入)" value="credit" /><el-option label="借方(支出)" value="debit" />
          </el-select>
        </el-form-item>
        <el-form-item label="科目过滤"><el-input v-model="valueSourceForm.subject_code" placeholder="如: 6601 销售费用" /></el-form-item>
        <el-form-item label="期间字段">
          <el-select v-model="valueSourceForm.period_field" style="width:150px;"><el-option label="period" value="period" /><el-option label="voucher_date" value="voucher_date" /></el-select>
        </el-form-item>
        <el-form-item label="单位倍率"><el-input-number v-model="valueSourceForm.unit_conversion" :min="0.0001" :precision="4" style="width:150px;" /><span style="font-size:12px;color:#999;margin-left:6px;">元→万元填 0.0001</span></el-form-item>
      </el-form>
      <template #footer><el-button @click="showValueSourceDialog = false">取消</el-button><el-button type="primary" @click="saveValueSource">保存</el-button></template>
    </MyDialog>

    <!-- P2-⑥ 分类规则新建弹窗 -->
    <MyDialog v-model="showClassifyRuleDialog" title="新建现金流分类规则" :width="500">
      <el-form :model="classifyRuleForm" label-width="110px">
        <el-form-item label="KPI(精确)">
          <el-select v-model="classifyRuleForm.kpi_id" filterable clearable placeholder="或使用关键词" style="width:100%;">
            <el-option v-for="k in zbbKpiOptions" :key="k.id" :label="`${k.kpi_code} - ${k.kpi_name}`" :value="k.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="关键词(兜底)"><el-input v-model="classifyRuleForm.kpi_code_pattern" placeholder="如: 保证金" /></el-form-item>
        <el-form-item label="收付类型">
          <el-radio-group v-model="classifyRuleForm.plan_type"><el-radio value="receive">收</el-radio><el-radio value="pay">付</el-radio></el-radio-group>
        </el-form-item>
        <el-form-item label="优先级"><el-input-number v-model="classifyRuleForm.priority" :min="1" :max="100" style="width:120px;" /></el-form-item>
      </el-form>
      <template #footer><el-button @click="showClassifyRuleDialog = false">取消</el-button><el-button type="primary" @click="saveClassifyRule">保存</el-button></template>
    </MyDialog>
  </div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed, watch, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { budgetApi, kpiApi, mapApi, actionPlanApi } from '../api/index'
import MyDialog from '../components/MyDialog.vue'
import DriverFactorBudget from '../components/DriverFactorBudget.vue'

const activeTab = ref('input')
const currentYear = new Date().getFullYear()
const currentMonth = new Date().getMonth() + 1

function getEntityId() {
  return Number(localStorage.getItem('cma_entity_id') || 1)
}
const yearOptions = computed(() => { const y: number[] = []; for (let i = currentYear - 2; i <= currentYear + 2; i++) y.push(i); return y })
const filterYear = ref(currentYear); const filterMonth = ref(0); const searchKpi = ref(''); const filterVersion = ref('')
const loading = ref(false); const budgetList = ref<any[]>([]); const page = ref(1); const pageSize = ref(20); const total = ref(0); const noMap = ref(false)
const batchEditMode = ref(false); const batchSavingAll = ref(false)

// ── 现金流联动（断点修复#1, 2026-08-27） ──
const syncingCash = ref(false)
async function syncCashPlans() {
  syncingCash.value = true
  try {
    const r: any = await api.post('/budget/sync-cash-plans')
    ElMessage.success(r.message || "现金流计划已同步")
    loadExecutionReport()
  } catch (e: any) {
    ElMessage.error("同步失败: " + (e?.message || ""))
  } finally {
    syncingCash.value = false
  }
}

// ── 战略地图选择 ──
const publishedMaps = ref<any[]>([])
const selectedMapId = ref<number | null>(null)
const selectedMap = ref<any>(null)

async function loadPublishedMaps() {
  try {
    const r: any = await mapApi.list(); const maps = Array.isArray(r) ? r : (r.data || r.items || [])
    const published = maps.filter((m: any) => m.status === 'published')
      .sort((a: any, b: any) => new Date(b.updated_at || b.created_at).getTime() - new Date(a.updated_at || a.created_at).getTime())
    publishedMaps.value = published
    noMap.value = published.length === 0
    // 如果有默认选中的地图ID没变，保持
    if (selectedMapId.value && published.find(m => m.id === selectedMapId.value)) return
    // 默认选中第一个
    if (published.length > 0) {
      selectedMapId.value = published[0].id
      selectedMap.value = published[0]
      // 同步加载版本并默认选中当前版本（与onMapSelect一致, 2026-08-27根治）
      await loadVersions()
      selectDefaultVersion()
    }
  } catch { publishedMaps.value = []; noMap.value = true }
}

async function onMapSelect(id: number) {
  selectedMap.value = publishedMaps.value.find(m => m.id === id) || null
  filterVersion.value = ''  // 切换地图清空版本筛选（重新加载后由用户选择）
  await loadVersions()      // 加载该地图年度版本列表
  selectDefaultVersion()    // 默认选中当前active版本, 避免全版本堆叠
  loadBudget()
}

// 加载版本后默认选中当前active版本（避免全版本堆叠重复期间, 2026-08-27）
function selectDefaultVersion() {
  const cur = versionsList.value.find((v: any) => v.status === 'active') || versionsList.value[0]
  if (cur) filterVersion.value = cur.version
}

function getMapKpiCodes(): Set<string> {
  const codes = new Set<string>(); if (!selectedMap.value) return codes
  let dims = selectedMap.value.dimensions
  if (typeof dims === 'string') { try { dims = JSON.parse(dims) } catch { return codes } }
  if (!Array.isArray(dims)) return codes
  for (const dim of dims) for (const obj of (dim.objectives || [])) for (const code of (obj.kpis || [])) if (code) codes.add(code)
  return codes
}

const budgetViewMode = ref('list'); const summaryCards = ref<any[]>([]); const dimSummaryRows = ref<any[]>([])
function onViewModeChange(mode: string) { if (mode === 'summary') loadSummary() }
function loadSummary() {
  const cfg: Record<string, any> = { finance: { n: '财务维度', i: '💰', c: '#409eff' }, customer: { n: '客户维度', i: '🤝', c: '#67c23a' }, process: { n: '内部流程', i: '⚙️', c: '#e6a23c' }, learning: { n: '学习成长', i: '📚', c: '#f56c6c' } }
  const dd: Record<string, { t: number; n: number }> = {}
  for (const item of budgetList.value) { const d = item.dimension || 'other'; if (!dd[d]) dd[d] = { t: 0, n: 0 }; dd[d].t += (item.budget_value || 0); dd[d].n++ }
  const tb = Object.values(dd).reduce((s, v) => s + v.t, 0); const cards: any[] = []; const rows: any[] = []
  for (const [k, v] of Object.entries(dd)) { const c = cfg[k] || { n: k, i: '📊', c: '#909399' }; const r = tb > 0 ? Math.round(v.t / tb * 100) : 0; cards.push({ key: k, name: c.n, icon: c.i, color: c.c, totalBudget: v.t, kpiCount: v.n, ratio: r }); rows.push({ dimension: k, kpiCount: v.n, totalBudget: v.t, ratio: r, avgPerKpi: v.n > 0 ? Math.round(v.t / v.n) : 0 }) }
  summaryCards.value = cards; dimSummaryRows.value = rows
}

const showAddBudget = ref(false)
const addForm = ref({ kpi_id: null as any, year: currentYear, month: new Date().getMonth() + 1, value: 0 })
const addKpiOptions = ref<any[]>([]); const addKpiLoading = ref(false); const addSaving = ref(false)
async function searchKpiForAdd(query: string) {
  addKpiLoading.value = true
  try { const r: any = await kpiApi.list({ keyword: query, page_size: 20, entity_id: getEntityId() }); const d = r.data || r || []; addKpiOptions.value = Array.isArray(d) ? d : (d.items || []) } catch { }
  addKpiLoading.value = false
}
async function doAddBudget() {
  if (!addForm.value.kpi_id) { ElMessage.warning('请选择KPI'); return }; addSaving.value = true
  try { await budgetApi.create({ kpi_id: addForm.value.kpi_id, period: `${addForm.value.year}-${String(addForm.value.month).padStart(2, '0')}`, budget_value: addForm.value.value, budget_year: addForm.value.year, budget_month: addForm.value.month }); ElMessage.success('预算已创建'); showAddBudget.value = false; loadBudget(); addForm.value = { kpi_id: null, year: currentYear, month: new Date().getMonth() + 1, value: 0 } } catch (e: any) { ElMessage.error(e?.response?.data?.detail || e?.message || '创建失败') }
  addSaving.value = false
}

const dimMap: Record<string, string> = { finance: '财务', customer: '客户', process: '内部流程', learning: '学习成长' }
const dimTagMap: Record<string, string> = { finance: 'danger', customer: 'warning', process: 'primary', learning: 'success' }
function dimLabel(d: string) { return dimMap[d] || d }; function dimTag(d: string) { return dimTagMap[d] || 'info' }
function formatNumber(v: any, unit?: string) { if (v === null || v === undefined) return '--'; const n = Number(v); const p = unit === '%' ? 2 : 0; return n.toLocaleString('zh-CN', { minimumFractionDigits: p, maximumFractionDigits: p }) + (unit ? ` ${unit}` : '') }
function rowClass({ row }: any) { return row._changed ? 'row-changed' : '' }

async function loadBudget() {
  if (batchEditMode.value && changedRows.value.length > 0) { try { await ElMessageBox.confirm(`有 ${changedRows.value.length} 条修改未保存，是否放弃？`, '提示', { confirmButtonText: '放弃修改', cancelButtonText: '取消' }) } catch { return } }
  if (!selectedMap.value) return
  const mapCodes = getMapKpiCodes()
  if (mapCodes.size === 0) { budgetList.value = []; total.value = 0; return }
  noMap.value = false; loading.value = true
  try {
    const params: any = { page: page.value, page_size: pageSize.value, entity_id: getEntityId(), map_id: selectedMapId.value }; if (filterYear.value) params.year = filterYear.value; if (filterVersion.value) params.version = filterVersion.value; if (filterMonth.value > 0) params.budget_month = filterMonth.value; if (searchKpi.value) params.keyword = searchKpi.value
    const r: any = await budgetApi.list(params); const d = r.data || r || []; const allPlans = (Array.isArray(d) ? d : (d.items || [])) as any[]
    // 只保留选中地图关联KPI的预算记录
    const plans = allPlans.filter((p: any) => mapCodes.has(p.kpi_code))
    const kr: any = await kpiApi.list({ page_size: 100, keyword: searchKpi.value || undefined, entity_id: getEntityId() }); const kd = kr.data || kr || []; const ak = Array.isArray(kd) ? kd : (kd.items || [])
    const mk = ak.filter((k: any) => mapCodes.has(k.kpi_code)); const pk = new Set(plans.map((p: any) => p.kpi_code))
    const ek = mk.filter((k: any) => !pk.has(k.kpi_code)).map((k: any) => ({ kpi_id: k.id, kpi_code: k.kpi_code, kpi_name: k.kpi_name, dimension: k.dimension, unit: k.unit || '', period: `${filterYear.value || currentYear}-${String(filterMonth.value || 1).padStart(2, '0')}`, budget_value: null, version: 'v1.0', status: '', precision: 0, step: 1 }))
    budgetList.value = [...plans.map((item: any) => initRow({ ...item, _empty: false })), ...ek.map(item => initRow({ ...item, _empty: true }))]
    total.value = budgetList.value.length; if (budgetViewMode.value === 'summary') loadSummary()
    // 加载非财务维度KPI的行动方案计数
    loadActionPlanCounts()
  } catch (e) { ElMessage.error('加载预算数据失败') }; loading.value = false
}
function initRow(item: any) { return { ...item, _editing: false, _editValue: item.budget_value ?? 0, _originalValue: item.budget_value, _saving: false, _changed: false } }

// ── 行动方案关联 ──
const showActionDialog = ref(false)
const actionDialogTitle = ref('')
const actionPlansForKpi = ref<any[]>([])
const actionDialogCurrentRow = ref<any>(null)

async function loadActionPlanCounts() {
  // 筛选出非财务维度且有关联kpi_id的行
  const rows = budgetList.value.filter(r => r.dimension !== 'finance' && r.kpi_id)
  for (const row of rows) {
    row._actionCount = null // 重置
    try {
      const r: any = await actionPlanApi.list({ kpi_id: row.kpi_id, page_size: 1 })
      const d = r.data || []
      row._actionCount = Array.isArray(d) ? d.length : 0
    } catch { row._actionCount = 0 }
  }
}

function showActionPlans(row: any) {
  actionDialogCurrentRow.value = row
  actionDialogTitle.value = `行动方案 - ${row.kpi_name || row.kpi_code}`
  actionPlansForKpi.value = []
  if (!row.kpi_id) { showActionDialog.value = true; return }
  actionPlanApi.list({ kpi_id: row.kpi_id }).then((r: any) => {
    const d = r.data || []
    actionPlansForKpi.value = Array.isArray(d) ? d : []
  }).catch(() => { actionPlansForKpi.value = [] })
  showActionDialog.value = true
}

function goCreateActionPlan() {
  const row = actionDialogCurrentRow.value
  if (!row) return
  // 跳转到差异分析页面并预填KPI
  window.open(`/action-plans?kpi_id=${row.kpi_id}&kpi_name=${encodeURIComponent(row.kpi_name || '')}`, '_blank')
}

function goActionPlansPage() {
  window.open('/action-plans', '_blank')
}

function onBatchEditToggle(val: boolean) { budgetList.value.forEach(r => { r._editing = val; r._editValue = r.budget_value ?? 0; r._originalValue = r.budget_value; r._changed = false }) }
function onEditChange(row: any) { row._changed = row._editValue !== row._originalValue }
const changedRows = computed(() => budgetList.value.filter(r => r._changed))
async function batchSaveAll() {
  const ts = changedRows.value; if (ts.length === 0) return; batchSavingAll.value = true; let ok = 0; let no = 0
  for (const r of ts) { try { if (r._empty || !r.id) { await budgetApi.create({ kpi_id: r.kpi_id, period: r.period, budget_value: r._editValue, budget_year: filterYear.value || currentYear, budget_month: filterMonth.value || 1 }) } else { await budgetApi.update(r.id, { budget_value: r._editValue }) }; r.budget_value = r._editValue; r._originalValue = r._editValue; r._changed = false; ok++ } catch { no++ } }
  ElMessage.success(`批量保存完成：${ok} 成功，${no} 失败`); batchSavingAll.value = false
}
function batchCancelAll() { budgetList.value.forEach(r => { r._editValue = r._originalValue ?? 0; r._changed = false }) }
function startEdit(row: any) { row._editing = true; row._editValue = row.budget_value ?? 0; row._originalValue = row.budget_value }
function cancelEdit(row: any) { row._editing = false; row._editValue = row.budget_value ?? 0; row._originalValue = row.budget_value; row._changed = false }
async function saveEdit(row: any) {
  row._saving = true
  try { if (row._empty || !row.id) { await budgetApi.create({ kpi_id: row.kpi_id, period: row.period, budget_value: row._editValue, budget_year: filterYear.value || currentYear, budget_month: filterMonth.value || 1 }); ElMessage.success('预算已创建') } else { await budgetApi.update(row.id, { budget_value: row._editValue }); ElMessage.success('已更新') }; row.budget_value = row._editValue; row._originalValue = row._editValue; row._editing = false; row._changed = false; row._empty = false } catch (e) { ElMessage.error('保存失败') }
  row._saving = false
}
async function doDelete(row: any) { try { await ElMessageBox.confirm(`确认删除「${row.kpi_name || row.kpi_code}」的预算？`, '确认'); await budgetApi.delete(row.id); ElMessage.success('已删除'); loadBudget() } catch (e: any) { if (e !== 'cancel') ElMessage.error('删除失败') } }

const showDecompose = ref(false); const decomposeForm = ref({ year: currentYear, method: 'equal' }); const decomposing = ref(false); const decomposeResult = ref(''); const decomposeDetails = ref<any[]>([])
const strategyTree = ref<any[]>([]); const strategyFilterYear = ref(currentYear); const strategyFilterMonth = ref(0); const strategyAllBudgetPlans = ref<any[]>([])

async function loadStrategyBudget() {
  if (!selectedMap.value) return
  try { const p: any = { budget_year: strategyFilterYear.value, entity_id: getEntityId(), map_id: selectedMapId.value }; if (strategyFilterMonth.value > 0) p.budget_month = strategyFilterMonth.value; const r: any = await budgetApi.list(p); const d = r.data || r || []; const plans = (Array.isArray(d) ? d : (d.items || [])) as any[]; strategyAllBudgetPlans.value = plans
    let dims = selectedMap.value.dimensions; if (typeof dims === 'string') { try { dims = JSON.parse(dims) } catch { dims = [] } }; if (!Array.isArray(dims) || dims.length === 0) { strategyTree.value = []; return }
    const kr: any = await kpiApi.list({ page_size: 100, entity_id: getEntityId() }); const kd = kr.data || kr || []; const km = new Map<string, any>(); (Array.isArray(kd) ? kd : (kd.items || [])).forEach((k: any) => km.set(k.kpi_code, k))
    const tree: any[] = []
    for (const dim of dims) { const dt = { budget: 0, kpiCount: 0 }; const objs = (dim.objectives || []).map((obj: any) => { const kpis = (obj.kpis || []).map((code: string) => { const kpi = km.get(code); const plan = plans.find((p: any) => p.kpi_code === code); const bv = plan ? plan.budget_value : null; dt.kpiCount++; if (bv) dt.budget += bv; return { kpi_code: code, kpi_id: kpi?.id || null, kpi_name: kpi?.kpi_name || code, unit: kpi?.unit || '', precision: kpi?.precision || 0, step: kpi?.step || 1, dimension: dim.key, period: plan?.period || `${strategyFilterYear.value}-${String(strategyFilterMonth.value || 1).padStart(2, '0')}`, version: plan?.version || 'v1.0', budget_value: bv, plan_id: plan?.id || null, target: kpi?.target_monthly || kpi?.target_value || null, _editValue: bv ?? 0, _changed: false, _saving: false, _dimKey: dim.key } }).filter(Boolean); return { name: obj.name, kpis } }); tree.push({ key: dim.key, name: dim.name, icon: dim.icon, color: dim.color, kpiCount: dt.kpiCount, totalBudget: dt.budget, objectives: objs }) }
    strategyTree.value = tree
  } catch (e: any) { ElMessage.error('加载战略预算数据失败: ' + (e?.response?.data?.detail || e?.message || '未知错误')) }
}

const strategySelectedKpis = ref<any[]>([]); const strategyBatchValue = ref(0); const strategyBatchSaving = ref(false)
function onStrategyKpiSelect(sel: any[], dimKey: string) { sel.forEach((i: any) => { i._dimKey = dimKey }); strategySelectedKpis.value = [...strategySelectedKpis.value.filter((k: any) => k._dimKey !== dimKey), ...sel] }
function strategySelectDim(dimKey: string) { const dim = strategyTree.value.find((d: any) => d.key === dimKey); if (!dim) return; const all: any[] = []; for (const o of dim.objectives) for (const k of o.kpis) { k._dimKey = dimKey; all.push(k) }; strategySelectedKpis.value = [...strategySelectedKpis.value.filter((k: any) => k._dimKey !== dimKey), ...all] }
function strategyBatchSetSame() { strategySelectedKpis.value.forEach((k: any) => { k._editValue = strategyBatchValue.value; k._changed = (k._editValue !== (k.budget_value ?? 0)) }) }
async function strategyBatchSave() {
  const ts = strategySelectedKpis.value.filter((k: any) => k._changed); if (ts.length === 0) { ElMessage.warning('没有需要保存的变更'); return }; strategyBatchSaving.value = true; let ok = 0; let no = 0
  for (const r of ts) { try { if (r.plan_id) { await budgetApi.update(r.plan_id, { budget_value: r._editValue }) } else { const rr: any = await budgetApi.create({ kpi_id: r.kpi_id, period: r.period, budget_value: r._editValue, budget_year: strategyFilterYear.value, budget_month: strategyFilterMonth.value || 1, map_id: selectedMapId.value }); r.plan_id = rr.id }; r.budget_value = r._editValue; r._changed = false; ok++ } catch { no++ } }
  ElMessage.success(`批量保存完成：${ok} 成功，${no} 失败`); strategyBatchSaving.value = false; loadStrategyBudget()
}
async function saveStrategyKpi(row: any) {
  row._saving = true
  try { if (row.plan_id) { await budgetApi.update(row.plan_id, { budget_value: row._editValue }) } else { const rr: any = await budgetApi.create({ kpi_id: row.kpi_id, period: row.period, budget_value: row._editValue, budget_year: strategyFilterYear.value, budget_month: strategyFilterMonth.value || 1, map_id: selectedMapId.value }); row.plan_id = rr.id }; row.budget_value = row._editValue; row._changed = false; ElMessage.success('已保存') } catch (e) { ElMessage.error('保存失败') }
  row._saving = false
}

async function submitVersion(row: any) { try { await ElMessageBox.confirm(`确认将版本「${row.version}」提交审批？`, '确认'); await budgetApi.versionSubmit({ version: row.version }); ElMessage.success('已提交审批'); loadVersions() } catch { } }
async function approveVersion(row: any, action: string) { const l = action === 'approved' ? '批准' : '驳回'; try { await ElMessageBox.confirm(`确认${l}版本「${row.version}」？`, '确认'); await budgetApi.versionApprove({ version: row.version, action }); ElMessage.success(`已${l}`); loadVersions() } catch { } }
async function resubmitVersion(row: any) { try { await ElMessageBox.confirm(`确认重新提交版本「${row.version}」？`, '确认'); await budgetApi.versionSubmit({ version: row.version }); ElMessage.success('已重新提交'); loadVersions() } catch { } }

const versionYear = ref(currentYear); const versionsList = ref<any[]>([]); const versionsLoading = ref(false); const selectedVersions = ref<any[]>([]); const showVersionDiff = ref(false); const diffLoading = ref(false); const diffData = ref<any>(null); const currentVersion = ref('')
async function loadVersions() { versionsLoading.value = true; try { const r: any = await budgetApi.versions({ year: versionYear.value, entity_id: getEntityId() }); const d = r.data || []; versionsList.value = d.map((v: any) => ({ ...v, _selected: false })); currentVersion.value = d[0]?.version || '' } catch (e) { ElMessage.error('加载版本列表失败') }; versionsLoading.value = false }
function onVersionSelect(row: any) { selectedVersions.value = versionsList.value.filter((v: any) => v._selected); if (selectedVersions.value.length > 2) { row._selected = false; selectedVersions.value = versionsList.value.filter((v: any) => v._selected) } }
async function loadVersionDiff() { if (selectedVersions.value.length !== 2) return; diffLoading.value = true; try { const [a, b] = selectedVersions.value; const r: any = await budgetApi.versionDiff({ version_a: a.version, version_b: b.version, year: versionYear.value }); diffData.value = r; showVersionDiff.value = true } catch (e) { ElMessage.error('加载版本对比失败') }; diffLoading.value = false }

const execFilterYear = ref(currentYear); const execFilterMonth = ref(new Date().getMonth()); const execFilterDim = ref(''); const execLoading = ref(false); const execReport = ref<any[]>([]); const execSummaryCards = ref<any[]>([])
async function loadExecutionReport() {
  execLoading.value = true
  try { const r: any = await budgetApi.deviationReport({ year: execFilterYear.value, month: execFilterMonth.value, dimension: execFilterDim.value || undefined, hierarchical: true, entity_id: getEntityId() }); const items: any[] = r.items || []; const en = items.map((i: any) => ({ ...i, execution_rate: i.budget_value && i.budget_value > 0 ? Math.round((i.actual_value || 0) / i.budget_value * 100) : 0, deviation_value: (i.actual_value || 0) - (i.budget_value || 0) })); execReport.value = en; const hb = en.filter((i: any) => i.budget_value != null); const ob = en.filter((i: any) => i.is_over_budget); const ar = hb.length > 0 ? Math.round(hb.reduce((s, i) => s + (i.execution_rate || 0), 0) / hb.length) : 0; const tb = hb.reduce((s, i) => s + (i.budget_value || 0), 0); const ta = hb.reduce((s, i) => s + (i.actual_value || 0), 0); execSummaryCards.value = [{ label: '有预算KPI', value: `${hb.length}/${en.length}`, color: '#409eff' }, { label: '超预算KPI', value: ob.length.toString(), color: '#f56c6c' }, { label: '平均执行率', value: `${ar}%`, color: ar > 100 ? '#f56c6c' : '#67c23a' }, { label: '预算执行进度', value: tb > 0 ? `${Math.round(ta / tb * 100)}%` : '-', color: '#e6a23c' }] } catch (e) { ElMessage.error('加载执行报告失败') }
  execLoading.value = false
}

async function doDecompose() {
  decomposing.value = true; decomposeResult.value = ''; decomposeDetails.value = []
  try { const r: any = await budgetApi.autoDecompose({ year: decomposeForm.value.year, method: decomposeForm.value.method, entity_id: getEntityId() }); decomposeResult.value = r.message || '分解成功'; if (r.results) decomposeDetails.value = r.results.map((res: any) => ({ kpi_code: res.kpi_code, kpi_name: res.kpi_name, annual_budget: res.annual_budget, method: res.method === 'equal' ? '均分' : '加权', monthly_count: res.monthly.length })); ElMessage.success('年度预算分解完成'); loadBudget() } catch (e: any) { ElMessage.error(e.detail || e.message || '分解失败') }
  decomposing.value = false
}

// ── 预算方法三选一向导 ──
const selectedMethod = ref('zero_based')
const budgetMethods = ref<any[]>([])
const selectedMethodDetail = ref<any>(null)

const selectedMethodName = computed(() => {
  return budgetMethods.value.find(m => m.id === selectedMethod.value)?.name || ''
})

function loadMethodDetail(id: string) {
  selectedMethodDetail.value = budgetMethods.value.find(m => m.id === id) || null
}

async function refreshMethodComparison() {
  try {
    const r: any = await budgetApi.methodComparison({ entity: 'hanke', entity_id: getEntityId() })
    budgetMethods.value = r.methods || []
    selectedMethod.value = r.recommended || 'zero_based'
    loadMethodDetail(selectedMethod.value)
  } catch { ElMessage.error('加载预算方法对比失败') }
}

async function confirmMethod() {
  const m = selectedMethodDetail.value
  if (!m) return
  try {
    const r: any = await budgetApi.applyMethod({
      method: m.id,
      year: currentYear,
      entity: 'hanke',
      entity_id: getEntityId(),
      last_month_budget: 91,
      current_revenue: 122,
    })
    ElMessage.success(r.message || `已应用「${m.name}」`)
    // 刷新预算列表
    loadBudget()
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.detail || e?.message || '应用失败')
  }
}

// ── 滚动/固定预算切换 ──
const budgetMode = ref('fixed')
const rollingForward = ref(false)

async function loadBudgetConfig() {
  try {
    const r: any = await budgetApi.getConfig()
    budgetMode.value = r.budget_mode || r.mode || 'fixed'
  } catch { budgetMode.value = 'fixed' }
}

async function onBudgetModeChange(mode: string) {
  try {
    await budgetApi.setConfig({ mode, rolling_months: 12 })
    ElMessage.success(`预算模式已切换为${mode === 'rolling' ? '滚动预算' : '固定预算'}`)
    loadBudget()
  } catch (e: any) {
    budgetMode.value = mode === 'rolling' ? 'fixed' : 'rolling'
    ElMessage.error(e?.response?.data?.detail || '切换失败')
  }
}

async function doRollForward() {
  rollingForward.value = true
  try {
    const r: any = await budgetApi.rollForward()
    ElMessage.success(r.message || '滚动预算已延展')
    if (r.rolled_kpis?.length > 0) {
      ElMessage.info(`新增了 ${r.rolled_kpis.length} 个KPI的未来一个月预测`)
    }
    loadBudget()
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.detail || '延展失败')
  }
  rollingForward.value = false
}

// ── 实际vs预测对比 ──
const rollingTab = ref('comparison')
const comparisonYear = ref(currentYear)
const comparisonKpiId = ref<number | null>(null)
const comparisonLoading = ref(false)
const comparisonData = ref<any[]>([])
const comparisonSummary = ref<any[]>([])
const comparisonKpiOptions = ref<any[]>([])
const comparisonChartRef = ref<HTMLElement | null>(null)
let comparisonChart: any = null

async function loadComparisonKpiOptions() {
  try {
    const r: any = await kpiApi.list({ page_size: 200, entity_id: getEntityId() })
    const d = r.data || r || []
    comparisonKpiOptions.value = Array.isArray(d) ? d : (d.items || [])
  } catch { comparisonKpiOptions.value = [] }
}

async function loadComparison() {
  comparisonLoading.value = true
  try {
    const params: any = { year: comparisonYear.value, entity_id: getEntityId() }
    if (comparisonKpiId.value) params.kpi_id = comparisonKpiId.value
    const r: any = await budgetApi.getComparison(params)
    comparisonData.value = r.months_data || []
    // 计算摘要
    const md = comparisonData.value
    const totalBudget = md.reduce((s: number, m: any) => s + (m.budget_total || 0), 0)
    const totalActual = md.reduce((s: number, m: any) => s + (m.actual_total || 0), 0)
    const maxDevRate = Math.max(...md.filter((m: any) => m.deviation_rate != null).map((m: any) => Math.abs(m.deviation_rate)), 0)
    const alertCount = md.filter((m: any) => m.deviation_rate != null && Math.abs(m.deviation_rate) > 20).length
    comparisonSummary.value = [
      { label: '预算总额', value: formatNumber(totalBudget), color: '#409eff' },
      { label: '实际总额', value: totalActual > 0 ? formatNumber(totalActual) : '--', color: totalActual > totalBudget ? '#f56c6c' : '#67c23a' },
      { label: '最大偏差率', value: maxDevRate > 0 ? `${maxDevRate}%` : '--', color: maxDevRate > 20 ? '#f56c6c' : '#67c23a' },
      { label: '超20%偏差月数', value: `${alertCount}个月`, color: alertCount > 0 ? '#f56c6c' : '#67c23a' },
    ]
    // 渲染echarts图表
    await nextTick()
    renderComparisonChart()
  } catch (e: any) {
    ElMessage.error('加载对比数据失败')
    comparisonData.value = []
  }
  comparisonLoading.value = false
}

function renderComparisonChart() {
  if (!comparisonChartRef.value) return
  // 使用ECharts
  const echarts = (window as any).echarts
  if (!echarts) {
    // 如果没有全局echarts，尝试从已挂载的组件获取
    import('echarts').then(echarts => {
      doRenderChart(echarts)
    }).catch(() => {
      // ECharts可能已经在vendor bundle中
      if ((window as any).echarts) doRenderChart((window as any).echarts)
    })
    return
  }
  doRenderChart(echarts)
}

function doRenderChart(echarts: any) {
  if (comparisonChart) comparisonChart.dispose()
  comparisonChart = echarts.init(comparisonChartRef.value!)
  const data = comparisonData.value
  const periods = data.map((d: any) => d.period)
  const budgetValues = data.map((d: any) => d.budget_total != null ? d.budget_total : null)
  const actualValues = data.map((d: any) => d.actual_total != null ? d.actual_total : null)

  // 找到分界点（当前期之后为预测）
  const nowPeriod = `${currentYear}-${String(currentMonth).padStart(2, '0')}`
  const splitIndex = periods.findIndex((p: string) => p > nowPeriod)

  const option = {
    tooltip: { trigger: 'axis' },
    legend: { data: ['预算值', '实际值'] },
    grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
    xAxis: { type: 'category', data: periods, axisLabel: { rotate: 45 } },
    yAxis: { type: 'value' },
    series: [
      {
        name: '预算值',
        type: 'line',
        data: budgetValues,
        smooth: true,
        lineStyle: { width: 2, color: '#409eff' },
        itemStyle: { color: '#409eff' },
        markLine: splitIndex >= 0 ? {
          data: [{ xAxis: periods[splitIndex] || periods[splitIndex - 1] }],
          label: { formatter: '▼ 预测开始', color: '#e6a23c' },
          lineStyle: { color: '#e6a23c', type: 'dashed', width: 2 },
        } : undefined,
      },
      {
        name: '实际值',
        type: 'bar',
        data: actualValues,
        barWidth: '20%',
        itemStyle: {
          color: (params: any) => {
            const item = data[params.dataIndex]
            if (!item) return '#67c23a'
            if (item.actual_total == null) return '#d9d9d9'
            if (item.deviation_rate != null && Math.abs(item.deviation_rate) > 20) return '#f56c6c'
            if (item.deviation_rate != null && Math.abs(item.deviation_rate) > 10) return '#e6a23c'
            return '#67c23a'
          },
        },
      },
    ],
  }
  comparisonChart.setOption(option)
}

// ── 偏差告警 ──
const alertFilterPeriod = ref(`${currentYear}-${String(currentMonth).padStart(2, '0')}`)
const alertFilterLevel = ref('')
const alertFilterStatus = ref('')
const alertLoading = ref(false)
const deviationAlerts = ref<any[]>([])
const deviationChecking = ref(false)
const deviationAlertMessage = ref('')

const alertPeriodOptions = computed(() => {
  const opts: string[] = []
  for (let m = 1; m <= 12; m++) {
    opts.push(`${currentYear}-${String(m).padStart(2, '0')}`)
  }
  return opts
})

async function loadDeviationAlerts() {
  alertLoading.value = true
  try {
    const params: any = { entity_id: getEntityId() }
    if (alertFilterPeriod.value) params.period = alertFilterPeriod.value
    if (alertFilterLevel.value) params.alert_level = alertFilterLevel.value
    if (alertFilterStatus.value) params.status = alertFilterStatus.value
    const r: any = await budgetApi.listDeviationAlerts(params)
    deviationAlerts.value = r.data || []
  } catch { deviationAlerts.value = [] }
  alertLoading.value = false
}

async function doDeviationCheck() {
  deviationChecking.value = true
  deviationAlertMessage.value = ''
  try {
    const r: any = await budgetApi.deviationCheck({
      period: alertFilterPeriod.value,
      threshold: 20,
      entity_id: getEntityId(),
    })
    if (r.alerts_generated > 0) {
      deviationAlertMessage.value = `偏差检查完成：生成了 ${r.alerts_generated} 条预警`
      ElMessage.warning(`发现 ${r.alerts_generated} 条偏差预警`)
    } else {
      deviationAlertMessage.value = '偏差检查完成：未发现超过20%的偏差'
      ElMessage.success('未发现偏差预警')
    }
    loadDeviationAlerts()
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.detail || '偏差检查失败')
  }
  deviationChecking.value = false
}

async function resolveAlert(row: any) {
  try {
    await budgetApi.updateDeviationAlert(row.id, { status: 'resolved' })
    ElMessage.success('已标记为已解决')
    row.status = 'resolved'
    loadDeviationAlerts()
  } catch { ElMessage.error('操作失败') }
}

async function reopenAlert(row: any) {
  try {
    await budgetApi.updateDeviationAlert(row.id, { status: 'open' })
    ElMessage.success('已重新打开')
    row.status = 'open'
    loadDeviationAlerts()
  } catch { ElMessage.error('操作失败') }
}

// ══════════════════════════════════════════════
// P1-③ 告警归因
// ══════════════════════════════════════════════
const showAttributionDialog = ref(false)
const attributionDetail = ref<any>(null)
async function showAlertAttribution(row: any) {
  try {
    const r: any = await budgetApi.getAlertAttribution(row.id)
    attributionDetail.value = r.data || r
    showAttributionDialog.value = true
  } catch (e) { ElMessage.error('加载归因详情失败') }
}

// ══════════════════════════════════════════════
// 通用 KPI 选项（零基/取数映射/派生规则/分类规则共用）
// ══════════════════════════════════════════════
const zbbKpiOptions = ref<any[]>([])
async function loadZbbKpiOptions() {
  try {
    const r: any = await kpiApi.list({ page_size: 200, entity_id: getEntityId() })
    const d = r.data || r || []
    zbbKpiOptions.value = Array.isArray(d) ? d : (d.items || [])
  } catch { zbbKpiOptions.value = [] }
}

// ══════════════════════════════════════════════
// P2-① 零基逐项论证
// ══════════════════════════════════════════════
const zbbKpiId = ref<number | null>(null)
const zbbPeriod = ref(`${currentYear}-${String(currentMonth).padStart(2, '0')}`)
const zbbPeriodOptions = computed(() => {
  const opts: string[] = []
  const now = new Date()
  for (let i = 5; i >= 0; i--) {
    const d = new Date(now.getFullYear(), now.getMonth() - i, 1)
    opts.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`)
  }
  return opts
})
const zeroBasedItems = ref<any[]>([])
const zbbTotalProposed = ref<number | null>(null)
const showZbbItemDialog = ref(false)
const zbbItemForm = ref<any>({})
const zbbGenerating = ref(false)
async function loadZeroBasedItems() {
  if (!zbbKpiId.value) { zeroBasedItems.value = []; zbbTotalProposed.value = null; return }
  try {
    const r: any = await budgetApi.zeroBasedItems({ kpi_id: zbbKpiId.value, period: zbbPeriod.value, entity_id: getEntityId() })
    zeroBasedItems.value = r.data || []
    zbbTotalProposed.value = r.total_proposed ?? null
  } catch { zeroBasedItems.value = [] }
}
function addZeroBasedItem() {
  if (!zbbKpiId.value) { ElMessage.warning('请先选择KPI'); return }
  zbbItemForm.value = { kpi_id: zbbKpiId.value, period: zbbPeriod.value, item_name: '', item_category: 'discretionary', base_value: 0, justification: '', proposed_value: 0, status: 'draft' }
  showZbbItemDialog.value = true
}
function editZeroBasedItem(row: any) {
  zbbItemForm.value = { ...row }
  showZbbItemDialog.value = true
}
async function saveZeroBasedItem() {
  const f = zbbItemForm.value
  if (!f.item_name) { ElMessage.warning('请填写费用科目'); return }
  try {
    if (f.id) await budgetApi.updateZeroBasedItem(f.id, f)
    else await budgetApi.createZeroBasedItem(f)
    ElMessage.success('已保存')
    showZbbItemDialog.value = false
    loadZeroBasedItems()
  } catch (e) { ElMessage.error('保存失败') }
}
async function deleteZeroBasedItem(row: any) {
  try {
    await ElMessageBox.confirm(`确认删除论证项「${row.item_name}」？`, '确认')
    await budgetApi.deleteZeroBasedItem(row.id)
    ElMessage.success('已删除')
    loadZeroBasedItems()
  } catch { }
}
async function generateZeroBased() {
  if (!zbbKpiId.value || !zbbPeriod.value) return
  zbbGenerating.value = true
  try {
    const r: any = await budgetApi.generateZeroBased({ kpi_id: zbbKpiId.value, period: zbbPeriod.value, entity_id: getEntityId() })
    ElMessage.success(r.message || '零基预算已生成')
  } catch (e: any) { ElMessage.error(e?.response?.data?.detail || '生成失败') }
  zbbGenerating.value = false
}

// ══════════════════════════════════════════════
// P2-② 派生规则配置
// ══════════════════════════════════════════════
const derivationRules = ref<any[]>([])
const showDerivationRuleDialog = ref(false)
const derivationRuleForm = ref<any>({})
async function loadDerivationRules() {
  try {
    const r: any = await budgetApi.derivationRules({ entity_id: getEntityId() })
    derivationRules.value = r.data || []
  } catch { derivationRules.value = [] }
}
function openDerivationRuleDialog() {
  derivationRuleForm.value = { kpi_id: null, rule_type: 'percentage_of', base_kpi_id: null, rate_pct: 2, formula_text: '' }
  showDerivationRuleDialog.value = true
}
async function saveDerivationRule() {
  const f = derivationRuleForm.value
  if (!f.kpi_id) { ElMessage.warning('请选择目标KPI'); return }
  try {
    await budgetApi.createDerivationRule({
      kpi_id: f.kpi_id,
      rule_type: f.rule_type,
      base_kpi_id: f.rule_type === 'percentage_of' ? f.base_kpi_id : null,
      params: { rate: (f.rate_pct ?? 0) / 100 },
      formula_text: f.formula_text,
      entity_id: getEntityId(),
    })
    ElMessage.success('规则已创建')
    showDerivationRuleDialog.value = false
    loadDerivationRules()
  } catch (e: any) { ElMessage.error(e?.response?.data?.detail || '创建失败') }
}
async function deleteDerivationRule(row: any) {
  try {
    await ElMessageBox.confirm(`确认删除规则「${row.kpi_name || row.kpi_code}」？`, '确认')
    await budgetApi.deleteDerivationRule(row.id)
    ElMessage.success('已删除')
    loadDerivationRules()
  } catch { }
}

// ══════════════════════════════════════════════
// P1-④ 实际值自动归集
// ══════════════════════════════════════════════
const collectTab = ref('mappings')
const collectKpiId = ref<number | null>(null)
const collectCoverageCards = ref<any[]>([])
const valueSources = ref<any[]>([])
const showValueSourceDialog = ref(false)
const valueSourceForm = ref<any>({})
const collectRunning = ref(false)
const collectLogStatus = ref('')
const collectLogs = ref<any[]>([])
const valueTagFilter = ref('auto_collect')
const valueTagRows = ref<any[]>([])
async function loadCollectData() {
  await Promise.all([loadValueSources(), loadCollectCoverage(), loadValueTags(), loadCollectLogs()])
}
async function loadValueSources() {
  try {
    const params: any = { entity_id: getEntityId() }
    if (collectKpiId.value) params.kpi_id = collectKpiId.value
    const r: any = await budgetApi.valueSources(params)
    valueSources.value = r.data || []
  } catch { valueSources.value = [] }
}
async function loadCollectCoverage() {
  try {
    const r: any = await budgetApi.valueSourceCoverage({ entity_id: getEntityId() })
    const c = r.data || r || {}
    collectCoverageCards.value = [
      { label: '已配映射KPI', value: c.mapped_count ?? 0, color: '#409eff' },
      { label: '活跃KPI总数', value: c.total_kpis ?? 0, color: '#606266' },
      { label: '覆盖率', value: `${c.coverage_pct ?? 0}%`, color: '#67c23a' },
      { label: '未配置KPI', value: c.unmapped_count ?? 0, color: '#e6a23c' },
    ]
  } catch { collectCoverageCards.value = [] }
}
async function loadValueTags() {
  try {
    const r: any = await kpiApi.list({ page_size: 100, entity_id: getEntityId() })
    const d = r.data || r || []
    const kpis = Array.isArray(d) ? d : (d.items || [])
    const rows: any[] = []
    for (const k of kpis) {
      const vr: any = await kpiApi.values(k.id, { entity_id: getEntityId() }).catch(() => null)
      const vals = vr?.data || []
      for (const v of (Array.isArray(vals) ? vals : (vals.items || []))) {
        rows.push({ kpi_code: k.kpi_code, kpi_name: k.kpi_name, period: v.period, actual_value: v.actual_value, source_type: v.source_type || 'manual', source_batch: v.source_batch || '', remark: v.remark || '' })
      }
    }
    const recent = rows.filter((r: any) => r.actual_value != null).sort((a: any, b: any) => (b.period || '').localeCompare(a.period || '')).slice(0, 100)
    valueTagRows.value = valueTagFilter.value === 'all' ? recent : recent.filter((r: any) => (valueTagFilter.value === 'auto_collect' ? r.source_type === 'auto_collect' : r.source_type !== 'auto_collect'))
  } catch { valueTagRows.value = [] }
}
async function loadCollectLogs() {
  try {
    const params: any = { entity_id: getEntityId() }
    if (collectLogStatus.value) params.status = collectLogStatus.value
    const r: any = await budgetApi.valueCollectLogs(params)
    collectLogs.value = r.data || []
  } catch { collectLogs.value = [] }
}
function openValueSourceDialog() {
  valueSourceForm.value = { kpi_id: null, source_table: 'voucher_details', source_field: 'credit_amount', aggregate: 'sum', direction: '', subject_code: '', period_field: 'period', unit_conversion: 1 }
  showValueSourceDialog.value = true
}
async function saveValueSource() {
  const f = valueSourceForm.value
  if (!f.kpi_id) { ElMessage.warning('请选择KPI'); return }
  const filter: any = {}
  if (f.direction) filter.direction = f.direction
  if (f.subject_code) filter.subject_code = f.subject_code
  try {
    await budgetApi.createValueSource({
      kpi_id: f.kpi_id,
      source_table: f.source_table,
      source_field: f.source_field,
      aggregate: f.aggregate,
      filter_rule: Object.keys(filter).length ? filter : null,
      period_field: f.period_field,
      unit_conversion: f.unit_conversion,
      entity_id: getEntityId(),
    })
    ElMessage.success('映射已创建')
    showValueSourceDialog.value = false
    loadValueSources()
    loadCollectCoverage()
  } catch (e: any) { ElMessage.error(e?.response?.data?.detail || '创建失败') }
}
async function deleteValueSource(row: any) {
  try {
    await ElMessageBox.confirm(`确认删除映射（${row.kpi_name} ← ${row.source_table}）？`, '确认')
    await budgetApi.deleteValueSource(row.id)
    ElMessage.success('已删除')
    loadValueSources()
    loadCollectCoverage()
  } catch { }
}
async function toggleValueSource(row: any) {
  try {
    await budgetApi.updateValueSource(row.id, { status: row.status === 'active' ? 'inactive' : 'active' })
    row.status = row.status === 'active' ? 'inactive' : 'active'
  } catch { ElMessage.error('操作失败') }
}
async function testValueSource(row: any) {
  try {
    const r: any = await budgetApi.testValueSource({
      kpi_id: row.kpi_id,
      source_table: row.source_table,
      source_field: row.source_field,
      aggregate: row.aggregate,
      filter_rule: row.filter_rule,
      period_field: row.period_field,
      unit_conversion: row.unit_conversion,
      period: zbbPeriod.value,
    })
    const d = r.data || r
    if (d.success) ElMessage.success(`试跑值: ${formatNumber(d.value)} (${d.message || ''})`)
    else ElMessage.error(`试跑失败: ${d.message || ''}`)
  } catch { ElMessage.error('试跑失败') }
}
async function runValueCollect() {
  collectRunning.value = true
  try {
    const r: any = await budgetApi.runValueCollect({ period: zbbPeriod.value, entity_id: getEntityId() })
    const d = r.data || r
    ElMessage.success(`采集完成: 成功${d.collected ?? 0} 失败${d.failed ?? 0}`)
    loadCollectData()
  } catch { ElMessage.error('采集失败') }
  collectRunning.value = false
}

// ══════════════════════════════════════════════
// P2-⑥ 现金流分类规则 + 待分类队列
// ══════════════════════════════════════════════
const cashClassifyRules = ref<any[]>([])
const showClassifyRuleDialog = ref(false)
const classifyRuleForm = ref<any>({})
const unclassifiedRows = ref<any[]>([])
const unclassifiedCount = ref(0)
async function loadCashClassify() {
  try {
    const r: any = await budgetApi.cashClassifyRules({ entity_id: getEntityId() })
    cashClassifyRules.value = r.data || []
  } catch { cashClassifyRules.value = [] }
  try {
    const r: any = await budgetApi.cashUnclassified({ status: 'pending', entity_id: getEntityId() })
    unclassifiedRows.value = r.data || []
    unclassifiedCount.value = (r.data || []).length
  } catch { unclassifiedRows.value = []; unclassifiedCount.value = 0 }
}
function openClassifyRuleDialog() {
  classifyRuleForm.value = { kpi_id: null, kpi_code_pattern: '', plan_type: 'receive', priority: 10 }
  showClassifyRuleDialog.value = true
}
async function saveClassifyRule() {
  const f = classifyRuleForm.value
  if (!f.kpi_id && !f.kpi_code_pattern) { ElMessage.warning('请选择KPI或填写关键词'); return }
  try {
    await budgetApi.createCashClassifyRule({
      kpi_id: f.kpi_id || null,
      kpi_code_pattern: f.kpi_code_pattern || null,
      plan_type: f.plan_type,
      priority: f.priority || 10,
      entity_id: getEntityId(),
    })
    ElMessage.success('规则已创建')
    showClassifyRuleDialog.value = false
    loadCashClassify()
  } catch (e: any) { ElMessage.error(e?.response?.data?.detail || '创建失败') }
}
async function deleteClassifyRule(row: any) {
  try {
    await ElMessageBox.confirm('确认删除该分类规则？', '确认')
    await budgetApi.deleteCashClassifyRule(row.id)
    ElMessage.success('已删除')
    loadCashClassify()
  } catch { }
}
async function classifyUnclassified(row: any, planType: string) {
  try {
    const r: any = await budgetApi.classifyCashUnclassified(row.id, { plan_type: planType })
    ElMessage.success(r.message || `已归类为${planType === 'receive' ? '收' : '付'}`)
    loadCashClassify()
  } catch (e: any) { ElMessage.error(e?.response?.data?.detail || '归类失败') }
}
async function ignoreUnclassified(row: any) {
  try {
    await budgetApi.ignoreCashUnclassified(row.id)
    ElMessage.success('已忽略')
    loadCashClassify()
  } catch { ElMessage.error('操作失败') }
}

watch(activeTab, (tab) => {
  if (tab === 'decompose') loadBudget()
  else if (tab === 'strategy') loadStrategyBudget()
  else if (tab === 'versions') loadVersions()
  else if (tab === 'execution') { loadExecutionReport(); loadCashClassify() }
  else if (tab === 'method') { refreshMethodComparison(); loadDerivationRules() }
  else if (tab === 'collect') loadCollectData()
  else if (tab === 'driver') {
    // DriverFactorBudget handles its own loading on mount
  }
  else if (tab === 'rolling') {
    loadComparison()
    loadDeviationAlerts()
  }
})
onMounted(async () => {
  await loadPublishedMaps()
  loadBudget()
  loadBudgetConfig()
  loadComparisonKpiOptions()
  loadZbbKpiOptions()
})
</script>

<style scoped>
:deep(.row-changed) { --el-table-tr-bg-color: #fff7e6 !important; }
.strategy-dim-block { margin-bottom: 20px; border: 1px solid #ebeef5; border-radius: 8px; overflow: hidden; }
.strategy-dim-header { padding: 10px 16px; background: #f5f7fa; border-left: 4px solid #409eff; display: flex; align-items: center; gap: 8px; }
.strategy-dim-icon { font-size: 20px; }
.strategy-dim-name { font-weight: 600; font-size: 15px; }
.strategy-dim-summary { font-size: 12px; color: #999; }
.strategy-obj-block { padding: 8px 16px; }
.strategy-obj-block:not(:last-child) { border-bottom: 1px solid #f0f0f0; }
.strategy-obj-header { padding: 6px 0 8px; font-size: 13px; color: #555; font-weight: 500; }
.strategy-obj-name::before { content: '◆ '; color: #999; font-size: 10px; }
</style>
