<script setup lang="ts">
import { Download, Plus, Refresh, Search, Setting, UploadFilled } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { computed, onMounted, reactive, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { http } from '../api/http';

type Column = { prop: string; label: string; width?: number; minWidth?: number; tag?: boolean };
type FieldKind = 'text' | 'textarea' | 'select' | 'radio' | 'checkbox' | 'upload' | 'date' | 'number';
type FormField = {
  prop: string;
  label: string;
  kind: FieldKind;
  options?: string[];
  placeholder?: string;
  span?: 1 | 2;
  multiple?: boolean;
};
type ModuleConfig = {
  title: string;
  desc: string;
  primary?: string;
  allowCreate?: boolean;
  allowEdit?: boolean;
  allowDelete?: boolean;
  allowProcess?: boolean;
  workflow: string[];
  metrics: Array<{ label: string; value: string; hint: string }>;
  filters: string[];
  columns: Column[];
  rows: Record<string, string>[];
  cards: Array<{ title: string; value: string; items: string[] }>;
  logicRules?: Array<{ name: string; scope: string; formula: string; owner: string; editable: string; notes: string }>;
  warningSettings?: Record<string, any>;
};
type ReportRule = {
  code?: string;
  name: string;
  scope: string;
  formula: string;
  formulaTemplate?: string;
  dataSourceKeys?: string[];
  owner: string;
  editable: string;
  notes: string;
  status: number;
  sortOrder: number;
};

const emptyConfig: ModuleConfig = {
  title: '运营模块',
  desc: '页面数据由运营后台数据库配置',
  primary: '新建',
  workflow: [],
  metrics: [],
  filters: [],
  columns: [],
  rows: [],
  cards: [],
  logicRules: [],
};

const route = useRoute();
const router = useRouter();
const loading = ref(false);
const config = ref<ModuleConfig>(emptyConfig);
const keyword = ref('');
const selectedFilters = ref<string[]>([]);
const detailRow = ref<Record<string, string> | null>(null);
const previewRow = ref<Record<string, string> | null>(null);
const createDialog = ref(false);
const columnDialog = ref(false);
const warningSettingsDialog = ref(false);
const reportRulesDialog = ref(false);
const editingRow = ref<Record<string, string> | null>(null);
const createForm = reactive<Record<string, any>>({});
const warningSettings = reactive<Record<string, any>>({});
const reportRules = ref<ReportRule[]>([]);
const visibleColumns = ref<string[]>([]);
const currentPage = ref(1);
const pageSize = ref(10);
const pageSizes = [10, 20, 30];
const moduleKey = computed(() => String(route.meta.module || ''));
const isWarnings = computed(() => moduleKey.value === 'warnings');
const isReports = computed(() => moduleKey.value === 'reports');
const isContentTemplateModule = computed(() => ['manuals', 'announcements'].includes(moduleKey.value));
const canCreate = computed(() => config.value.allowCreate !== false);
const canEdit = computed(() => config.value.allowEdit !== false);
const canDelete = computed(() => config.value.allowDelete !== false);
const canProcess = computed(() => config.value.allowProcess !== false);
const actionColumnWidth = computed(() => {
  const actionCount = 1 + Number(isContentTemplateModule.value) + Number(canEdit.value) + Number(canProcess.value) + Number(canDelete.value);
  return actionCount >= 5 ? 250 : actionCount >= 3 ? 210 : 140;
});
const contentTemplateOptions = [
  '帮助中心文章',
  '步骤指南',
  '更新公告',
  '政策说明',
  '强制确认弹窗',
  '新闻资讯卡片',
];
const contentAudienceOptions = ['企业侧', '运营侧', '全部用户', '企业管理员', '企业成员', '运营人员'];
const tableColumns = computed(() =>
  config.value.columns
    .filter((column) => visibleColumns.value.includes(column.prop))
    .map((column) => ({
      ...column,
      width: undefined,
      minWidth: column.minWidth || column.width || 150,
    })),
);
const detailEntries = computed(() => {
  if (!detailRow.value) return [];
  const labelMap = new Map(config.value.columns.map((column) => [column.prop, column.label]));
  return Object.entries(detailRow.value)
    .filter(([, value]) => value !== undefined && value !== null && String(value) !== '')
    .map(([key, value]) => ({ key, label: labelMap.get(key) || fieldLabel(key), value: String(value) }));
});
const filterColumns = computed(() => {
  const preferred = config.value.columns.filter((column) => {
    return column.tag || /状态|类型|等级|阶段|角色|优先级|阅读|负责人|归属|分类/.test(column.label);
  });
  return preferred.length ? preferred : config.value.columns;
});
const filterOptions = computed(() => {
  return config.value.filters.map((label, index) => {
    const column = filterColumns.value[index % Math.max(filterColumns.value.length, 1)];
    const options = column
      ? Array.from(new Set(config.value.rows.map((row) => String(row[column.prop] || '')).filter(Boolean)))
      : [];
    return { label, prop: column?.prop || '', options };
  });
});
const filteredRows = computed(() => {
  const term = keyword.value.trim().toLowerCase();
  return config.value.rows.filter((row) => {
    const hitKeyword = !term || Object.values(row).some((value) => String(value || '').toLowerCase().includes(term));
    const hitFilters = selectedFilters.value.every((value) => {
      if (!value || value === 'all') return true;
      const [prop, selected] = value.split('::');
      return prop ? String(row[prop] || '') === selected : JSON.stringify(row).includes(selected);
    });
    return hitKeyword && hitFilters;
  });
});
const pagedRows = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value;
  return filteredRows.value.slice(start, start + pageSize.value);
});
const createFields = computed(() => {
  const fields = [...(moduleFieldMap[moduleKey.value] || []), ...config.value.columns.map(columnToField)];
  const seen = new Set<string>();
  return fields
    .filter((field) => {
      if (seen.has(field.prop)) return false;
      seen.add(field.prop);
      return true;
    })
    .map(resolveFieldOptions);
});
const warningChannelOptions = ['站内信', '短信', '邮件', '企业微信'];
const warningOwnerOptions = ['运营一组', '财务值班', '技术值班', '客服主管', '内容运营', '商务负责人'];
const reportOwnerOptions = ['运营负责人', '计费负责人', '财务运营', '客服主管', '内容运营', '系统'];
const reportEditableOptions = ['可调整排除条件', '可调整复购窗口', '只允许调整维度分组', '可调整天数阈值', '可在接入订单表后替换为实付金额', '需管理员确认'];
const reportDataSourceOptions = [
  { label: '企业租户', value: 'tenants' },
  { label: '组织员工', value: 'users' },
  { label: '成员关系', value: 'tenant_members' },
  { label: '短剧项目', value: 'projects' },
  { label: '资源流水', value: 'billing_records' },
  { label: '企业余额', value: 'tenant_balances' },
  { label: '套餐配额', value: 'tenant_quotas' },
  { label: '套餐配置', value: 'plans' },
  { label: 'AI 任务', value: 'ai_tasks' },
];
const reportRuleTemplateOptions = [
  {
    code: 'paid_conversion_rate',
    name: '付费转化率',
    formula: '付费转化率 = 付费企业数 / 有效企业数 * 100%',
    sources: ['tenants', 'billing_records'],
  },
  {
    code: 'repurchase_rate',
    name: '复购率',
    formula: '复购率 = 复购企业数 / 已付费企业数 * 100%',
    sources: ['billing_records'],
  },
  {
    code: 'resource_consumption',
    name: '资源消耗结构',
    formula: '模型消耗 = 成功模型调用积分合计',
    sources: ['billing_records', 'ai_tasks'],
  },
  {
    code: 'tenant_activity',
    name: '企业活跃分层',
    formula: '活跃企业 = 近 7 天登录或近 14 天有项目/资源行为；沉睡企业 = 30 天无有效行为',
    sources: ['tenants', 'users', 'projects', 'billing_records'],
  },
  {
    code: 'average_recharge',
    name: '平均单笔充值积分',
    formula: '平均单笔充值积分 = 充值积分合计 / 充值笔数',
    sources: ['billing_records'],
  },
  {
    code: 'monthly_report',
    name: '月报生成规则',
    formula: '月报 = 按规则快照汇总企业、成员、项目、资源流水与配额数据',
    sources: ['tenants', 'users', 'projects', 'billing_records', 'tenant_quotas'],
  },
];

const moduleStatusOptions: Record<string, string[]> = {
  tenantAudits: ['资料待审', '待补件', '人工复核', '待开通', '已通过', '已驳回'],
  ordersInvoices: ['待支付', '已支付', '待开票', '已邮寄', '退款中', '已归档'],
  orderOperationLogs: ['成功', '失败', '系统生成', '已归档'],
  warnings: ['待处理', '处理中', '已关闭', '已升级'],
  reports: ['待计算', '计算中', '已生成', '生成失败'],
  tickets: ['待响应', '处理中', '待客户确认', '已解决', '已关闭'],
  scriptLibrary: ['草稿', '待审核', '已发布', '已下线'],
  supportPerformance: ['待复盘', '已复盘'],
  manuals: ['草稿', '已发布', '已下线'],
  announcements: ['草稿', '待发布', '已发布', '已撤回'],
  globalSettings: ['待生效', '已生效', '已停用'],
  permissionConfig: ['启用', '停用'],
  modelConfig: ['已接入', '待补全', '已停用'],
};

const moduleFieldMap: Record<string, FormField[]> = {
  tenantAudits: [
    { prop: 'company', label: '企业名称', kind: 'text', placeholder: '请填写营业执照上的企业全称' },
    { prop: 'creditCode', label: '统一社会信用代码', kind: 'text', placeholder: '15-18 位统一社会信用代码' },
    { prop: 'legalPerson', label: '法人姓名', kind: 'text' },
    { prop: 'agentName', label: '经办人', kind: 'text', placeholder: '如非法人办理，请上传授权书' },
    { prop: 'contact', label: '联系人', kind: 'text' },
    { prop: 'phone', label: '联系电话', kind: 'text' },
    { prop: 'contactEmail', label: '联系邮箱', kind: 'text' },
    { prop: 'ownerUsername', label: 'Owner 登录名', kind: 'text' },
    { prop: 'ownerEmail', label: 'Owner 登录邮箱', kind: 'text' },
    { prop: 'ownerPassword', label: 'Owner 初始密码', kind: 'text', placeholder: '不填默认 Dramix@123' },
    { prop: 'industry', label: '所属行业', kind: 'select', options: ['短剧制作', '影视传媒', 'MCN 内容机构', '广告营销', '教育培训', '企业宣传', '其他'] },
    { prop: 'companyScale', label: '企业规模', kind: 'select', options: ['1-10 人', '11-50 人', '51-200 人', '200 人以上'] },
    { prop: 'trialPlan', label: '拟开通套餐', kind: 'select', options: ['免费版试用', '专业版试用', '团队版试用', '企业版试用'] },
    { prop: 'businessScope', label: '经营范围', kind: 'textarea', span: 2, placeholder: '如：广播电视节目制作、数字内容服务、AI 视频制作等' },
    { prop: 'licenseFile', label: '营业执照图片', kind: 'upload', span: 2 },
    { prop: 'identityFile', label: '法人身份证/人脸核验材料', kind: 'upload', span: 2 },
    { prop: 'authorizationFile', label: '经办人授权书', kind: 'upload', span: 2 },
    { prop: 'licenseStatus', label: '营业执照核验', kind: 'select', options: ['待验真', '已验真', '待补件', '证照不一致'] },
    { prop: 'identityStatus', label: '身份核验', kind: 'select', options: ['待核身', '人证一致', '待补件', '姓名不一致'] },
    { prop: 'authorizationStatus', label: '授权书核验', kind: 'select', options: ['无需授权', '待确认', '已确认', '待补件'] },
    { prop: 'riskFactors', label: '风险项', kind: 'checkbox', span: 2, options: ['证照信息一致', '法人身份一致', '经营范围匹配', '无历史欠费', '需人工复核'] },
    { prop: 'reviewConclusion', label: '审核结论', kind: 'radio', options: ['准入通过', '补充资料', '暂缓开通', '拒绝入驻'] },
    { prop: 'reviewNote', label: '审核意见', kind: 'textarea', span: 2 },
  ],
  permissionConfig: [
    { prop: 'company', label: '企业名称', kind: 'select' },
    { prop: 'member', label: '成员账号', kind: 'select' },
    { prop: 'feature', label: '功能权限', kind: 'select', options: ['团队管理', '用量统计', '模型配置', '项目管理', '资产管理', '费用管理', '安全审计'] },
    { prop: 'permissionLevel', label: '授权结果', kind: 'radio', options: ['允许', '只读', '禁止'] },
    { prop: 'permissionScope', label: '数据范围', kind: 'select', options: ['企业全部数据', '企业运营数据', '本人参与项目', '本企业'] },
    { prop: 'status', label: '状态', kind: 'select', options: ['启用', '停用'] },
    { prop: 'remark', label: '配置说明', kind: 'textarea', span: 2 },
  ],
  modelConfig: [
    { prop: 'company', label: '企业名称', kind: 'select' },
    { prop: 'providerName', label: '供应商名称', kind: 'text', placeholder: '如：火山引擎视频模型' },
    { prop: 'protocol', label: '接入协议', kind: 'select', options: ['火山引擎', 'OpenAI', '通义千问', 'Gemini', '可灵'] },
    { prop: 'capability', label: '模型能力', kind: 'select', options: ['文本生成', '图片生成', '视频生成', '音频生成'] },
    { prop: 'baseUrl', label: '接口地址', kind: 'text' },
    { prop: 'apiKey', label: 'API Key', kind: 'text', placeholder: '留空则保持原密钥' },
    { prop: 'secretKey', label: 'Secret Key', kind: 'text', placeholder: '留空则保持原密钥' },
    { prop: 'modelsJson', label: '可用模型清单', kind: 'textarea', span: 2, placeholder: '[{\"id\":\"模型ID\",\"name\":\"模型名称\",\"checked\":true}]' },
  ],
  ordersInvoices: [
    { prop: 'paymentProof', label: '支付凭证/线下汇款截图', kind: 'upload', span: 2 },
    { prop: 'invoiceType', label: '发票类型', kind: 'radio', options: ['不开票', '增值税普通发票', '增值税专用发票'] },
    { prop: 'invoiceTitle', label: '发票抬头', kind: 'text' },
    { prop: 'taxNo', label: '纳税人识别号', kind: 'text' },
    { prop: 'refundReason', label: '退款/异常说明', kind: 'textarea', span: 2 },
  ],
  warnings: [
    { prop: 'type', label: '预警类型', kind: 'select', options: ['资源不足', '任务失败率', '入驻审核超时', '工单 SLA', '发票待处理', '内容安全', '套餐到期', '沉睡客户'] },
    { prop: 'level', label: '预警等级', kind: 'radio', options: ['低', '中', '高', '严重'] },
    { prop: 'triggerValue', label: '触发值', kind: 'text', placeholder: '如：剩余 12%、失败率 28%、5.2 小时' },
    { prop: 'threshold', label: '触发阈值', kind: 'text', placeholder: '如：<20%、>4 小时、>=3 次' },
    { prop: 'channels', label: '通知渠道', kind: 'checkbox', options: ['站内信', '短信', '邮件', '企业微信'] },
    { prop: 'owner', label: '处理人', kind: 'select', options: ['运营一组', '财务值班', '技术值班', '客服主管'] },
    { prop: 'resolution', label: '处置说明', kind: 'textarea', span: 2 },
  ],
  reports: [
    { prop: 'dateRange', label: '统计周期', kind: 'select', options: ['今日', '近 7 天', '近 30 天', '本月', '上月', '自定义'] },
    { prop: 'dimensions', label: '统计维度', kind: 'checkbox', span: 2, options: ['租户', '资源包', '模型', '行业', '销售归属', '任务类型'] },
    { prop: 'format', label: '输出格式', kind: 'radio', options: ['在线报表', 'Excel', 'CSV', 'PDF'] },
  ],
  tickets: [
    { prop: 'channel', label: '来源渠道', kind: 'select', options: ['在线客服', '电话', '企业微信', '邮件', '运营代提'] },
    { prop: 'priority', label: '优先级', kind: 'radio', options: ['普通', '加急', '紧急'] },
    { prop: 'description', label: '问题描述', kind: 'textarea', span: 2 },
    { prop: 'attachments', label: '问题截图/录屏', kind: 'upload', span: 2 },
  ],
  scriptLibrary: [
    { prop: 'category', label: '分类', kind: 'select' },
    { prop: 'scene', label: '适用场景', kind: 'select' },
    { prop: 'targetRole', label: '适用对象', kind: 'select' },
    { prop: 'status', label: '状态', kind: 'select' },
    { prop: 'triggerKeywords', label: '触发关键词', kind: 'textarea', span: 2 },
    { prop: 'replyTemplate', label: '标准回复话术', kind: 'textarea', span: 2 },
    { prop: 'handoffRule', label: '升级/转交规则', kind: 'textarea', span: 2 },
    { prop: 'tags', label: '标签', kind: 'checkbox', span: 2, options: ['新客', '高价值客户', '财务', '技术', '风险', '售后'] },
  ],
  supportPerformance: [
    { prop: 'period', label: '考核周期', kind: 'select', options: ['今日', '本周', '本月', '季度'] },
    { prop: 'reviewItems', label: '复盘项', kind: 'checkbox', span: 2, options: ['响应时长', '一次解决率', '满意度', '升级率', '回访完成'] },
    { prop: 'reviewNote', label: '复盘记录', kind: 'textarea', span: 2 },
  ],
  manuals: [
    { prop: 'frontTemplate', label: '前端模板', kind: 'select', options: ['帮助中心文章', '步骤指南', '政策说明'] },
    { prop: 'category', label: '手册分类', kind: 'select' },
    { prop: 'scope', label: '适用端', kind: 'select' },
    { prop: 'frontAudience', label: '前端可见对象', kind: 'select', options: ['企业侧', '运营侧', '全部用户', '企业管理员', '企业成员', '运营人员'] },
    { prop: 'status', label: '状态', kind: 'select' },
    { prop: 'updatedAt', label: '更新时间', kind: 'date' },
    { prop: 'summary', label: '内容摘要', kind: 'textarea', span: 2 },
    { prop: 'content', label: '正文内容', kind: 'textarea', span: 2 },
    { prop: 'manualFile', label: '手册附件/封面图', kind: 'upload', span: 2 },
  ],
  announcements: [
    { prop: 'frontTemplate', label: '前端模板', kind: 'select', options: ['更新公告', '政策说明', '强制确认弹窗', '新闻资讯卡片'] },
    { prop: 'type', label: '类型', kind: 'select' },
    { prop: 'publishAt', label: '发布时间', kind: 'date' },
    { prop: 'readMode', label: '阅读要求', kind: 'radio' },
    { prop: 'frontAudience', label: '前端可见对象', kind: 'select', options: ['全部用户', '企业管理员', '企业成员', '运营人员'] },
    { prop: 'status', label: '状态', kind: 'select' },
    { prop: 'publishTargets', label: '发布对象', kind: 'checkbox', span: 2, options: ['全部租户', '试用企业', '付费企业', '企业管理员', '运营人员'] },
    { prop: 'summary', label: '摘要', kind: 'textarea', span: 2 },
    { prop: 'content', label: '公告正文', kind: 'textarea', span: 2 },
  ],
  globalSettings: [
    { prop: 'configKey', label: '参数编码', kind: 'text', placeholder: 'platform.warning.example' },
    { prop: 'group', label: '配置分组', kind: 'select', options: ['预警', '计费', '灰度', '安全', '内容', '客服'] },
    { prop: 'valueType', label: '值类型', kind: 'select', options: ['text', 'number', 'percent', 'days', 'hours', 'currency', 'boolean'] },
    { prop: 'scope', label: '生效范围', kind: 'select', options: ['全平台', '指定租户', '新入驻租户', '付费租户'] },
    { prop: 'effectiveAt', label: '生效时间', kind: 'date' },
    { prop: 'status', label: '状态', kind: 'select', options: ['待生效', '启用', '已停用'] },
    { prop: 'changeReason', label: '变更原因', kind: 'textarea', span: 2 },
    { prop: 'description', label: '配置说明', kind: 'textarea', span: 2 },
  ],
};

async function load() {
  if (!moduleKey.value) return;
  loading.value = true;
  try {
    config.value = await http.get<ModuleConfig>(`/module-pages/${moduleKey.value}`);
    if (isWarnings.value) {
      Object.assign(warningSettings, config.value.warningSettings || (await http.get<Record<string, any>>(`/module-pages/${moduleKey.value}/settings`)));
    }
    selectedFilters.value = config.value.filters.map(() => '');
    visibleColumns.value = config.value.columns.map((column) => column.prop);
    applyRouteQuery();
    resetCreateForm();
  } finally {
    loading.value = false;
  }
}

function findFilterIndex(propOrLabel: string) {
  return filterOptions.value.findIndex((filter) => {
    const column = config.value.columns.find((item) => item.prop === filter.prop);
    return filter.prop === propOrLabel || column?.label === propOrLabel || filter.label === propOrLabel;
  });
}

function applyFilter(propOrLabel: string, value?: string) {
  if (!value) return;
  const index = findFilterIndex(propOrLabel);
  if (index < 0) {
    keyword.value = [keyword.value, value].filter(Boolean).join(' ');
    return;
  }
  const prop = filterOptions.value[index]?.prop;
  if (prop) selectedFilters.value[index] = `${prop}::${value}`;
}

function applyRouteQuery() {
  keyword.value = String(route.query.keyword || '');
  selectedFilters.value = config.value.filters.map(() => '');
  applyFilter('status', String(route.query.status || ''));
  applyFilter('状态', String(route.query.status || ''));
  applyFilter(String(route.query.filterProp || ''), String(route.query.filterValue || ''));
  currentPage.value = 1;
}

function metricTarget(metric: { label: string; value: string; hint: string }) {
  const label = metric.label;
  if (moduleKey.value === 'tenantAudits') {
    if (label.includes('真实企业')) return { path: '/tenants' };
    if (label.includes('已开通')) return { path: '/tenants', query: { status: '1' } };
    if (label.includes('复核')) return { path: '/tenant-audits', query: { status: '人工复核' } };
    if (label.includes('异常')) return { path: '/users', query: { keyword: 'owner' } };
  }
  if (moduleKey.value === 'warnings') {
    if (label.includes('待') || label.includes('高')) return { path: '/warnings', query: { status: '待处理' } };
    return { path: '/warnings' };
  }
  if (moduleKey.value === 'reports') return { path: '/reports' };
  if (moduleKey.value === 'tickets' || moduleKey.value === 'supportPerformance') return { path: '/tickets' };
  if (moduleKey.value === 'ordersInvoices') return { path: '/orders-invoices' };
  if (moduleKey.value === 'orderOperationLogs') return { path: '/order-operation-logs' };
  if (moduleKey.value === 'announcements') return { path: '/announcements' };
  if (moduleKey.value === 'manuals') return { path: '/manuals' };
  return null;
}

function openMetric(metric: { label: string; value: string; hint: string }) {
  const target = metricTarget(metric);
  if (!target) return;
  router.push(target);
}

async function openWarningSettings() {
  const settings = await http.get<Record<string, any>>(`/module-pages/${moduleKey.value}/settings`);
  Object.keys(warningSettings).forEach((key) => delete warningSettings[key]);
  Object.assign(warningSettings, settings);
  warningSettingsDialog.value = true;
}

async function saveWarningSettings() {
  await http.put(`/module-pages/${moduleKey.value}/settings`, { ...warningSettings });
  ElMessage.success('预警参数与提醒策略已保存');
  warningSettingsDialog.value = false;
  await load();
}

async function openReportRules() {
  const rules = await http.get<ReportRule[]>('/module-pages/reports/rules');
  reportRules.value = rules.map(normalizeReportRule);
  reportRulesDialog.value = true;
}

function addReportRule() {
  const template = reportRuleTemplateOptions.find((option) => !reportRules.value.some((rule) => rule.code === option.code)) || reportRuleTemplateOptions[0];
  reportRules.value.push({
    code: template.code,
    name: template.name,
    scope: '',
    formula: template.formula,
    formulaTemplate: template.formula,
    dataSourceKeys: [...template.sources],
    owner: '运营负责人',
    editable: '可调整',
    notes: '',
    status: 1,
    sortOrder: (reportRules.value.length + 1) * 10,
  });
}

function normalizeReportRule(rule: ReportRule): ReportRule {
  const template = reportRuleTemplateOptions.find((option) => option.code === rule.code || option.name === rule.name);
  return {
    ...rule,
    code: template?.code || rule.code,
    name: template?.name || rule.name,
    formula: toBusinessFormula(rule.formula || template?.formula || ''),
    formulaTemplate: template?.formula || toBusinessFormula(rule.formula || ''),
    dataSourceKeys: inferReportDataSources(rule, template),
  };
}

function inferReportDataSources(rule: ReportRule, template?: (typeof reportRuleTemplateOptions)[number]) {
  const haystack = `${rule.scope || ''} ${rule.formula || ''} ${rule.notes || ''}`;
  const matched = reportDataSourceOptions.filter((option) => haystack.includes(option.value) || haystack.includes(option.label)).map((option) => option.value);
  return matched.length ? matched : [...(template?.sources || [])];
}

function toBusinessFormula(value: string) {
  const labels = new Map(reportDataSourceOptions.map((option) => [option.value, option.label]));
  return Array.from(labels.entries()).reduce((text, [key, label]) => text.split(key).join(label), value);
}

function reportSourceLabels(keys: string[] = []) {
  const labels = new Map(reportDataSourceOptions.map((option) => [option.value, option.label]));
  return keys.map((key) => labels.get(key) || key).join('、');
}

function applyReportRuleTemplate(rule: ReportRule) {
  const template = reportRuleTemplateOptions.find((option) => option.code === rule.code);
  if (!template) return;
  rule.name = template.name;
  rule.formula = template.formula;
  rule.formulaTemplate = template.formula;
  rule.dataSourceKeys = [...template.sources];
}

function normalizeReportRulesPayload() {
  return reportRules.value.map((rule) => ({
    ...rule,
    formula: rule.formulaTemplate || rule.formula,
    notes: [String(rule.notes || '').replace(/\n?业务数据源：.*$/s, ''), rule.dataSourceKeys?.length ? `业务数据源：${reportSourceLabels(rule.dataSourceKeys)}` : ''].filter(Boolean).join('\n'),
  }));
}

async function removeReportRule(index: number) {
  await ElMessageBox.confirm('删除后该规则会停用，不再参与报表口径展示，是否继续？', '删除规则确认', {
    type: 'warning',
    confirmButtonText: '继续',
    cancelButtonText: '取消',
  });
  reportRules.value.splice(index, 1);
}

async function saveReportRules() {
  await http.put('/module-pages/reports/rules', normalizeReportRulesPayload());
  ElMessage.success('报表规则已保存');
  reportRulesDialog.value = false;
  await load();
}

function resetCreateForm() {
  Object.keys(createForm).forEach((key) => delete createForm[key]);
  createFields.value.forEach((field) => {
    createForm[field.prop] = field.kind === 'checkbox' || field.kind === 'upload' || field.multiple ? [] : '';
  });
}

function resetQuery() {
  keyword.value = '';
  selectedFilters.value = config.value.filters.map(() => '');
  currentPage.value = 1;
}

function fieldLabel(key: string) {
  const labels: Record<string, string> = {
    id: '业务编号',
    phone: '联系电话',
    submittedAt: '提交时间',
    creditCode: '统一社会信用代码',
    legalPerson: '法人姓名',
    agentName: '经办人',
    contactEmail: '联系邮箱',
    ownerUsername: 'Owner 登录名',
    ownerEmail: 'Owner 登录邮箱',
    ownerPassword: 'Owner 初始密码',
    industry: '所属行业',
    companyScale: '企业规模',
    trialPlan: '拟开通套餐',
    businessScope: '经营范围',
    handledAt: '处理时间',
    licenseFile: '营业执照图片',
    identityFile: '身份核验材料',
    authorizationFile: '经办人授权书',
    riskFactors: '风险项',
    reviewConclusion: '审核结论',
    paymentProof: '支付凭证',
    invoiceType: '发票类型',
    taxNo: '纳税人识别号',
    refundReason: '退款/异常说明',
    attachments: '附件',
    publishTargets: '发布对象',
    forceRead: '阅读要求',
    tenantId: '企业 ID',
    userId: '成员 ID',
    policyCode: '权限编码',
    feature: '功能权限',
    permissionScope: '数据范围',
    permissionLevel: '授权结果',
    providerName: '供应商名称',
    protocol: '接入协议',
    capability: '模型能力',
    modelsJson: '可用模型清单',
    baseUrl: '接口地址',
    apiKey: 'API Key',
    secretKey: 'Secret Key',
  };
  return labels[key] || key;
}

function uniqueOptions(values: string[]) {
  return Array.from(new Set(values.map((value) => String(value || '').trim()).filter(Boolean)));
}

function columnValues(prop: string) {
  return uniqueOptions(config.value.rows.map((row) => businessDisplay(row[prop])));
}

function businessDisplay(value: unknown) {
  if (value === undefined || value === null) return '';
  return String(value)
    .split('tenant_members').join('成员关系')
    .split('tenant_balances').join('企业余额')
    .split('tenant_quotas').join('套餐配额')
    .split('billing_records').join('资源流水')
    .split('content_articles').join('内容文章')
    .split('customer_scripts').join('客服话术')
    .split('customer_tickets').join('客服工单')
    .split('warning_rules').join('预警规则')
    .split('admin_audit_logs').join('操作审计日志')
    .split('system_settings').join('系统配置')
    .split('admin_report_definitions').join('报表目录')
    .split('admin_report_rules').join('报表规则')
    .split('ai_tasks').join('AI 任务')
    .split('projects').join('短剧项目')
    .split('tenants').join('企业租户')
    .split('users').join('组织员工')
    .split('plans').join('套餐配置')
    .split('tenant_model_providers').join('模型供应商')
    .split('tenant_model_defaults').join('默认模型配置')
    .split('tenant_member_permissions').join('成员权限配置')
    .split('permission_policies').join('权限项')
    .split('volcengine').join('火山引擎')
    .split('dashscope').join('通义千问')
    .split('MODEL_CONFIG').join('模型配置')
    .split('USAGE_VIEW').join('用量统计')
    .split('TEAM_MANAGE').join('团队管理');
}

function cellValue(row: Record<string, string>, column: Column) {
  return businessDisplay(row[column.prop]);
}

function resolveFieldOptions(field: FormField): FormField {
  if (!['select', 'radio', 'checkbox'].includes(field.kind)) return field;
  const dbOptions = columnValues(field.prop);
  const options = uniqueOptions([...dbOptions, ...(field.options || [])]);
  return { ...field, options };
}

function optionsFor(column: Column) {
  const rowOptions = columnValues(column.prop);
  const label = `${column.prop}${column.label}`;
  if (/status|状态/.test(label)) return uniqueOptions([...(moduleStatusOptions[moduleKey.value] || []), ...rowOptions]);
  if (/licenseStatus|营业执照/.test(label)) return uniqueOptions(['已验真', '待补件', '证照不一致', '已驳回', ...rowOptions]);
  if (/identityStatus|身份/.test(label)) return uniqueOptions(['人证一致', '人脸通过', '身份证缺失', '姓名不一致', '待核验', ...rowOptions]);
  if (/trialPlan|plan|package|套餐|资源包/.test(label)) return uniqueOptions(['免费版', '专业版', '团队版', '企业版', '专业版试用', '团队版试用', '积分加油包', ...rowOptions]);
  if (/priority|优先级/.test(label)) return uniqueOptions(['普通', '加急', '紧急', ...rowOptions]);
  if (/risk|风险|结论/.test(label)) return uniqueOptions(['低风险', '需人工确认', '高风险', '准入通过', '补充资料', '拒绝入驻', ...rowOptions]);
  if (/role|角色/.test(label)) return uniqueOptions(['超级管理员', '运营专员', '财务', '客服', '只读', ...rowOptions]);
  if (/type|category|scope|readMode|group|类型|分类|范围|对象|渠道|来源|负责人|归属/.test(label)) {
    return uniqueOptions([...rowOptions, '全部', '运营', '财务', '客服', '技术', '内容']);
  }
  return rowOptions;
}

function columnToField(column: Column): FormField {
  const label = `${column.prop}${column.label}`;
  if (/file|image|proof|attachment|license|identity|营业执照|图片|附件|截图|凭证|身份证/.test(label)) {
    return { prop: column.prop, label: column.label, kind: 'upload', span: 2 };
  }
  if (/amount|price|count|credits|handled|total|used|limit|member|project|concurrency|金额|价格|积分|次数|数量|成员|项目|并发/.test(label)) {
    return { prop: column.prop, label: column.label, kind: 'number' };
  }
  if (/date|time|At|时间|日期|周期/.test(label)) {
    return { prop: column.prop, label: column.label, kind: 'date' };
  }
  if (/content|desc|description|reason|businessScope|items|note|remark|经营范围|说明|原因|内容|备注|口径/.test(label)) {
    return { prop: column.prop, label: column.label, kind: 'textarea', span: 2 };
  }
  if (/risk|风险|结论|priority|优先级/.test(label)) {
    return { prop: column.prop, label: column.label, kind: 'radio', options: optionsFor(column) };
  }
  const options = optionsFor(column);
  if (column.tag || options.length > 0 || /status|状态|类型|分类|套餐|角色|负责人|渠道|来源/.test(label)) {
    return { prop: column.prop, label: column.label, kind: 'select', options };
  }
  return { prop: column.prop, label: column.label, kind: 'text' };
}

function normalizeCreateForm() {
  return Object.fromEntries(
    createFields.value.map((field) => {
      const value = createForm[field.prop];
      if (Array.isArray(value)) {
        const formatted = value
          .map((item) => {
            if (typeof item === 'string') return item;
            return item?.response?.path || item?.response?.name || item?.name || item?.url || '';
          })
          .filter(Boolean)
          .join('、');
        return [field.prop, formatted];
      }
      return [field.prop, value ?? ''];
    }),
  );
}

function contentTemplate(row?: Record<string, string> | null) {
  if (!row) return '帮助中心文章';
  if (row.frontTemplate) return row.frontTemplate;
  if (moduleKey.value === 'announcements') {
    if (row.readMode === '强制确认') return '强制确认弹窗';
    return row.type === '新闻' ? '新闻资讯卡片' : '政策说明';
  }
  return row.category?.includes('入门') || row.category?.includes('流程') ? '步骤指南' : '帮助中心文章';
}

function contentPreviewMeta(row?: Record<string, string> | null) {
  if (!row) return '';
  return [
    row.category || row.type,
    row.scope || row.frontAudience,
    row.version,
    row.publishAt || row.updatedAt,
  ].filter(Boolean).join(' / ');
}

function contentBody(row?: Record<string, string> | null) {
  if (!row) return '';
  return row.content || row.summary || row.replyTemplate || '暂无正文内容';
}

function previewContent(row: Record<string, string>) {
  previewRow.value = row;
}

async function uploadBusinessFile(option: any) {
  const formData = new FormData();
  formData.append('file', option.file);
  try {
    const result = await http.post('/module-pages/files', formData);
    option.onSuccess?.(result);
  } catch (error) {
    option.onError?.(error);
  }
}

async function createRow() {
  if (editingRow.value) {
    const index = config.value.rows.indexOf(editingRow.value);
    if (index < 0) return;
    await http.put(`/module-pages/${moduleKey.value}/rows/${index}`, normalizeCreateForm());
    ElMessage.success('记录已更新');
  } else {
    await http.post(`/module-pages/${moduleKey.value}/rows`, normalizeCreateForm());
    ElMessage.success('记录已写入数据库');
  }
  createDialog.value = false;
  editingRow.value = null;
  await load();
}

function openCreateRow() {
  editingRow.value = null;
  resetCreateForm();
  createDialog.value = true;
}

function openEditRow(row: Record<string, string>) {
  editingRow.value = row;
  resetCreateForm();
  createFields.value.forEach((field) => {
    if (field.kind === 'checkbox') {
      createForm[field.prop] = String(row[field.prop] || '').split('、').filter(Boolean);
    } else if (field.kind === 'upload') {
      createForm[field.prop] = [];
    } else {
      createForm[field.prop] = row[field.prop] || '';
    }
  });
  createDialog.value = true;
}

async function deleteRow(row: Record<string, string>) {
  const index = config.value.rows.indexOf(row);
  if (index < 0) return;
  await ElMessageBox.confirm('删除后该记录将从当前业务模块数据库中移除，是否继续？', '删除确认', {
    type: 'warning',
    confirmButtonText: '继续',
    cancelButtonText: '取消',
  });
  await ElMessageBox.prompt('请输入“确认删除”完成二次确认。', '二次确认', {
    type: 'warning',
    inputPattern: /^确认删除$/,
    inputErrorMessage: '请输入：确认删除',
    confirmButtonText: '确认删除',
    cancelButtonText: '取消',
  });
  await http.delete(`/module-pages/${moduleKey.value}/rows/${index}`);
  ElMessage.success('记录已删除');
  await load();
}

async function processRow(row: Record<string, string>) {
  const index = config.value.rows.indexOf(row);
  if (index < 0) return;
  await http.post(`/module-pages/${moduleKey.value}/rows/${index}/process`);
  ElMessage.success('处理结果已回写数据库');
  await load();
}

function exportRows() {
  const headers = tableColumns.value.map((column) => column.label);
  const props = tableColumns.value.map((column) => column.prop);
  const lines = [headers, ...filteredRows.value.map((row) => props.map((prop) => row[prop] || ''))];
  const csv = lines
    .map((line) => line.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(','))
    .join('\n');
  const blob = new Blob([`\uFEFF${csv}`], { type: 'text/csv;charset=utf-8;' });
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = `${config.value.title}-${new Date().toISOString().slice(0, 10)}.csv`;
  link.click();
  URL.revokeObjectURL(link.href);
  ElMessage.success('已导出当前筛选结果');
}

onMounted(load);
watch(moduleKey, load);
watch(() => route.query, applyRouteQuery);
watch([keyword, selectedFilters], () => {
  currentPage.value = 1;
}, { deep: true });
watch(filteredRows, () => {
  const maxPage = Math.max(1, Math.ceil(filteredRows.value.length / pageSize.value));
  if (currentPage.value > maxPage) {
    currentPage.value = maxPage;
  }
});
</script>

<template>
  <div v-loading="loading">
    <div class="page-head">
      <div>
        <h1>{{ config.title }}</h1>
        <p>{{ businessDisplay(config.desc) }}</p>
      </div>
      <div class="page-actions">
        <el-button :icon="Refresh" @click="load">刷新</el-button>
        <el-button v-if="isWarnings" :icon="Setting" @click="openWarningSettings">预警参数</el-button>
        <el-button v-if="isReports" :icon="Setting" @click="openReportRules">规则维护</el-button>
        <el-button v-if="canCreate" type="primary" :icon="Plus" @click="openCreateRow">{{ config.primary || '新建' }}</el-button>
      </div>
    </div>

    <div v-if="config.workflow.length" class="workflow-strip">
      <span v-for="(step, index) in config.workflow" :key="step">
        <b>{{ index + 1 }}</b>{{ step }}
      </span>
    </div>

    <div v-if="config.metrics.length" class="metric-grid module-metrics">
      <div
        v-for="metric in config.metrics"
        :key="metric.label"
        class="metric"
      >
        <span>{{ metric.label }}</span>
        <button type="button" class="metric-action-number" :aria-label="`查看${metric.label}`" @click="openMetric(metric)">
          <strong>{{ metric.value }}</strong>
        </button>
        <small>{{ businessDisplay(metric.hint) }}</small>
      </div>
    </div>

    <div class="toolbar module-toolbar">
      <el-input v-model="keyword" :prefix-icon="Search" placeholder="搜索企业、订单号、工单号或关键词" clearable />
      <el-select v-for="(filter, index) in filterOptions" :key="filter.label" v-model="selectedFilters[index]" :placeholder="filter.label" clearable>
        <el-option label="全部" value="" />
        <el-option
          v-for="option in filter.options"
          :key="`${filter.prop}-${option}`"
          :label="option"
          :value="`${filter.prop}::${option}`"
        />
      </el-select>
      <el-button type="primary">查询</el-button>
      <el-button @click="resetQuery">重置</el-button>
    </div>

    <div v-if="config.logicRules?.length" class="panel logic-rules-panel">
      <div class="panel-title">
        <div>
          <b>计算口径与业务规则</b>
          <span>说明关键指标、算法口径、维护负责人和可调整范围</span>
        </div>
      </div>
      <div class="logic-rule-grid">
        <article v-for="rule in config.logicRules" :key="rule.name" class="logic-rule-card">
          <div>
            <strong>{{ rule.name }}</strong>
            <el-tag effect="plain">{{ rule.editable }}</el-tag>
          </div>
          <p>{{ rule.scope }}</p>
          <code>{{ rule.formula }}</code>
          <footer>
            <span>{{ rule.owner }}</span>
            <small>{{ rule.notes }}</small>
          </footer>
        </article>
      </div>
    </div>

    <div class="module-layout">
      <div class="panel module-table">
        <div class="panel-title">
          <div>
            <b>{{ config.title }}列表</b>
            <span>支持筛选、导出、刷新与行内操作</span>
          </div>
          <div class="panel-tools">
            <el-button :icon="Setting" @click="columnDialog = true">列设置</el-button>
            <el-button :icon="Download" @click="exportRows">导出</el-button>
          </div>
        </div>
        <el-table :data="pagedRows" row-key="id" class="business-data-table">
          <el-table-column v-for="column in tableColumns" :key="column.prop" v-bind="column">
            <template v-if="column.tag" #default="{ row }">
              <el-tag effect="plain">{{ cellValue(row, column) }}</el-tag>
            </template>
            <template v-else #default="{ row }">
              <span class="table-cell-text">{{ cellValue(row, column) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" :width="actionColumnWidth" align="left" class-name="action-cell">
            <template #default="{ row }">
              <div class="row-actions">
                <el-button text type="primary" @click="detailRow = row">详情</el-button>
                <el-button v-if="isContentTemplateModule" text type="primary" @click="previewContent(row)">预览</el-button>
                <el-button v-if="canEdit" text type="primary" @click="openEditRow(row)">编辑</el-button>
                <el-button v-if="canProcess" text type="primary" :disabled="['已处理', '已通过', '已归档', '已关闭', '已生成', '已解决', '已发布', '已复盘', '已生效'].includes(row.status)" @click="processRow(row)">处理</el-button>
                <el-button v-if="canDelete" text type="danger" @click="deleteRow(row)">删除</el-button>
              </div>
            </template>
          </el-table-column>
        </el-table>
        <el-pagination
          v-model:current-page="currentPage"
          v-model:page-size="pageSize"
          layout="total, sizes, prev, pager, next"
          :page-sizes="pageSizes"
          :total="filteredRows.length"
        />
      </div>

      <aside v-if="config.cards.length" class="module-side">
        <div v-for="card in config.cards" :key="card.title" class="module-card">
          <span>{{ card.title }}</span>
          <strong>{{ businessDisplay(card.value) }}</strong>
          <ul>
            <li v-for="item in card.items" :key="item">{{ businessDisplay(item) }}</li>
          </ul>
        </div>
      </aside>
    </div>

    <el-drawer v-model="detailRow" title="记录详情" size="42%">
      <div v-if="detailRow" class="detail-list">
        <label v-for="entry in detailEntries" :key="entry.key">
          <span>{{ entry.label }}</span>
          <strong>{{ entry.value }}</strong>
        </label>
      </div>
    </el-drawer>

    <el-dialog v-model="previewRow" title="前端预览" width="860px" class="content-preview-dialog">
      <div v-if="previewRow" class="content-preview-shell" :class="`template-${contentTemplate(previewRow)}`">
        <aside class="content-preview-sidebar">
          <span>{{ moduleKey === 'manuals' ? '帮助中心' : '平台公告' }}</span>
          <strong>Dramix</strong>
          <button>{{ contentTemplate(previewRow) }}</button>
          <small>{{ previewRow.status || '草稿' }}</small>
        </aside>
        <article class="content-preview-article">
          <header>
            <el-tag effect="plain">{{ contentTemplate(previewRow) }}</el-tag>
            <h1>{{ previewRow.title }}</h1>
            <p>{{ previewRow.summary || config.desc }}</p>
            <div>
              <span>{{ contentPreviewMeta(previewRow) }}</span>
              <span>{{ previewRow.owner || previewRow.updatedBy || '内容运营' }}</span>
            </div>
          </header>
          <section class="content-preview-body">
            <p v-for="paragraph in contentBody(previewRow).split(/[。；;]\\s*/).filter(Boolean)" :key="paragraph">
              {{ paragraph }}{{ /[。；;]$/.test(paragraph) ? '' : '。' }}
            </p>
          </section>
          <footer v-if="moduleKey === 'announcements'" class="content-preview-actions">
            <el-button v-if="previewRow.readMode === '强制确认'" type="primary">我已阅读并确认</el-button>
            <el-button v-else type="primary" plain>知道了</el-button>
          </footer>
        </article>
      </div>
    </el-dialog>

    <el-dialog v-model="createDialog" :title="editingRow ? '编辑业务记录' : (config.primary || '新建记录')" width="760px" class="business-dialog">
      <el-form label-position="top" class="business-form-grid">
        <el-form-item v-for="field in createFields" :key="field.prop" :label="field.label" :class="{ 'span-2': field.span === 2 }">
          <el-input v-if="field.kind === 'text'" v-model="createForm[field.prop]" :placeholder="field.placeholder || `请输入${field.label}`" clearable />
          <el-input
            v-else-if="field.kind === 'textarea'"
            v-model="createForm[field.prop]"
            type="textarea"
            :rows="3"
            :placeholder="field.placeholder || `请填写${field.label}`"
          />
          <el-input-number v-else-if="field.kind === 'number'" v-model="createForm[field.prop]" :min="0" controls-position="right" />
          <el-select
            v-else-if="field.kind === 'select'"
            v-model="createForm[field.prop]"
            :multiple="field.multiple"
            :placeholder="field.placeholder || `请选择${field.label}`"
            clearable
            filterable
          >
            <el-option v-for="option in field.options || []" :key="option" :label="option" :value="option" />
          </el-select>
          <el-radio-group v-else-if="field.kind === 'radio'" v-model="createForm[field.prop]" class="business-radio-group">
            <el-radio-button v-for="option in field.options || []" :key="option" :label="option" />
          </el-radio-group>
          <el-checkbox-group v-else-if="field.kind === 'checkbox'" v-model="createForm[field.prop]" class="business-check-group">
            <el-checkbox v-for="option in field.options || []" :key="option" :label="option" border />
          </el-checkbox-group>
          <el-date-picker
            v-else-if="field.kind === 'date'"
            v-model="createForm[field.prop]"
            type="datetime"
            value-format="YYYY-MM-DD HH:mm:ss"
            :placeholder="field.placeholder || `请选择${field.label}`"
          />
          <el-upload
            v-else-if="field.kind === 'upload'"
            v-model:file-list="createForm[field.prop]"
            class="business-upload"
            drag
            multiple
            action="#"
            :http-request="uploadBusinessFile"
          >
            <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
            <div class="el-upload__text">点击或拖拽上传{{ field.label }}</div>
            <template #tip>
              <div class="el-upload__tip">支持图片、PDF、Word，上传后随业务记录进入审核流程</div>
            </template>
          </el-upload>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="createDialog = false">取消</el-button>
        <el-button type="primary" @click="createRow">{{ editingRow ? '保存修改' : '提交业务记录' }}</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="columnDialog" title="列设置" width="420px">
      <el-checkbox-group v-model="visibleColumns" class="column-checks">
        <el-checkbox v-for="column in config.columns" :key="column.prop" :label="column.prop">{{ column.label }}</el-checkbox>
      </el-checkbox-group>
      <template #footer>
        <el-button @click="visibleColumns = config.columns.map((column) => column.prop)">恢复默认</el-button>
        <el-button type="primary" @click="columnDialog = false">完成</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="warningSettingsDialog" title="预警参数与提醒策略" width="820px" class="business-dialog">
      <el-form label-position="top" class="business-form-grid warning-settings-form">
        <el-form-item label="资源不足提醒阈值（%）">
          <el-input-number v-model="warningSettings.creditLowPercent" :min="1" :max="100" controls-position="right" />
        </el-form-item>
        <el-form-item label="资源严重阈值（%）">
          <el-input-number v-model="warningSettings.creditCriticalPercent" :min="1" :max="100" controls-position="right" />
        </el-form-item>
        <el-form-item label="沉睡客户天数">
          <el-input-number v-model="warningSettings.dormantDays" :min="1" :max="365" controls-position="right" />
        </el-form-item>
        <el-form-item label="套餐到期提前提醒天数">
          <el-input-number v-model="warningSettings.expireSoonDays" :min="1" :max="90" controls-position="right" />
        </el-form-item>
        <el-form-item label="任务失败率阈值（%）">
          <el-input-number v-model="warningSettings.taskFailRatePercent" :min="1" :max="100" controls-position="right" />
        </el-form-item>
        <el-form-item label="工单 SLA 超时（小时）">
          <el-input-number v-model="warningSettings.ticketSlaHours" :min="1" :max="72" controls-position="right" />
        </el-form-item>
        <el-form-item label="发票待处理天数">
          <el-input-number v-model="warningSettings.invoicePendingDays" :min="1" :max="30" controls-position="right" />
        </el-form-item>
        <el-form-item label="入驻审核超时（小时）">
          <el-input-number v-model="warningSettings.auditPendingHours" :min="1" :max="24" controls-position="right" />
        </el-form-item>
        <el-form-item label="内容安全连续拦截次数">
          <el-input-number v-model="warningSettings.contentBlockTimes" :min="1" :max="20" controls-position="right" />
        </el-form-item>
        <el-form-item label="每日提醒时间" class="span-2">
          <el-time-picker
            v-model="warningSettings.remindTimes"
            is-range
            range-separator="至"
            start-placeholder="首次提醒"
            end-placeholder="二次提醒"
            value-format="HH:mm"
            format="HH:mm"
          />
        </el-form-item>
        <el-form-item label="通知渠道" class="span-2">
          <el-checkbox-group v-model="warningSettings.channels" class="business-check-group">
            <el-checkbox v-for="option in warningChannelOptions" :key="option" :label="option" border />
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="升级负责人" class="span-2">
          <el-select v-model="warningSettings.escalationOwners" multiple filterable placeholder="选择需要接收升级提醒的角色">
            <el-option v-for="option in warningOwnerOptions" :key="option" :label="option" :value="option" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="warningSettingsDialog = false">取消</el-button>
        <el-button type="primary" @click="saveWarningSettings">保存参数</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="reportRulesDialog" title="报表规则与算法口径维护" width="960px" class="business-dialog">
      <div class="report-rule-editor">
        <div v-for="(rule, index) in reportRules" :key="rule.code || index" class="report-rule-edit-card">
          <div class="report-rule-edit-head">
            <strong>{{ rule.name || `新规则 ${index + 1}` }}</strong>
            <el-button text type="danger" @click="removeReportRule(index)">删除</el-button>
          </div>
          <el-form label-position="top" class="business-form-grid">
            <el-form-item label="规则类型">
              <el-select v-model="rule.code" placeholder="请选择规则类型" @change="applyReportRuleTemplate(rule)">
                <el-option v-for="option in reportRuleTemplateOptions" :key="option.code" :label="option.name" :value="option.code" />
              </el-select>
            </el-form-item>
            <el-form-item label="规则名称">
              <el-select v-model="rule.name" placeholder="请选择规则名称">
                <el-option v-for="option in reportRuleTemplateOptions" :key="option.name" :label="option.name" :value="option.name" />
              </el-select>
            </el-form-item>
            <el-form-item label="统计口径" class="span-2">
              <el-input v-model="rule.scope" type="textarea" :rows="3" placeholder="说明数据范围、排除条件和业务边界，不需要填写数据库表名" />
            </el-form-item>
            <el-form-item label="业务数据源" class="span-2">
              <el-select v-model="rule.dataSourceKeys" multiple placeholder="请选择业务数据源">
                <el-option v-for="option in reportDataSourceOptions" :key="option.value" :label="option.label" :value="option.value" />
              </el-select>
            </el-form-item>
            <el-form-item label="计算公式" class="span-2">
              <el-select v-model="rule.formulaTemplate" placeholder="请选择计算公式模板">
                <el-option v-for="option in reportRuleTemplateOptions" :key="option.formula" :label="option.formula" :value="option.formula" />
              </el-select>
            </el-form-item>
            <el-form-item label="负责人">
              <el-select v-model="rule.owner" filterable placeholder="请选择负责人">
                <el-option v-for="option in reportOwnerOptions" :key="option" :label="option" :value="option" />
              </el-select>
            </el-form-item>
            <el-form-item label="可调整范围">
              <el-select v-model="rule.editable" placeholder="请选择可调整范围">
                <el-option v-for="option in reportEditableOptions" :key="option" :label="option" :value="option" />
              </el-select>
            </el-form-item>
            <el-form-item label="状态">
              <el-select v-model="rule.status">
                <el-option label="启用" :value="1" />
                <el-option label="停用" :value="0" />
              </el-select>
            </el-form-item>
            <el-form-item label="排序">
              <el-input-number v-model="rule.sortOrder" :min="0" :step="10" controls-position="right" />
            </el-form-item>
            <el-form-item label="维护说明" class="span-2">
              <el-input v-model="rule.notes" type="textarea" :rows="2" placeholder="说明使用场景、变更影响和重算建议" />
            </el-form-item>
          </el-form>
        </div>
        <div class="report-rule-edit-actions">
          <el-button :icon="Plus" @click="addReportRule">新增规则</el-button>
        </div>
      </div>
      <template #footer>
        <el-button @click="reportRulesDialog = false">取消</el-button>
        <el-button type="primary" @click="saveReportRules">保存规则</el-button>
      </template>
    </el-dialog>
  </div>
</template>
