<script setup lang="ts">
import { computed, onMounted, ref } from "vue";

type Profile = {
  id: string;
  username: string;
  display_name: string;
  status: string;
  roles: string[];
  permissions: string[];
  data_scope: string;
};
type Dashboard = {
  order_count: number;
  policy_count: number;
  premium_cents: number;
};
type Order = {
  order_id: string;
  order_no: string;
  user_id: string;
  product_name: string;
  plan_name: string;
  relationship: "SELF" | "PARENT" | "SPOUSE" | "CHILD";
  applicant: { name?: string; id_no?: string };
  insured: { name?: string; id_no?: string };
  contact: { mobile?: string };
  status: string;
  amount_cents: number;
  currency: string;
  payment: {
    payment_id: string;
    payment_no: string;
    provider: string;
    status: string;
    provider_transaction_no?: string | null;
    succeeded_at?: string | null;
  } | null;
  policy_no?: string | null;
  created_at: string;
};
type Policy = {
  policy_id: string;
  policy_no: string;
  order_id: string;
  order_no: string;
  product_name: string;
  plan_name: string;
  relationship: "SELF" | "PARENT" | "SPOUSE" | "CHILD";
  applicant: { name?: string; id_no?: string };
  insured: { name?: string; id_no?: string };
  status: string;
  premium_cents: number;
  currency: string;
  coverage_start: string;
  coverage_end: string;
  issued_at: string;
};
type ServiceRequestView = {
  request_id: string;
  request_no: string;
  user_id: string;
  policy_id: string;
  request_type: string;
  recognition_id: string;
  confirmed_fields: Record<string, string>;
  status: "SUBMITTED" | "ACCEPTED" | "REJECTED";
  submitted_at: string;
  reviewed_by?: string | null;
  review_note?: string | null;
  reviewed_at?: string | null;
  recognition?: {
    source_name: string;
    media_type: string;
    confidence: number;
    warnings: string[];
    material_available: boolean;
  } | null;
};
type PermissionItem = {
  code: string;
  name: string;
  group: string;
};
type RoleDefinition = {
  code: string;
  name: string;
  data_scope: string;
  status: string;
  permissions: string[];
  protected: boolean;
};
type DataScopeOption = {
  code: string;
  name: string;
};
type AdminAccount = {
  id: string;
  username: string;
  display_name: string;
  status: string;
  roles: string[];
  permissions: string[];
  data_scope: string;
};
type Customer = {
  id: string;
  mobile: string;
  mobile_masked: string;
  display_name: string;
  status: string;
  created_at: string;
};
type AgentMetric = {
  label: string;
  value: string | number;
  unit: string;
  trend?: string | null;
};
type AgentChartPoint = {
  label: string;
  value: number;
};
type AgentBlock = {
  type:
    | "metrics"
    | "business_list"
    | "product_recommendations"
    | "quote"
    | "chart";
  version: string;
  title: string;
  items?: Array<Record<string, unknown>>;
  total?: number;
  entity?: string;
  quote?: Record<string, unknown>;
  chart_type?: "line" | "bar";
  unit?: string;
  points?: AgentChartPoint[];
};
type AgentAction = {
  type:
    | "open_admin_orders"
    | "open_admin_policies"
    | "open_attribution"
    | "none";
  label: string;
  payload: Record<string, unknown>;
  confirmation_required: boolean;
};
type OperationMessage = {
  role: "user" | "assistant";
  text: string;
  cards?: AgentBlock[];
  actions?: AgentAction[];
  traceUrl?: string | null;
  tools?: string[];
};
type OperationThreadView = {
  id: string;
  persona: "operation";
  title: string;
  status: string;
  message_count: number;
  last_message?: {
    role: "USER" | "ASSISTANT";
    content: string;
    created_at: string;
  } | null;
  created_at: string;
  updated_at: string;
};
type StoredOperationMessage = {
  role: "USER" | "ASSISTANT";
  content: string;
  metadata?: {
    cards?: AgentBlock[];
    actions?: AgentAction[];
    trace_url?: string | null;
    invoked_tools?: string[];
  };
};
type AgentRunView = {
  id: string;
  thread_id: string;
  persona: string;
  status: string;
  trace_id?: string | null;
  trace_url?: string | null;
  input: { text?: string };
  error_code?: string | null;
  created_at: string;
  completed_at?: string | null;
};
type AgentReportBlock =
  | { kind: "heading"; level: number; text: string }
  | { kind: "paragraph"; text: string; emphasized: boolean }
  | { kind: "list"; ordered: boolean; items: string[] }
  | { kind: "table"; headers: string[]; rows: string[][] }
  | { kind: "divider" };
type ManagedPlan = {
  id?: string | null;
  code: string;
  name: string;
  summary: string;
  status: "ACTIVE" | "INACTIVE";
  premium_cents: number;
  coverage_amount_cents: number;
  min_age: number;
  max_age: number;
};
type ManagedProductVersion = {
  id: string;
  version_no: string;
  status: string;
  effective_from: string;
  effective_to?: string | null;
  rule_version: string;
  rate_version: string;
  terms_summary: string;
  plan_count: number;
  plans: ManagedPlan[];
};
type ProductChangeLog = {
  id: string;
  version_id?: string | null;
  action: string;
  actor_name: string;
  detail: Record<string, unknown>;
  created_at: string;
};
type ManagedProduct = {
  product_id: string;
  product_code: string;
  name: string;
  category: string;
  summary: string;
  status: "ACTIVE" | "INACTIVE";
  versions: ManagedProductVersion[];
  recent_logs: ProductChangeLog[];
};
type KnowledgeDocumentView = {
  document_id: string;
  title: string;
  document_type: "INSURANCE_TERMS" | "PRODUCT_GUIDE" | "SERVICE_RULES" | "FAQ";
  source_name: string;
  product_code?: string | null;
  status: "DRAFT" | "INDEXED" | "PUBLISHED" | "INACTIVE";
  version_no: number;
  chunk_count: number;
  created_by: string;
  created_at: string;
  updated_at: string;
  indexed_at?: string | null;
  published_at?: string | null;
  search_test_count: number;
  last_search_test_at?: string | null;
  last_search_test_passed?: boolean | null;
  excerpt: string;
};
type KnowledgeSearchHitView = {
  chunk_id: string;
  document_id: string;
  document_version: number;
  ordinal: number;
  title: string;
  content: string;
  document_type: string;
  source_name: string;
  product_code?: string | null;
  score: number;
};
type AttributionItem = {
  salesperson_id: string;
  admin_user_id: string;
  salesperson_code: string;
  salesperson_name: string;
  visit_count: number;
  lead_count: number;
  order_count: number;
  premium_cents: number;
  visit_conversion_rate: number;
  lead_conversion_rate: number;
  average_premium_cents: number;
};
type AttributionOverview = {
  salesperson_count: number;
  visit_count: number;
  lead_count: number;
  order_count: number;
  premium_cents: number;
  visit_conversion_rate: number;
  lead_conversion_rate: number;
  average_premium_cents: number;
  items: AttributionItem[];
};
type PromotionCodeView = {
  code: string;
  channel: string;
  status: string;
  salesperson_name: string;
  salesperson_code: string;
  created_at: string;
};
type ViewKey =
  | "overview"
  | "agent"
  | "orders"
  | "policies"
  | "services"
  | "products"
  | "knowledge"
  | "attribution"
  | "users"
  | "permissions";

const apiBase =
  import.meta.env.VITE_API_BASE_URL ?? "http://127.0.0.1:8000/api/v1";
const username = ref("");
const password = ref("");
const showPassword = ref(false);
const profile = ref<Profile | null>(null);
const token = ref("");
const error = ref("");
const loading = ref(false);
const restoring = ref(true);
const dashboard = ref<Dashboard | null>(null);
const orders = ref<Order[]>([]);
const policies = ref<Policy[]>([]);
const serviceRequests = ref<ServiceRequestView[]>([]);
const roles = ref<RoleDefinition[]>([]);
const permissionCatalog = ref<PermissionItem[]>([]);
const dataScopes = ref<DataScopeOption[]>([]);
const adminUsers = ref<AdminAccount[]>([]);
const customers = ref<Customer[]>([]);
const managedProducts = ref<ManagedProduct[]>([]);
const knowledgeDocuments = ref<KnowledgeDocumentView[]>([]);
const knowledgeTitle = ref("");
const knowledgeDocumentType =
  ref<KnowledgeDocumentView["document_type"]>("INSURANCE_TERMS");
const knowledgeProductCode = ref("");
const knowledgeUploadFile = ref<File | null>(null);
const knowledgeUploading = ref(false);
const knowledgeActionId = ref("");
const knowledgeFeedback = ref("");
const knowledgeTestDocumentId = ref("");
const knowledgeTestQuery = ref("");
const knowledgeTestResults = ref<KnowledgeSearchHitView[]>([]);
const knowledgeTestRunning = ref(false);
const knowledgeTestFeedback = ref("");
const attributionOverview = ref<AttributionOverview | null>(null);
const promotionCodes = ref<PromotionCodeView[]>([]);
const productSavingId = ref("");
const productEditorOpen = ref(false);
const productCreatorOpen = ref(false);
const productCreatorStep = ref<1 | 2 | 3>(1);
const createProductCode = ref("");
const createProductName = ref("");
const createProductCategory = ref<"MEDICAL" | "ACCIDENT">("MEDICAL");
const createProductSummary = ref("");
const createVersionNo = ref("1.0.0");
const createEffectiveFrom = ref("");
const createRuleVersion = ref("eligibility-v1");
const createRateVersion = ref("rate-v1");
const createTermsSummary = ref("");
const createProductPlans = ref<ManagedPlan[]>([]);
const productCreatorSaving = ref(false);
const productCreatorError = ref("");
const selectedProductId = ref("");
const productEditorTab = ref<"profile" | "versions" | "logs">("profile");
const productName = ref("");
const productCategory = ref<"MEDICAL" | "ACCIDENT">("MEDICAL");
const productSummary = ref("");
const productEditorSaving = ref(false);
const productEditorMessage = ref("");
const selectedProductVersionId = ref("");
const versionEffectiveFrom = ref("");
const versionEffectiveTo = ref("");
const versionRule = ref("");
const versionRate = ref("");
const versionTerms = ref("");
const versionPlans = ref<ManagedPlan[]>([]);
const creatingVersion = ref(false);
const newVersionNo = ref("");
const newVersionEffectiveFrom = ref("");
const newVersionSourceId = ref("");
const activeView = ref<ViewKey>("overview");
const selectedOrder = ref<Order | null>(null);
const selectedPolicy = ref<Policy | null>(null);
const selectedServiceRequest = ref<ServiceRequestView | null>(null);
const serviceStatusFilter = ref("ALL");
const serviceSearch = ref("");
const serviceReviewDecision =
  ref<"ACCEPTED" | "REJECTED">("ACCEPTED");
const serviceReviewLoading = ref(false);
const serviceReviewFeedback = ref("");
const serviceMaterialPreview = ref("");
const serviceMaterialLoading = ref(false);
const serviceMaterialError = ref("");
const selectedRoleCode = ref("");
const editablePermissions = ref<string[]>([]);
const editableDataScope = ref("");
const roleSaving = ref(false);
const roleMessage = ref("");
const userSection = ref<"admins" | "customers">("admins");
const accountEditorOpen = ref(false);
const editingAdmin = ref<AdminAccount | null>(null);
const accountUsername = ref("");
const accountDisplayName = ref("");
const accountPassword = ref("");
const accountRoleCode = ref("");
const accountStatus = ref("ACTIVE");
const accountSaving = ref(false);
const accountError = ref("");
const passwordResetValue = ref("");
const passwordResetMessage = ref("");
const customerToToggle = ref<Customer | null>(null);
const customerSaving = ref(false);
const operationThreadId = ref("");
const operationThreads = ref<OperationThreadView[]>([]);
const operationInput = ref("请分析当前订单、有效保单和累计保费情况。");
const operationMessages = ref<OperationMessage[]>([]);
const operationLoading = ref(false);
const operationHistoryLoading = ref(false);
const agentRuns = ref<AgentRunView[]>([]);
const agentWorkspaceTab = ref<"analysis" | "runs">("analysis");

const currentViewLabel = computed(
  () =>
    ({
      overview: "总览台",
      agent: "运营智能体",
      orders: "订单中心",
      policies: "保单中心",
      services: "服务申请中心",
      products: "产品管理",
      knowledge: "知识中心",
      attribution: "推广归因",
      users: "用户维护",
      permissions: "角色权限",
    })[activeView.value],
);
const issuedRate = computed(() => {
  const orderCount = dashboard.value?.order_count ?? 0;
  if (!orderCount) return 0;
  return Math.min(100, Math.round(((dashboard.value?.policy_count ?? 0) / orderCount) * 100));
});
const averagePremium = computed(() => {
  const policyCount = dashboard.value?.policy_count ?? 0;
  return policyCount ? Math.round((dashboard.value?.premium_cents ?? 0) / policyCount) : 0;
});
const orderStatusSummary = computed(() => {
  const counts = new Map<string, number>();
  orders.value.forEach((item) => counts.set(item.status, (counts.get(item.status) ?? 0) + 1));
  return Array.from(counts, ([status, count]) => ({ status, count }));
});
const completedAgentRuns = computed(
  () => agentRuns.value.filter((run) => run.status === "COMPLETED").length,
);
const publishedKnowledgeCount = computed(
  () => knowledgeDocuments.value.filter((item) => item.status === "PUBLISHED").length,
);
const indexedKnowledgeCount = computed(
  () => knowledgeDocuments.value.filter((item) => item.status === "INDEXED").length,
);
const draftKnowledgeCount = computed(
  () => knowledgeDocuments.value.filter((item) => item.status === "DRAFT").length,
);
const pendingServiceCount = computed(
  () => serviceRequests.value.filter((item) => item.status === "SUBMITTED").length,
);
const acceptedServiceCount = computed(
  () => serviceRequests.value.filter((item) => item.status === "ACCEPTED").length,
);
const rejectedServiceCount = computed(
  () => serviceRequests.value.filter((item) => item.status === "REJECTED").length,
);
const filteredServiceRequests = computed(() => {
  const keyword = serviceSearch.value.trim().toLowerCase();
  return serviceRequests.value.filter((item) => {
    if (
      serviceStatusFilter.value !== "ALL" &&
      item.status !== serviceStatusFilter.value
    ) {
      return false;
    }
    if (!keyword) return true;
    const policy = policyForService(item);
    const order = orderForService(item);
    const customer = customerForService(item);
    return [
      item.request_no,
      policy?.policy_no,
      order?.product_name,
      order?.insured.name,
      customer?.mobile,
      item.confirmed_fields.hospital_name,
    ].some((value) => String(value ?? "").toLowerCase().includes(keyword));
  });
});
const knowledgeTestDocument = computed(
  () =>
    knowledgeDocuments.value.find(
      (item) => item.document_id === knowledgeTestDocumentId.value,
    ) ?? null,
);
const tracedAgentRuns = computed(
  () => agentRuns.value.filter((run) => Boolean(run.trace_id)).length,
);
const selectedRole = computed(
  () => roles.value.find((role) => role.code === selectedRoleCode.value) ?? null,
);
const selectedProduct = computed(
  () =>
    managedProducts.value.find(
      (product) => product.product_id === selectedProductId.value,
    ) ?? null,
);
const selectedProductVersion = computed(
  () =>
    selectedProduct.value?.versions.find(
      (version) => version.id === selectedProductVersionId.value,
    ) ?? null,
);
const productCreatorStepValid = computed(() => {
  if (productCreatorStep.value === 1) {
    return Boolean(
      /^[A-Za-z][A-Za-z0-9-]{2,31}$/.test(createProductCode.value.trim()) &&
        createProductName.value.trim() &&
        createProductSummary.value.trim(),
    );
  }
  if (productCreatorStep.value === 2) {
    return Boolean(
      createVersionNo.value.trim() &&
        createEffectiveFrom.value &&
        createRuleVersion.value.trim() &&
        createRateVersion.value.trim() &&
        createTermsSummary.value.trim(),
    );
  }
  return (
    createProductPlans.value.length > 0 &&
    createProductPlans.value.every(
      (plan) =>
        plan.code.trim() &&
        plan.name.trim() &&
        plan.summary.trim() &&
        plan.premium_cents > 0 &&
        plan.coverage_amount_cents > 0 &&
        plan.min_age >= 0 &&
        plan.max_age >= plan.min_age,
    )
  );
});
const roleDirty = computed(() => {
  if (!selectedRole.value) return false;
  const saved = [...selectedRole.value.permissions].sort().join("|");
  const editing = [...editablePermissions.value].sort().join("|");
  return saved !== editing || selectedRole.value.data_scope !== editableDataScope.value;
});
const canManageRoles = computed(() => hasPermission("role:write"));
const currentDate = new Intl.DateTimeFormat("zh-CN", {
  year: "numeric",
  month: "long",
  day: "numeric",
  weekday: "long",
}).format(new Date());

function formatMoney(cents: number) {
  return new Intl.NumberFormat("zh-CN", {
    style: "currency",
    currency: "CNY",
    minimumFractionDigits: 2,
  }).format(cents / 100);
}

function promotionChannelLabel(channel: string) {
  return (
    {
      GROUND_PROMOTION: "社区地推",
      COMMUNITY: "社区服务点",
      ENTERPRISE: "企业团体",
      PARTNER: "合作渠道",
      ONLINE_REFERRAL: "线上转介绍",
      STORE: "直营网点",
    }[channel] ?? channel
  );
}

function formatDate(value?: string | null, includeTime = false) {
  if (!value) return "—";
  return new Intl.DateTimeFormat("zh-CN", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    ...(includeTime ? { hour: "2-digit", minute: "2-digit" } : {}),
  }).format(new Date(value));
}

function maskIdNo(value?: string) {
  if (!value) return "—";
  return value.length >= 8 ? `${value.slice(0, 6)}********${value.slice(-4)}` : value;
}

function maskMobile(value?: string) {
  if (!value) return "—";
  return value.length === 11 ? `${value.slice(0, 3)}****${value.slice(-4)}` : value;
}

function hasPermission(permission: string) {
  return Boolean(
    profile.value?.permissions.includes("*") ||
      profile.value?.permissions.includes(permission),
  );
}

function statusLabel(status: string) {
  return (
    {
      CREATED: "待支付",
      PENDING: "处理中",
      PENDING_PAYMENT: "待支付",
      PAID: "已支付",
      SUCCEEDED: "支付成功",
      COMPLETED: "已完成",
      RUNNING: "运行中",
      FAILED: "支付失败",
      ISSUED: "已承保",
      ACTIVE: "保障中",
      EXPIRED: "已失效",
      CANCELLED: "已取消",
      SURRENDERING: "退保处理中",
      SURRENDERED: "已退保",
      REFUNDED: "已退款",
      DRAFT: "草稿",
      INDEXED: "已索引",
      PUBLISHED: "已发布",
      RETIRED: "已终止",
      INACTIVE: "已停用",
      SUBMITTED: "待处理",
      ACCEPTED: "已受理",
      REJECTED: "未受理",
    }[status] ?? status
  );
}

function productLogLabel(action: string) {
  return (
    {
      PRODUCT_UPDATED: "更新产品信息",
      PRODUCT_CREATED: "创建保险产品",
      PRODUCT_STATUS_CHANGED: "变更销售状态",
      VERSION_IMPORTED: "初始化产品版本",
      VERSION_DRAFT_CREATED: "创建版本草稿",
      VERSION_DRAFT_UPDATED: "保存版本草稿",
      VERSION_PUBLISHED: "发布产品版本",
      VERSION_RETIRED: "终止产品版本",
      VERSION_DRAFT_DELETED: "删除版本草稿",
    }[action] ?? action
  );
}

function toDateTimeLocal(value?: string | null) {
  if (!value) return "";
  const date = new Date(value);
  const offset = date.getTimezoneOffset() * 60_000;
  return new Date(date.getTime() - offset).toISOString().slice(0, 16);
}

function paymentProviderLabel(provider?: string) {
  return (
    {
      LOCAL_MOCK_PAY: "智保通模拟支付",
    }[provider ?? ""] ?? provider ?? "—"
  );
}

function relationshipLabel(relationship?: string) {
  return (
    {
      SELF: "本人",
      PARENT: "父母",
      SPOUSE: "配偶",
      CHILD: "子女",
    }[relationship ?? "SELF"] ?? "本人"
  );
}

function accountStatusLabel(status: string) {
  return status === "ACTIVE" ? "启用" : "停用";
}

function statusTone(status: string) {
  if (["ISSUED", "ACTIVE", "PAID", "COMPLETED", "ACCEPTED"].includes(status)) {
    return "success";
  }
  if (
    [
      "CANCELLED",
      "SURRENDERED",
      "REFUNDED",
      "EXPIRED",
      "DISABLED",
      "FAILED",
      "REJECTED",
    ].includes(status)
  ) {
    return "muted";
  }
  return "pending";
}

function selectView(view: ViewKey) {
  if (["overview", "agent"].includes(view) && !hasPermission("dashboard:read")) return;
  if (view === "orders" && !hasPermission("order:read")) return;
  if (view === "policies" && !hasPermission("policy:read")) return;
  if (view === "services" && !hasPermission("service:read")) return;
  if (view === "knowledge" && !hasPermission("knowledge:read")) return;
  activeView.value = view;
  selectedOrder.value = null;
  selectedPolicy.value = null;
  closeServiceRequest();
  if (view === "knowledge") void loadKnowledgeDocuments();
  if (view === "services") void loadServiceRequests();
}

function knowledgeTypeLabel(type: KnowledgeDocumentView["document_type"]) {
  return (
    {
      INSURANCE_TERMS: "保险条款",
      PRODUCT_GUIDE: "产品说明",
      SERVICE_RULES: "服务规则",
      FAQ: "常见问题",
    }[type] ?? type
  );
}

function chooseKnowledgeFile(event: Event) {
  const input = event.target as HTMLInputElement;
  knowledgeUploadFile.value = input.files?.[0] ?? null;
  if (!knowledgeTitle.value && knowledgeUploadFile.value) {
    knowledgeTitle.value = knowledgeUploadFile.value.name.replace(/\.[^.]+$/, "");
  }
}

function selectAgentWorkspaceTab(tab: "analysis" | "runs") {
  agentWorkspaceTab.value = tab;
  if (tab === "runs") void loadAgentRuns();
}

function agentMetricItems(block: AgentBlock) {
  return (block.items ?? []) as unknown as AgentMetric[];
}

function agentChartWidth(block: AgentBlock, value: number) {
  const maximum = Math.max(
    ...(block.points ?? []).map((item) => Number(item.value)),
    1,
  );
  return `${Math.max((Number(value) / maximum) * 100, 2)}%`;
}

function agentBusinessValue(
  item: Record<string, unknown>,
  ...keys: string[]
) {
  for (const key of keys) {
    const value = item[key];
    if (value !== undefined && value !== null && value !== "") return String(value);
  }
  return "—";
}

function splitAgentTableRow(line: string) {
  return line
    .trim()
    .replace(/^\|/, "")
    .replace(/\|$/, "")
    .split("|")
    .map((cell) => cell.trim());
}

function parseAgentReport(source: string): AgentReportBlock[] {
  const lines = source.replace(/\r/g, "").split("\n");
  const blocks: AgentReportBlock[] = [];
  let index = 0;
  while (index < lines.length) {
    const line = lines[index].trim();
    if (!line) {
      index += 1;
      continue;
    }
    if (/^-{3,}$/.test(line)) {
      blocks.push({ kind: "divider" });
      index += 1;
      continue;
    }
    const heading = line.match(/^(#{1,3})\s+(.+)$/);
    if (heading) {
      blocks.push({
        kind: "heading",
        level: heading[1].length,
        text: heading[2],
      });
      index += 1;
      continue;
    }
    if (
      line.includes("|") &&
      index + 1 < lines.length &&
      /^\s*\|?[\s:|-]+\|[\s:|-]+/.test(lines[index + 1])
    ) {
      const headers = splitAgentTableRow(line);
      const rows: string[][] = [];
      index += 2;
      while (index < lines.length && lines[index].includes("|")) {
        rows.push(splitAgentTableRow(lines[index]));
        index += 1;
      }
      blocks.push({ kind: "table", headers, rows });
      continue;
    }
    if (/^[-*]\s+/.test(line)) {
      const items: string[] = [];
      while (index < lines.length && /^[-*]\s+/.test(lines[index].trim())) {
        items.push(lines[index].trim().replace(/^[-*]\s+/, ""));
        index += 1;
      }
      blocks.push({ kind: "list", ordered: false, items });
      continue;
    }
    if (/^\d+[.、]\s*/.test(line)) {
      const items: string[] = [];
      while (index < lines.length && /^\d+[.、]\s*/.test(lines[index].trim())) {
        items.push(lines[index].trim().replace(/^\d+[.、]\s*/, ""));
        index += 1;
      }
      blocks.push({ kind: "list", ordered: true, items });
      continue;
    }
    const paragraphs = [line];
    index += 1;
    while (
      index < lines.length &&
      lines[index].trim() &&
      !/^(#{1,3})\s+/.test(lines[index].trim()) &&
      !/^[-*]\s+/.test(lines[index].trim()) &&
      !/^\d+[.、]\s*/.test(lines[index].trim()) &&
      !/^-{3,}$/.test(lines[index].trim()) &&
      !lines[index].includes("|")
    ) {
      paragraphs.push(lines[index].trim());
      index += 1;
    }
    const text = paragraphs.join(" ");
    blocks.push({
      kind: "paragraph",
      text,
      emphasized: /结论|核心发现|总体来看/.test(text),
    });
  }
  return blocks;
}

function agentInlineHtml(source: string) {
  return source
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;")
    .replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
    .replace(/`(.+?)`/g, "<code>$1</code>");
}

function agentToolLabel(tool: string) {
  return (
    {
      get_operation_overview: "经营总览",
      list_recent_orders: "订单查询",
      list_recent_policies: "保单查询",
      get_attribution_performance: "推广归因",
      list_available_products: "产品目录",
    }[tool] ?? tool
  );
}

async function sendOperationMessage() {
  const text = operationInput.value.trim();
  if (!text || operationLoading.value) return;
  operationLoading.value = true;
  error.value = "";
  operationMessages.value.push({ role: "user", text });
  operationInput.value = "";
  try {
    if (!operationThreadId.value) {
      const threadResponse = await authorizedFetch("/admin/agent/threads", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ title: operationThreadTitle(text) }),
      });
      if (!threadResponse.ok) {
        throw new Error(await responseError(threadResponse, "创建运营会话失败"));
      }
      operationThreadId.value = (await threadResponse.json()).data.id;
    }
    const response = await authorizedFetch(
      `/admin/agent/threads/${operationThreadId.value}/messages`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          content: { type: "text", text },
          page_context: { view: activeView.value },
          client_message_id: crypto.randomUUID(),
        }),
      },
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "运营智能体响应失败"));
    }
    const data = (await response.json()).data;
    // 图表和指标卡来自后端工具结果，不从模型回复文本中猜测业务数据。
    operationMessages.value.push({
      role: "assistant",
      text: data.assistant_message.text,
      cards: data.assistant_message.cards ?? [],
      actions: data.assistant_message.actions ?? [],
      traceUrl: data.trace_url,
      tools: data.invoked_tools ?? [],
    });
    await Promise.all([loadAgentRuns(), loadOperationThreads(false)]);
  } catch (reason) {
    if (!operationInput.value.trim()) operationInput.value = text;
    error.value = reason instanceof Error ? reason.message : "运营智能体响应失败";
  } finally {
    operationLoading.value = false;
  }
}

function operationThreadTitle(text: string) {
  const normalized = text.replace(/\s+/g, " ").trim();
  return normalized.length > 26 ? `${normalized.slice(0, 26)}…` : normalized;
}

function startNewOperationThread() {
  if (operationLoading.value) return;
  operationThreadId.value = "";
  operationMessages.value = [];
  operationInput.value = "";
  error.value = "";
}

async function loadOperationThreads(selectLatest = true) {
  if (!hasPermission("dashboard:read")) return;
  operationHistoryLoading.value = true;
  try {
    const response = await authorizedFetch("/admin/agent/threads?limit=100");
    if (!response.ok) {
      throw new Error(await responseError(response, "加载历史会话失败"));
    }
    operationThreads.value = (await response.json()).data.items;
    if (
      selectLatest &&
      !operationThreadId.value &&
      operationThreads.value.length > 0
    ) {
      await selectOperationThread(operationThreads.value[0].id);
    }
  } catch (reason) {
    error.value = reason instanceof Error ? reason.message : "加载历史会话失败";
  } finally {
    operationHistoryLoading.value = false;
  }
}

async function selectOperationThread(threadId: string) {
  if (operationLoading.value || threadId === operationThreadId.value) return;
  operationHistoryLoading.value = true;
  error.value = "";
  try {
    const response = await authorizedFetch(
      `/admin/agent/threads/${threadId}/messages`,
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "恢复历史会话失败"));
    }
    const items = (await response.json()).data.items as StoredOperationMessage[];
    operationMessages.value = items.map((item) => ({
      role: item.role === "ASSISTANT" ? "assistant" : "user",
      text: item.content,
      cards: item.metadata?.cards ?? [],
      actions: item.metadata?.actions ?? [],
      traceUrl: item.metadata?.trace_url,
      tools: item.metadata?.invoked_tools ?? [],
    }));
    operationThreadId.value = threadId;
    operationInput.value = "";
  } catch (reason) {
    error.value = reason instanceof Error ? reason.message : "恢复历史会话失败";
  } finally {
    operationHistoryLoading.value = false;
  }
}

function executeOperationAction(action: AgentAction) {
  if (action.type === "open_admin_orders" && hasPermission("order:read")) {
    selectView("orders");
  }
  if (action.type === "open_admin_policies" && hasPermission("policy:read")) {
    selectView("policies");
  }
  if (action.type === "open_attribution") selectView("attribution");
}

function openOrder(item: Order) {
  selectedOrder.value = item;
}

function openPolicy(item: Policy) {
  selectedPolicy.value = item;
}

function policyForService(item: ServiceRequestView) {
  return policies.value.find((policy) => policy.policy_id === item.policy_id) ?? null;
}

function orderForService(item: ServiceRequestView) {
  const policy = policyForService(item);
  return orders.value.find((order) => order.order_id === policy?.order_id) ?? null;
}

function customerForService(item: ServiceRequestView) {
  return customers.value.find((customer) => customer.id === item.user_id) ?? null;
}

function serviceTypeLabel(requestType: string) {
  return (
    {
      MEDICAL_REIMBURSEMENT_ASSISTANCE: "医疗费用报销协助",
    }[requestType] ?? requestType
  );
}

// 服务申请审核区会同时加载用户确认字段、模型审计信息和原始材料图片。
function clearServiceMaterialPreview() {
  if (serviceMaterialPreview.value) {
    URL.revokeObjectURL(serviceMaterialPreview.value);
  }
  serviceMaterialPreview.value = "";
  serviceMaterialError.value = "";
  serviceMaterialLoading.value = false;
}

function closeServiceRequest() {
  selectedServiceRequest.value = null;
  clearServiceMaterialPreview();
}

async function openServiceRequest(item: ServiceRequestView) {
  clearServiceMaterialPreview();
  selectedServiceRequest.value = item;
  serviceReviewDecision.value = "ACCEPTED";
  serviceReviewFeedback.value = "";
  if (!item.recognition?.material_available) return;

  serviceMaterialLoading.value = true;
  try {
    const response = await authorizedFetch(
      `/admin/service-requests/${item.request_id}/material`,
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "原始票据读取失败"));
    }
    // 只为当前弹窗创建临时浏览器地址，关闭弹窗时必须调用 revokeObjectURL 释放。
    serviceMaterialPreview.value = URL.createObjectURL(await response.blob());
  } catch (reason) {
    serviceMaterialError.value =
      reason instanceof Error ? reason.message : "原始票据读取失败";
  } finally {
    serviceMaterialLoading.value = false;
  }
}

async function reviewServiceRequest() {
  const item = selectedServiceRequest.value;
  if (!item || item.status !== "SUBMITTED" || serviceReviewLoading.value) return;
  serviceReviewLoading.value = true;
  serviceReviewFeedback.value = "";
  try {
    const response = await authorizedFetch(
      `/admin/service-requests/${item.request_id}/review`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          decision: serviceReviewDecision.value,
        }),
      },
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "服务申请处理失败"));
    }
    const reviewed = (await response.json()).data as ServiceRequestView;
    selectedServiceRequest.value = reviewed;
    serviceReviewFeedback.value = "处理结果已提交，用户端服务记录将同步更新。";
    await loadServiceRequests();
  } catch (reason) {
    serviceReviewFeedback.value =
      reason instanceof Error ? reason.message : "服务申请处理失败";
  } finally {
    serviceReviewLoading.value = false;
  }
}

function chooseRole(role: RoleDefinition) {
  selectedRoleCode.value = role.code;
  editablePermissions.value = [...role.permissions];
  editableDataScope.value = role.data_scope;
  roleMessage.value = "";
}

function openCreateAdmin() {
  editingAdmin.value = null;
  accountUsername.value = "";
  accountDisplayName.value = "";
  accountPassword.value = "";
  accountRoleCode.value =
    roles.value.find((role) => !role.protected)?.code ?? roles.value[0]?.code ?? "";
  accountStatus.value = "ACTIVE";
  accountError.value = "";
  passwordResetValue.value = "";
  passwordResetMessage.value = "";
  accountEditorOpen.value = true;
}

function openEditAdmin(user: AdminAccount) {
  editingAdmin.value = user;
  accountUsername.value = user.username;
  accountDisplayName.value = user.display_name;
  accountPassword.value = "";
  accountRoleCode.value = user.roles[0] ?? "";
  accountStatus.value = user.status;
  accountError.value = "";
  passwordResetValue.value = "";
  passwordResetMessage.value = "";
  accountEditorOpen.value = true;
}

async function saveAdminAccount() {
  accountSaving.value = true;
  accountError.value = "";
  try {
    const editing = editingAdmin.value;
    const path = editing
      ? `/admin/users/admins/${editing.id}`
      : "/admin/users/admins";
    const payload = editing
      ? {
          display_name: accountDisplayName.value,
          status: accountStatus.value,
          role_code: accountRoleCode.value,
        }
      : {
          username: accountUsername.value,
          password: accountPassword.value,
          display_name: accountDisplayName.value,
          role_code: accountRoleCode.value,
        };
    const response = await authorizedFetch(path, {
      method: editing ? "PUT" : "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(await responseError(response, "后台账号保存失败"));
    }
    await loadManagedUsers();
    accountEditorOpen.value = false;
  } catch (reason) {
    accountError.value = reason instanceof Error ? reason.message : "后台账号保存失败";
  } finally {
    accountSaving.value = false;
  }
}

async function resetAdminPassword() {
  if (!editingAdmin.value || passwordResetValue.value.length < 8) return;
  passwordResetMessage.value = "";
  const response = await authorizedFetch(
    `/admin/users/admins/${editingAdmin.value.id}/password`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ new_password: passwordResetValue.value }),
    },
  );
  if (!response.ok) {
    passwordResetMessage.value = await responseError(response, "密码重置失败");
    return;
  }
  passwordResetValue.value = "";
  passwordResetMessage.value = "密码已重置";
}

async function confirmCustomerStatusChange() {
  if (!customerToToggle.value) return;
  customerSaving.value = true;
  const customer = customerToToggle.value;
  const nextStatus = customer.status === "ACTIVE" ? "DISABLED" : "ACTIVE";
  try {
    const response = await authorizedFetch(
      `/admin/users/customers/${customer.id}/status`,
      {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ status: nextStatus }),
      },
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "用户状态更新失败"));
    }
    await loadManagedUsers();
    customerToToggle.value = null;
  } catch (reason) {
    accountError.value = reason instanceof Error ? reason.message : "用户状态更新失败";
  } finally {
    customerSaving.value = false;
  }
}

function togglePermission(permission: string) {
  if (!selectedRole.value || selectedRole.value.protected || !canManageRoles.value) return;
  editablePermissions.value = editablePermissions.value.includes(permission)
    ? editablePermissions.value.filter((item) => item !== permission)
    : [...editablePermissions.value, permission];
}

async function saveRole() {
  const role = selectedRole.value;
  if (!role || role.protected || !canManageRoles.value || !roleDirty.value) return;
  roleSaving.value = true;
  roleMessage.value = "";
  try {
    const response = await authorizedFetch(`/admin/roles/${role.code}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        permissions: editablePermissions.value,
        data_scope: editableDataScope.value,
      }),
    });
    if (!response.ok) {
      throw new Error(await responseError(response, "角色权限保存失败"));
    }
    const updated = (await response.json()).data as RoleDefinition;
    roles.value = roles.value.map((item) => (item.code === updated.code ? updated : item));
    chooseRole(updated);
    roleMessage.value = "角色权限已保存并立即生效";
  } catch (reason) {
    roleMessage.value = reason instanceof Error ? reason.message : "角色权限保存失败";
  } finally {
    roleSaving.value = false;
  }
}

async function apiFetch(path: string, init: RequestInit = {}) {
  return fetch(`${apiBase}${path}`, {
    ...init,
    credentials: "include",
  });
}

async function responseError(response: Response, fallback: string) {
  try {
    const body = await response.json();
    return body.error?.message ?? fallback;
  } catch {
    return fallback;
  }
}

async function authorizedFetch(path: string, init: RequestInit = {}, allowRefresh = true) {
  const headers = new Headers(init.headers);
  headers.set("Authorization", `Bearer ${token.value}`);
  const response = await apiFetch(path, { ...init, headers });
  if (response.status === 401 && allowRefresh && (await refreshAccessToken())) {
    return authorizedFetch(path, init, false);
  }
  return response;
}

async function login() {
  loading.value = true;
  error.value = "";
  try {
    const response = await apiFetch("/admin/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username: username.value, password: password.value }),
    });
    if (!response.ok) {
      throw new Error(await responseError(response, "登录失败"));
    }
    const body = await response.json();
    token.value = body.data.tokens.access_token;
    await loadProfile();
    await loadBusiness();
  } catch (reason) {
    error.value = reason instanceof Error ? reason.message : "请求失败";
  } finally {
    loading.value = false;
  }
}

async function refreshAccessToken() {
  const response = await apiFetch("/admin/auth/refresh", { method: "POST" });
  if (!response.ok) {
    token.value = "";
    profile.value = null;
    return false;
  }
  const body = await response.json();
  token.value = body.data.tokens.access_token;
  return true;
}

async function loadProfile(allowRefresh = true) {
  const response = await apiFetch("/admin/me", {
    headers: { Authorization: `Bearer ${token.value}` },
  });
  if (response.status === 401 && allowRefresh && (await refreshAccessToken())) {
    return loadProfile(false);
  }
  if (!response.ok) {
    token.value = "";
    profile.value = null;
    throw new Error(await responseError(response, "身份读取失败"));
  }
  const body = await response.json();
  profile.value = body.data;
}

async function loadBusiness() {
  dashboard.value = null;
  if (hasPermission("dashboard:read")) {
    const dashboardResponse = await authorizedFetch("/admin/dashboard");
    if (!dashboardResponse.ok) {
      throw new Error(await responseError(dashboardResponse, "运营数据加载失败"));
    }
    dashboard.value = (await dashboardResponse.json()).data;
  } else if (hasPermission("service:read")) {
    activeView.value = "services";
  } else if (hasPermission("order:read")) {
    activeView.value = "orders";
  } else if (hasPermission("policy:read")) {
    activeView.value = "policies";
  } else if (hasPermission("knowledge:read")) {
    activeView.value = "knowledge";
  }
  orders.value = [];
  policies.value = [];
  const [ordersResponse, policiesResponse] = await Promise.all([
    hasPermission("order:read")
      ? authorizedFetch("/admin/orders")
      : Promise.resolve(null),
    hasPermission("policy:read")
      ? authorizedFetch("/admin/policies")
      : Promise.resolve(null),
  ]);
  if (ordersResponse?.ok) orders.value = (await ordersResponse.json()).data.items;
  if (policiesResponse?.ok) policies.value = (await policiesResponse.json()).data.items;
  if (hasPermission("role:read")) {
    const rolesResponse = await authorizedFetch("/admin/roles");
    if (rolesResponse.ok) {
      const roleData = (await rolesResponse.json()).data;
      roles.value = roleData.items;
      permissionCatalog.value = roleData.permission_catalog;
      dataScopes.value = roleData.data_scopes;
      if (roles.value.length) chooseRole(roles.value[0]);
    }
  }
  if (hasPermission("user:read")) await loadManagedUsers();
  if (hasPermission("product:read")) await loadManagedProducts();
  if (hasPermission("knowledge:read")) await loadKnowledgeDocuments();
  if (hasPermission("attribution:read")) await loadAttribution();
  if (hasPermission("service:read")) await loadServiceRequests();
  if (hasPermission("dashboard:read")) {
    await Promise.all([loadAgentRuns(), loadOperationThreads()]);
  }
}

async function loadServiceRequests() {
  const response = await authorizedFetch("/admin/service-requests");
  if (!response.ok) return;
  serviceRequests.value = (await response.json()).data.items;
}

async function loadAgentRuns() {
  const response = await authorizedFetch("/admin/agent/runs?limit=10");
  if (!response.ok) return;
  agentRuns.value = (await response.json()).data.items;
}

async function loadManagedUsers() {
  const response = await authorizedFetch("/admin/users");
  if (!response.ok) return;
  const data = (await response.json()).data;
  adminUsers.value = data.admin_users;
  customers.value = data.customers;
}

async function loadManagedProducts() {
  const response = await authorizedFetch("/admin/products");
  if (!response.ok) return;
  managedProducts.value = (await response.json()).data.items;
}

async function loadKnowledgeDocuments() {
  const response = await authorizedFetch("/admin/knowledge/documents");
  if (!response.ok) return;
  knowledgeDocuments.value = (await response.json()).data.items;
}

// 知识发布主链路：上传草稿 → 生成索引 → 检索测试 → 发布。
async function uploadKnowledgeDocument() {
  if (
    !hasPermission("knowledge:write") ||
    !knowledgeUploadFile.value ||
    !knowledgeTitle.value.trim() ||
    knowledgeUploading.value
  ) {
    return;
  }
  knowledgeUploading.value = true;
  knowledgeFeedback.value = "";
  try {
    const form = new FormData();
    form.set("file", knowledgeUploadFile.value);
    form.set("title", knowledgeTitle.value.trim());
    form.set("document_type", knowledgeDocumentType.value);
    if (knowledgeProductCode.value.trim()) {
      form.set("product_code", knowledgeProductCode.value.trim().toUpperCase());
    }
    const response = await authorizedFetch("/admin/knowledge/documents/upload", {
      method: "POST",
      body: form,
    });
    if (!response.ok) {
      throw new Error(await responseError(response, "知识文档上传失败"));
    }
    knowledgeTitle.value = "";
    knowledgeProductCode.value = "";
    knowledgeUploadFile.value = null;
    knowledgeFeedback.value = "文档已进入草稿区，可以继续生成索引";
    await loadKnowledgeDocuments();
  } catch (reason) {
    knowledgeFeedback.value =
      reason instanceof Error ? reason.message : "知识文档上传失败";
  } finally {
    knowledgeUploading.value = false;
  }
}

async function executeKnowledgeAction(
  document: KnowledgeDocumentView,
  action: "index" | "publish" | "disable",
) {
  if (knowledgeActionId.value) return;
  const actionName =
    action === "index" ? "生成索引" : action === "publish" ? "发布知识" : "停用知识";
  if (
    action !== "index" &&
    !window.confirm(`确认${actionName}“${document.title}”？`)
  ) {
    return;
  }
  knowledgeActionId.value = document.document_id;
  // 建立索引可能触发本地模型首次加载，因此向用户展示明确的等待状态。
  knowledgeFeedback.value =
    action === "index" ? "正在调用本地 BGE-M3 与 Milvus 生成索引…" : "";
  try {
    const response = await authorizedFetch(
      `/admin/knowledge/documents/${document.document_id}/${action}`,
      { method: "POST" },
    );
    if (!response.ok) {
      throw new Error(await responseError(response, `${actionName}失败`));
    }
    knowledgeFeedback.value = `${document.title}已完成${actionName}`;
    await loadKnowledgeDocuments();
  } catch (reason) {
    knowledgeFeedback.value =
      reason instanceof Error ? reason.message : `${actionName}失败`;
  } finally {
    knowledgeActionId.value = "";
  }
}

function openKnowledgeSearchTest(document: KnowledgeDocumentView) {
  knowledgeTestDocumentId.value = document.document_id;
  knowledgeTestQuery.value = "";
  knowledgeTestResults.value = [];
  knowledgeTestFeedback.value = "";
}

function closeKnowledgeSearchTest() {
  knowledgeTestDocumentId.value = "";
  knowledgeTestQuery.value = "";
  knowledgeTestResults.value = [];
  knowledgeTestFeedback.value = "";
}

async function runKnowledgeSearchTest() {
  if (
    !knowledgeTestDocumentId.value ||
    !knowledgeTestQuery.value.trim() ||
    knowledgeTestRunning.value
  ) {
    return;
  }
  knowledgeTestRunning.value = true;
  knowledgeTestResults.value = [];
  knowledgeTestFeedback.value = "";
  try {
    const response = await authorizedFetch(
      `/admin/knowledge/documents/${knowledgeTestDocumentId.value}/search-test`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          query: knowledgeTestQuery.value.trim(),
          limit: 5,
        }),
      },
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "检索测试失败"));
    }
    // 测试结果由后端持久化；成功记录同时作为“允许发布”的服务端门禁。
    const data = (await response.json()).data;
    knowledgeTestResults.value = data.items;
    knowledgeTestFeedback.value = data.total
      ? `已召回 ${data.total} 个知识片段`
      : "没有召回相关内容，请调整问题或重新生成索引";
    await loadKnowledgeDocuments();
  } catch (reason) {
    knowledgeTestFeedback.value =
      reason instanceof Error ? reason.message : "检索测试失败";
  } finally {
    knowledgeTestRunning.value = false;
  }
}

function emptyProductPlan(): ManagedPlan {
  return {
    code: "STANDARD",
    name: "标准计划",
    summary: "",
    status: "ACTIVE",
    premium_cents: 0,
    coverage_amount_cents: 0,
    min_age: 0,
    max_age: 65,
  };
}

function openProductCreator() {
  createProductCode.value = "";
  createProductName.value = "";
  createProductCategory.value = "MEDICAL";
  createProductSummary.value = "";
  createVersionNo.value = "1.0.0";
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  createEffectiveFrom.value = toDateTimeLocal(tomorrow.toISOString());
  createRuleVersion.value = "eligibility-v1";
  createRateVersion.value = "rate-v1";
  createTermsSummary.value = "";
  createProductPlans.value = [emptyProductPlan()];
  productCreatorStep.value = 1;
  productCreatorError.value = "";
  productCreatorOpen.value = true;
}

function addCreateProductPlan() {
  const next = emptyProductPlan();
  next.code = "";
  next.name = "";
  createProductPlans.value.push(next);
}

function removeCreateProductPlan(index: number) {
  if (createProductPlans.value.length > 1) createProductPlans.value.splice(index, 1);
}

function moveProductCreator(step: -1 | 1) {
  if (step === 1 && !productCreatorStepValid.value) return;
  productCreatorStep.value = Math.min(
    3,
    Math.max(1, productCreatorStep.value + step),
  ) as 1 | 2 | 3;
  productCreatorError.value = "";
}

async function createNewProduct() {
  if (!productCreatorStepValid.value || productCreatorSaving.value) return;
  productCreatorSaving.value = true;
  productCreatorError.value = "";
  try {
    const response = await authorizedFetch("/admin/products", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        product_code: createProductCode.value.trim().toUpperCase(),
        name: createProductName.value,
        category: createProductCategory.value,
        summary: createProductSummary.value,
        version_no: createVersionNo.value,
        effective_from: new Date(createEffectiveFrom.value).toISOString(),
        rule_version: createRuleVersion.value,
        rate_version: createRateVersion.value,
        terms_summary: createTermsSummary.value,
        plans: createProductPlans.value,
      }),
    });
    if (!response.ok) throw new Error(await responseError(response, "产品创建失败"));
    const data = (await response.json()).data as {
      product: { product_id: string };
      initial_version: ManagedProductVersion;
    };
    await loadManagedProducts();
    const product = managedProducts.value.find(
      (item) => item.product_id === data.product.product_id,
    );
    productCreatorOpen.value = false;
    if (product) {
      openProductEditor(product);
      productEditorTab.value = "versions";
      const version = product.versions.find(
        (item) => item.id === data.initial_version.id,
      );
      if (version) selectProductVersion(version);
      productEditorMessage.value = "产品已创建，发布版本后再恢复销售";
    }
  } catch (reason) {
    productCreatorError.value =
      reason instanceof Error ? reason.message : "产品创建失败";
  } finally {
    productCreatorSaving.value = false;
  }
}

function openProductEditor(product: ManagedProduct) {
  selectedProductId.value = product.product_id;
  productName.value = product.name;
  productCategory.value = product.category as "MEDICAL" | "ACCIDENT";
  productSummary.value = product.summary;
  productEditorTab.value = "profile";
  productEditorMessage.value = "";
  creatingVersion.value = false;
  productEditorOpen.value = true;
  const version = product.versions[0];
  if (version) selectProductVersion(version);
}

function selectProductVersion(version: ManagedProductVersion) {
  selectedProductVersionId.value = version.id;
  versionEffectiveFrom.value = toDateTimeLocal(version.effective_from);
  versionEffectiveTo.value = toDateTimeLocal(version.effective_to);
  versionRule.value = version.rule_version;
  versionRate.value = version.rate_version;
  versionTerms.value = version.terms_summary;
  versionPlans.value = version.plans.map((plan) => ({ ...plan }));
  creatingVersion.value = false;
  productEditorMessage.value = "";
}

async function refreshProductEditor(versionId?: string) {
  await loadManagedProducts();
  const product = selectedProduct.value;
  if (!product) {
    productEditorOpen.value = false;
    return;
  }
  productName.value = product.name;
  productCategory.value = product.category as "MEDICAL" | "ACCIDENT";
  productSummary.value = product.summary;
  const version =
    product.versions.find((item) => item.id === versionId) ?? product.versions[0];
  if (version) selectProductVersion(version);
}

async function saveProductProfile() {
  const product = selectedProduct.value;
  if (!product || productEditorSaving.value) return;
  productEditorSaving.value = true;
  productEditorMessage.value = "";
  try {
    const response = await authorizedFetch(`/admin/products/${product.product_id}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        name: productName.value,
        category: productCategory.value,
        summary: productSummary.value,
      }),
    });
    if (!response.ok) throw new Error(await responseError(response, "产品信息保存失败"));
    await refreshProductEditor();
    productEditorMessage.value = "产品基本信息已保存";
  } catch (reason) {
    productEditorMessage.value =
      reason instanceof Error ? reason.message : "产品信息保存失败";
  } finally {
    productEditorSaving.value = false;
  }
}

function startCreateVersion() {
  const product = selectedProduct.value;
  if (!product) return;
  const latest = product.versions[0];
  const major = Number(latest?.version_no.split(".")[0] ?? "0") + 1;
  newVersionNo.value = `${major}.0.0`;
  const future = new Date();
  future.setDate(future.getDate() + 1);
  newVersionEffectiveFrom.value = toDateTimeLocal(future.toISOString());
  newVersionSourceId.value = latest?.id ?? "";
  creatingVersion.value = true;
  productEditorMessage.value = "";
}

async function createProductVersion() {
  const product = selectedProduct.value;
  if (!product || productEditorSaving.value) return;
  productEditorSaving.value = true;
  productEditorMessage.value = "";
  try {
    const response = await authorizedFetch(
      `/admin/products/${product.product_id}/versions`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          version_no: newVersionNo.value,
          effective_from: new Date(newVersionEffectiveFrom.value).toISOString(),
          source_version_id: newVersionSourceId.value || null,
        }),
      },
    );
    if (!response.ok) throw new Error(await responseError(response, "版本草稿创建失败"));
    const created = (await response.json()).data as ManagedProductVersion;
    await refreshProductEditor(created.id);
    productEditorTab.value = "versions";
    productEditorMessage.value = "新版本草稿已创建，可继续编辑计划和条款";
  } catch (reason) {
    productEditorMessage.value =
      reason instanceof Error ? reason.message : "版本草稿创建失败";
  } finally {
    productEditorSaving.value = false;
  }
}

function addProductPlan() {
  versionPlans.value.push({
    code: "",
    name: "",
    summary: "",
    status: "ACTIVE",
    premium_cents: 0,
    coverage_amount_cents: 0,
    min_age: 0,
    max_age: 70,
  });
}

function removeProductPlan(index: number) {
  versionPlans.value.splice(index, 1);
}

function updatePlanAmount(
  plan: ManagedPlan,
  field: "premium_cents" | "coverage_amount_cents",
  event: Event,
) {
  const value = Number((event.target as HTMLInputElement).value);
  plan[field] = Number.isFinite(value) ? Math.round(value * 100) : 0;
}

async function saveProductVersion() {
  const product = selectedProduct.value;
  const version = selectedProductVersion.value;
  if (!product || !version || version.status !== "DRAFT" || productEditorSaving.value) return;
  productEditorSaving.value = true;
  productEditorMessage.value = "";
  try {
    const response = await authorizedFetch(
      `/admin/products/${product.product_id}/versions/${version.id}`,
      {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          effective_from: new Date(versionEffectiveFrom.value).toISOString(),
          effective_to: versionEffectiveTo.value
            ? new Date(versionEffectiveTo.value).toISOString()
            : null,
          rule_version: versionRule.value,
          rate_version: versionRate.value,
          terms_summary: versionTerms.value,
          plans: versionPlans.value,
        }),
      },
    );
    if (!response.ok) throw new Error(await responseError(response, "版本保存失败"));
    await refreshProductEditor(version.id);
    productEditorMessage.value = "版本草稿已保存";
  } catch (reason) {
    productEditorMessage.value =
      reason instanceof Error ? reason.message : "版本保存失败";
  } finally {
    productEditorSaving.value = false;
  }
}

async function executeVersionAction(action: "publish" | "retire" | "delete") {
  const product = selectedProduct.value;
  const version = selectedProductVersion.value;
  if (!product || !version || productEditorSaving.value) return;
  const actionText = action === "publish" ? "发布" : action === "retire" ? "终止" : "删除";
  if (!window.confirm(`确认${actionText}版本 V${version.version_no}？`)) return;
  productEditorSaving.value = true;
  productEditorMessage.value = "";
  try {
    const suffix = action === "delete" ? "" : `/${action}`;
    const response = await authorizedFetch(
      `/admin/products/${product.product_id}/versions/${version.id}${suffix}`,
      { method: action === "delete" ? "DELETE" : "POST" },
    );
    if (!response.ok) throw new Error(await responseError(response, `版本${actionText}失败`));
    await refreshProductEditor();
    productEditorMessage.value = `版本已${actionText}`;
  } catch (reason) {
    productEditorMessage.value =
      reason instanceof Error ? reason.message : `版本${actionText}失败`;
  } finally {
    productEditorSaving.value = false;
  }
}

async function changeProductStatus(product: ManagedProduct) {
  if (!hasPermission("product:write") || productSavingId.value) return;
  productSavingId.value = product.product_id;
  try {
    const response = await authorizedFetch(
      `/admin/products/${product.product_id}/status`,
      {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          status: product.status === "ACTIVE" ? "INACTIVE" : "ACTIVE",
        }),
      },
    );
    if (!response.ok) {
      throw new Error(await responseError(response, "产品状态更新失败"));
    }
    await refreshProductEditor();
  } catch (reason) {
    error.value = reason instanceof Error ? reason.message : "产品状态更新失败";
  } finally {
    productSavingId.value = "";
  }
}

async function loadAttribution() {
  const [overviewResponse, codesResponse] = await Promise.all([
    authorizedFetch("/admin/attribution/overview"),
    authorizedFetch("/admin/attribution/codes"),
  ]);
  if (overviewResponse.ok) {
    attributionOverview.value = (await overviewResponse.json()).data;
  }
  if (codesResponse.ok) {
    promotionCodes.value = (await codesResponse.json()).data.items;
  }
}

async function logout() {
  try {
    if (token.value) {
      await apiFetch("/admin/auth/logout", {
        method: "POST",
        headers: { Authorization: `Bearer ${token.value}` },
      });
    }
  } finally {
    token.value = "";
    profile.value = null;
    dashboard.value = null;
    orders.value = [];
    policies.value = [];
    serviceRequests.value = [];
    roles.value = [];
    permissionCatalog.value = [];
    dataScopes.value = [];
    adminUsers.value = [];
    customers.value = [];
    managedProducts.value = [];
    knowledgeDocuments.value = [];
    knowledgeUploadFile.value = null;
    knowledgeFeedback.value = "";
    attributionOverview.value = null;
    promotionCodes.value = [];
    operationThreadId.value = "";
    operationThreads.value = [];
    operationMessages.value = [];
    agentRuns.value = [];
    selectedOrder.value = null;
    selectedPolicy.value = null;
    closeServiceRequest();
    serviceStatusFilter.value = "ALL";
    serviceSearch.value = "";
    serviceReviewFeedback.value = "";
    activeView.value = "overview";
    error.value = "";
    password.value = "";
  }
}

onMounted(async () => {
  try {
    if (await refreshAccessToken()) {
      await loadProfile(false);
      await loadBusiness();
    }
  } catch {
    token.value = "";
    profile.value = null;
  } finally {
    restoring.value = false;
  }
});
</script>

<template>
  <main v-if="restoring" class="auth-shell">
    <section class="auth-brand-panel">
      <div class="auth-brand">
        <b>Z</b>
        <span>智保通<small>OPERATIONS CONSOLE</small></span>
      </div>
      <div class="auth-brand-copy">
        <p class="kicker">INSURANCE OPERATIONS</p>
        <h1>智保通<br /><em>统一运营中枢</em></h1>
        <p>连接投保、订单、保单与渠道数据，为运营团队提供清晰、可靠的工作入口。</p>
      </div>
      <p class="auth-copyright">ZBT · SECURE OPERATIONS</p>
    </section>
    <section class="auth-form-panel">
      <div class="session-card" aria-live="polite">
        <span class="session-symbol"><i></i></span>
        <p class="kicker">SECURE SESSION</p>
        <h2>正在验证登录状态</h2>
        <p>请稍候，正在建立安全连接。</p>
      </div>
    </section>
  </main>

  <main v-else-if="!profile" class="auth-shell">
    <section class="auth-brand-panel">
      <div class="auth-brand">
        <b>Z</b>
        <span>智保通<small>OPERATIONS CONSOLE</small></span>
      </div>
      <div class="auth-brand-copy">
        <p class="kicker">INSURANCE OPERATIONS</p>
        <h1>智保通<br /><em>统一运营中枢</em></h1>
        <p>连接投保、订单、保单与渠道数据，为运营团队提供清晰、可靠的工作入口。</p>
        <div class="capability-tags">
          <span>订单管理</span>
          <span>保单服务</span>
          <span>组织权限</span>
        </div>
      </div>
      <p class="auth-copyright">ZBT · SECURE OPERATIONS</p>
    </section>

    <section class="auth-form-panel">
      <div class="login-card">
        <div class="login-heading">
          <p class="kicker">WELCOME BACK</p>
          <h2>登录运营平台</h2>
          <p>请输入已授权的账号信息</p>
        </div>

        <form @submit.prevent="login">
          <label>
            <span>账号</span>
            <div class="input-control">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M5 20a7 7 0 0 1 14 0M12 13a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
              </svg>
              <input
                v-model.trim="username"
                autocomplete="username"
                placeholder="请输入账号"
                autofocus
              />
            </div>
          </label>
          <label>
            <span>密码</span>
            <div class="input-control password-control">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M7 10V8a5 5 0 0 1 10 0v2M5 10h14v10H5V10Zm7 4v3" />
              </svg>
              <input
                v-model="password"
                :type="showPassword ? 'text' : 'password'"
                autocomplete="current-password"
                placeholder="请输入密码"
              />
              <button
                type="button"
                class="password-toggle"
                :aria-label="showPassword ? '隐藏密码' : '显示密码'"
                @click="showPassword = !showPassword"
              >
                {{ showPassword ? "隐藏" : "显示" }}
              </button>
            </div>
          </label>
          <button
            class="login-submit"
            type="submit"
            :disabled="loading || !username.trim() || !password"
          >
            <span>{{ loading ? "正在验证身份…" : "安全登录" }}</span>
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <path d="M5 12h14m-5-5 5 5-5 5" />
            </svg>
          </button>
          <p v-if="error" class="login-error" role="alert"><span>!</span>{{ error }}</p>
        </form>

        <div class="security-note">
          <span>◇</span>
          <p>为保障业务数据安全，请勿共享账号信息，离开设备时请及时退出。</p>
        </div>
      </div>
    </section>
  </main>

  <main v-else class="admin-shell">
    <aside class="admin-sidebar">
      <div class="logo">
        <b>Z</b>
        <span>智保通<br /><small>OPERATIONS</small></span>
      </div>
      <nav class="admin-nav">
        <p>业务工作台</p>
        <button
          v-if="hasPermission('dashboard:read')"
          :class="{ active: activeView === 'overview' }"
          @click="selectView('overview')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M4 4h6v6H4V4Zm10 0h6v6h-6V4ZM4 14h6v6H4v-6Zm10 0h6v6h-6v-6Z" />
          </svg>
          <span>总览台</span>
        </button>
        <button
          v-if="hasPermission('dashboard:read')"
          :class="{ active: activeView === 'agent' }"
          @click="selectView('agent')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M7 8h10M7 12h7M8 18l-4 2 1-4a8 8 0 1 1 3 2Zm4-15v3m-5 1L4 5m14 2 2-2" />
          </svg>
          <span>运营智能体</span>
          <small>AI</small>
        </button>
        <button
          v-if="hasPermission('order:read')"
          :class="{ active: activeView === 'orders' }"
          @click="selectView('orders')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M6 3h12v18l-3-2-3 2-3-2-3 2V3Zm3 5h6m-6 4h6" />
          </svg>
          <span>订单中心</span>
          <small>{{ orders.length }}</small>
        </button>
        <button
          v-if="hasPermission('policy:read')"
          :class="{ active: activeView === 'policies' }"
          @click="selectView('policies')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M12 3 5 6v5c0 4.6 2.8 8.4 7 10 4.2-1.6 7-5.4 7-10V6l-7-3Zm-3 9 2 2 4-4" />
          </svg>
          <span>保单中心</span>
          <small>{{ policies.length }}</small>
        </button>
        <button
          v-if="hasPermission('service:read')"
          :class="{ active: activeView === 'services' }"
          @click="selectView('services')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M5 4h14v16H5V4Zm3 4h8m-8 4h8m-8 4h5M16 2v4M8 2v4" />
          </svg>
          <span>服务申请</span>
          <small>{{ pendingServiceCount }}</small>
        </button>
        <button
          v-if="hasPermission('product:read')"
          :class="{ active: activeView === 'products' }"
          @click="selectView('products')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M4 6h16v12H4V6Zm4-3v6m8-6v6M7 13h4m2 0h4" />
          </svg>
          <span>产品管理</span>
          <small>{{ managedProducts.length }}</small>
        </button>
        <button
          v-if="hasPermission('knowledge:read')"
          :class="{ active: activeView === 'knowledge' }"
          @click="selectView('knowledge')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M5 4.5A2.5 2.5 0 0 1 7.5 2H20v16H7.5A2.5 2.5 0 0 0 5 20.5v-16Zm0 0V21m4-14h7m-7 4h7" />
          </svg>
          <span>知识中心</span>
          <small>{{ knowledgeDocuments.length }}</small>
        </button>
        <button
          v-if="hasPermission('attribution:read')"
          :class="{ active: activeView === 'attribution' }"
          @click="selectView('attribution')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M4 19V9m6 10V5m6 14v-7m4 7H2M6 7l4-4 5 5 5-5" />
          </svg>
          <span>推广归因</span>
          <small>{{ attributionOverview?.order_count ?? 0 }}</small>
        </button>
        <p>系统治理</p>
        <button
          v-if="hasPermission('user:read')"
          :class="{ active: activeView === 'users' }"
          @click="selectView('users')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M16 20v-1.5a4.5 4.5 0 0 0-4.5-4.5h-3A4.5 4.5 0 0 0 4 18.5V20m6-9a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm6-1 2 2 3-4" />
          </svg>
          <span>用户维护</span>
          <small>{{ adminUsers.length + customers.length }}</small>
        </button>
        <button
          v-if="hasPermission('role:read')"
          :class="{ active: activeView === 'permissions' }"
          @click="selectView('permissions')"
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M12 3 4 7v5c0 4.5 3 7.8 8 9 5-1.2 8-4.5 8-9V7l-8-4Zm0 6a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm-3 7c.8-1.3 1.8-2 3-2s2.2.7 3 2" />
          </svg>
          <span>角色权限</span>
          <small>{{ roles.length }}</small>
        </button>
      </nav>
      <div class="sidebar-profile">
        <span>{{ profile.display_name.slice(0, 1) }}</span>
        <div>
          <strong>{{ profile.display_name }}</strong>
          <small>{{ profile.roles[0] ?? "OPERATOR" }}</small>
        </div>
      </div>
    </aside>

    <section class="workspace">
      <header class="topbar">
        <div class="breadcrumb">
          <span>运营中心</span>
          <i>/</i>
          <strong>{{ currentViewLabel }}</strong>
        </div>
        <div class="header-actions">
          <span class="system-status"><i></i> 系统运行正常</span>
          <button class="logout-button" @click="logout" aria-label="退出登录">
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <path d="M10 5H5v14h5m4-3 4-4-4-4m4 4H9" />
            </svg>
            退出
          </button>
        </div>
      </header>

      <div v-if="activeView === 'overview'" class="dashboard view-enter">
        <section class="command-hero">
          <div class="hero-copy">
            <p class="eyebrow"><span></span> ZBT OPERATIONS · LIVE</p>
            <h1>{{ profile.display_name }}，欢迎回来</h1>
            <p>今天是 {{ currentDate }}。投保与承保链路运行稳定，业务数据已完成同步。</p>
            <div class="hero-actions">
              <button v-if="hasPermission('order:read')" @click="selectView('orders')">查看订单</button>
              <button v-if="hasPermission('policy:read')" class="ghost" @click="selectView('policies')">保单中心</button>
            </div>
          </div>
          <img src="/images/zbt-operations-hero.png" alt="智保通智能保险运营视觉图" />
          <div class="hero-scope">
            <small>DATA SCOPE</small>
            <strong>{{ profile.data_scope }}</strong>
            <span>数据权限范围</span>
          </div>
        </section>

        <section class="metric-grid">
          <article>
            <div class="metric-icon blue">
              <svg viewBox="0 0 24 24"><path d="M6 3h12v18l-3-2-3 2-3-2-3 2V3Zm3 5h6m-6 4h6" /></svg>
            </div>
            <div>
              <small>投保订单</small>
              <strong>{{ dashboard?.order_count ?? 0 }}</strong>
              <p><i></i> 交易数据实时同步</p>
            </div>
          </article>
          <article>
            <div class="metric-icon cyan">
              <svg viewBox="0 0 24 24"><path d="M12 3 5 6v5c0 4.6 2.8 8.4 7 10 4.2-1.6 7-5.4 7-10V6l-7-3Zm-3 9 2 2 4-4" /></svg>
            </div>
            <div>
              <small>有效保单</small>
              <strong>{{ dashboard?.policy_count ?? 0 }}</strong>
              <p><i></i> 承保率 {{ issuedRate }}%</p>
            </div>
          </article>
          <article>
            <div class="metric-icon gold">
              <svg viewBox="0 0 24 24"><path d="M12 3v18m5-14H9.5a3 3 0 0 0 0 6h5a3 3 0 0 1 0 6H7" /></svg>
            </div>
            <div>
              <small>累计保费</small>
              <strong>{{ formatMoney(dashboard?.premium_cents ?? 0) }}</strong>
              <p>单均 {{ formatMoney(averagePremium) }}</p>
            </div>
          </article>
          <article>
            <div class="metric-icon violet">
              <svg viewBox="0 0 24 24"><path d="M12 3 4 7v5c0 4.5 3 7.8 8 9 5-1.2 8-4.5 8-9V7l-8-4" /></svg>
            </div>
            <div>
              <small>当前角色</small>
              <strong>{{ profile.roles.length }}</strong>
              <p>{{ profile.roles.join(" / ") }}</p>
            </div>
          </article>
        </section>

        <section class="overview-grid">
          <article class="overview-card order-overview">
            <div class="card-heading">
              <div>
                <p class="eyebrow">ORDER PULSE</p>
                <h2>订单动态</h2>
              </div>
              <button @click="selectView('orders')">查看全部 <span>→</span></button>
            </div>
            <div v-if="orders.length === 0" class="empty-state">暂无订单数据</div>
            <div v-else class="compact-table">
              <div v-for="item in orders.slice(0, 5)" :key="item.order_id" class="compact-row">
                <span class="row-symbol">订</span>
                <div>
                  <strong>{{ item.order_no }}</strong>
                  <small>{{ item.order_id }}</small>
                </div>
                <span class="status-pill" :class="statusTone(item.status)">
                  {{ statusLabel(item.status) }}
                </span>
                <b>{{ formatMoney(item.amount_cents) }}</b>
              </div>
            </div>
          </article>

          <article class="overview-card health-card">
            <div class="card-heading">
              <div>
                <p class="eyebrow">BUSINESS HEALTH</p>
                <h2>业务健康度</h2>
              </div>
              <span class="live-badge"><i></i> LIVE</span>
            </div>
            <div class="health-content">
              <div
                class="rate-ring"
                :style="{ background: `conic-gradient(#146ce5 ${issuedRate}%, #e8eef6 0)` }"
              >
                <div><strong>{{ issuedRate }}%</strong><span>承保转化</span></div>
              </div>
              <div class="health-details">
                <div><span>已生成保单</span><b>{{ dashboard?.policy_count ?? 0 }}</b></div>
                <div><span>订单总量</span><b>{{ dashboard?.order_count ?? 0 }}</b></div>
                <div><span>平均保费</span><b>{{ formatMoney(averagePremium) }}</b></div>
              </div>
            </div>
            <div class="status-bars">
              <div v-for="item in orderStatusSummary" :key="item.status">
                <span>{{ statusLabel(item.status) }}</span>
                <i><b :style="{ width: `${Math.round((item.count / orders.length) * 100)}%` }"></b></i>
                <strong>{{ item.count }}</strong>
              </div>
            </div>
          </article>
        </section>
      </div>

      <div v-else-if="activeView === 'agent'" class="page-view agent-workspace view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">OPERATION AGENT · READ ONLY</p>
            <h1>运营智能体</h1>
            <p>通过受控只读工具分析订单、保单、产品和推广数据，权限范围与当前账号一致。</p>
          </div>
          <span class="agent-security"><i></i> 只读模式</span>
        </section>

        <nav class="agent-workspace-tabs" role="tablist" aria-label="运营智能体功能">
          <button
            type="button"
            role="tab"
            :aria-selected="agentWorkspaceTab === 'analysis'"
            :class="{ active: agentWorkspaceTab === 'analysis' }"
            @click="selectAgentWorkspaceTab('analysis')"
          >
            <span class="agent-tab-icon">✦</span>
            <span><strong>智能分析</strong><small>对话与数据洞察</small></span>
          </button>
          <button
            type="button"
            role="tab"
            :aria-selected="agentWorkspaceTab === 'runs'"
            :class="{ active: agentWorkspaceTab === 'runs' }"
            @click="selectAgentWorkspaceTab('runs')"
          >
            <span class="agent-tab-icon">⌁</span>
            <span><strong>运行中心</strong><small>Run 与分析链路</small></span>
            <b>{{ agentRuns.length }}</b>
          </button>
        </nav>

        <section v-if="agentWorkspaceTab === 'analysis'" class="operation-agent-panel">
          <aside class="agent-suggestions agent-thread-sidebar">
            <header class="thread-sidebar-heading">
              <div>
                <p class="eyebrow">CONVERSATIONS</p>
                <h2>历史会话</h2>
              </div>
              <button type="button" @click="startNewOperationThread">
                <span>＋</span> 新建
              </button>
            </header>
            <div v-if="operationHistoryLoading && !operationThreads.length" class="thread-list-state">
              正在加载历史会话…
            </div>
            <div v-else-if="!operationThreads.length" class="thread-list-state">
              还没有历史会话
            </div>
            <div v-else class="operation-thread-list">
              <button
                v-for="thread in operationThreads"
                :key="thread.id"
                type="button"
                :class="{ active: operationThreadId === thread.id }"
                @click="selectOperationThread(thread.id)"
              >
                <span class="thread-status-dot"></span>
                <span class="thread-copy">
                  <strong>{{ thread.title }}</strong>
                  <small>{{ thread.last_message?.content ?? "尚未开始分析" }}</small>
                  <time>{{ formatDate(thread.updated_at, true) }} · {{ thread.message_count }} 条消息</time>
                </span>
              </button>
            </div>
            <section class="quick-analysis-block">
              <p class="eyebrow">QUICK ANALYSIS</p>
              <div>
                <button @click="operationInput = '请分析当前订单、有效保单和累计保费情况。'">
                  经营总览
                </button>
                <button @click="operationInput = '查看最近5笔订单，并总结状态分布。'">
                  近期订单
                </button>
                <button @click="operationInput = '分析推广员的访问、线索、归因订单和保费。'">
                  推广业绩
                </button>
              </div>
            </section>
            <div class="agent-boundary-note">
              <strong>权限边界</strong>
              <p>历史会话仅当前账号可见 · {{ profile.data_scope }} 数据范围</p>
            </div>
          </aside>

          <article class="agent-conversation">
            <header>
              <span class="agent-avatar">AI</span>
              <div>
                <strong>{{ operationThreads.find((item) => item.id === operationThreadId)?.title ?? "新的运营分析" }}</strong>
                <small><i></i> 智保通运营助手 · 已连接业务数据工具</small>
              </div>
            </header>
            <div class="operation-messages" aria-live="polite">
              <div v-if="operationMessages.length === 0" class="agent-empty">
                <span>◇</span>
                <h2>从一个业务问题开始</h2>
                <p>我会根据当前账号权限调用确定性工具，并把结论和数据依据一起返回。</p>
              </div>
              <section
                v-for="(message, index) in operationMessages"
                :key="index"
                :class="['operation-message', message.role]"
              >
                <div class="message-label">{{ message.role === "assistant" ? "运营助手" : "你" }}</div>
                <p v-if="message.role === 'user'">{{ message.text }}</p>
                <div v-else class="agent-report">
                  <header class="agent-report-heading">
                    <span>AI ANALYSIS</span>
                    <small><i></i> 基于实时业务数据</small>
                  </header>
                  <template
                    v-for="(reportBlock, blockIndex) in parseAgentReport(message.text)"
                    :key="`${index}-report-${blockIndex}`"
                  >
                    <h2
                      v-if="reportBlock.kind === 'heading'"
                      :class="`report-heading level-${reportBlock.level}`"
                      v-html="agentInlineHtml(reportBlock.text)"
                    ></h2>
                    <p
                      v-else-if="reportBlock.kind === 'paragraph'"
                      :class="['report-paragraph', { emphasized: reportBlock.emphasized }]"
                      v-html="agentInlineHtml(reportBlock.text)"
                    ></p>
                    <component
                      :is="reportBlock.ordered ? 'ol' : 'ul'"
                      v-else-if="reportBlock.kind === 'list'"
                      class="report-list"
                    >
                      <li
                        v-for="(item, itemIndex) in reportBlock.items"
                        :key="itemIndex"
                        v-html="agentInlineHtml(item)"
                      ></li>
                    </component>
                    <div
                      v-else-if="reportBlock.kind === 'table'"
                      class="report-table-wrap"
                    >
                      <table class="report-table">
                        <thead>
                          <tr>
                            <th
                              v-for="(header, headerIndex) in reportBlock.headers"
                              :key="headerIndex"
                              v-html="agentInlineHtml(header)"
                            ></th>
                          </tr>
                        </thead>
                        <tbody>
                          <tr v-for="(row, rowIndex) in reportBlock.rows" :key="rowIndex">
                            <td
                              v-for="(cell, cellIndex) in row"
                              :key="cellIndex"
                              v-html="agentInlineHtml(cell)"
                            ></td>
                          </tr>
                        </tbody>
                      </table>
                    </div>
                    <hr v-else class="report-divider" />
                  </template>
                </div>
                <div v-if="message.cards?.length" class="agent-evidence-label">
                  <span>DATA EVIDENCE</span>
                  <small>工具返回的确定性数据</small>
                </div>
                <div v-for="block in message.cards" :key="`${index}-${block.type}-${block.title}`">
                  <div v-if="block.type === 'metrics'" class="agent-metrics">
                    <article v-for="metric in agentMetricItems(block)" :key="metric.label">
                      <small>{{ metric.label }}</small>
                      <strong>{{ metric.value }}<em>{{ metric.unit }}</em></strong>
                      <span v-if="metric.trend">{{ metric.trend }}</span>
                    </article>
                  </div>
                  <div v-else-if="block.type === 'business_list'" class="agent-business-list">
                    <header><strong>{{ block.title }}</strong><small>{{ block.total ?? 0 }} 条</small></header>
                    <div v-for="(item, itemIndex) in block.items" :key="itemIndex">
                      <strong>{{ agentBusinessValue(item, "order_no", "policy_no", "product_name") }}</strong>
                      <span>{{ agentBusinessValue(item, "product_name", "plan_name", "status") }}</span>
                      <small>{{ agentBusinessValue(item, "status", "amount_cents", "premium_cents") }}</small>
                    </div>
                  </div>
                  <section v-else-if="block.type === 'chart'" class="agent-chart">
                    <header>
                      <strong>{{ block.title }}</strong>
                      <small>分析库 · 只读指标</small>
                    </header>
                    <div
                      v-for="point in block.points"
                      :key="point.label"
                      class="agent-chart-row"
                    >
                      <span>{{ point.label }}</span>
                      <i>
                        <b :style="{ width: agentChartWidth(block, point.value) }"></b>
                      </i>
                      <strong>{{ point.value }}{{ block.unit }}</strong>
                    </div>
                    <p v-if="!block.points?.length">当前筛选范围暂无数据</p>
                  </section>
                </div>
                <div v-if="message.actions?.length" class="operation-actions">
                  <button
                    v-for="action in message.actions.filter((item) => item.type !== 'none')"
                    :key="`${action.type}-${action.label}`"
                    @click="executeOperationAction(action)"
                  >
                    {{ action.label }} →
                  </button>
                </div>
                <div
                  v-if="message.role === 'assistant' && message.tools?.length"
                  class="agent-tool-chips"
                >
                  <span>分析依据</span>
                  <b v-for="tool in message.tools" :key="tool">
                    {{ agentToolLabel(tool) }}
                  </b>
                </div>
                <footer v-if="message.role === 'assistant'" class="agent-proof">
                  <span v-if="message.tools?.length">工具：{{ message.tools.join(" · ") }}</span>
                  <a
                    v-if="message.traceUrl"
                    :href="message.traceUrl"
                    target="_blank"
                    rel="noopener noreferrer"
                  >查看 LangSmith Trace ↗</a>
                </footer>
              </section>
              <div v-if="operationLoading" class="agent-thinking">
                <i></i><i></i><i></i><span>正在调用业务工具并生成分析</span>
              </div>
            </div>
            <form class="operation-composer" @submit.prevent="sendOperationMessage">
              <textarea
                v-model="operationInput"
                rows="3"
                placeholder="例如：分析最近订单并告诉我需要关注什么"
              ></textarea>
              <button type="submit" :disabled="operationLoading || !operationInput.trim()">
                {{ operationLoading ? "分析中…" : "发送分析" }}
              </button>
            </form>
          </article>
        </section>
        <section v-else class="agent-run-center agent-run-workspace">
          <div class="panel-heading">
            <div>
              <h2>智能体运行中心</h2>
              <p>独立查看每次运营分析的状态、本地 Run 和 LangSmith Trace。</p>
            </div>
            <button type="button" class="sync-label" @click="loadAgentRuns">刷新</button>
          </div>
          <div class="agent-run-summary">
            <article>
              <small>RECENT RUNS</small>
              <strong>{{ agentRuns.length }}</strong>
              <span>最近运行</span>
            </article>
            <article>
              <small>COMPLETED</small>
              <strong>{{ completedAgentRuns }}</strong>
              <span>成功完成</span>
            </article>
            <article>
              <small>TRACE LINKED</small>
              <strong>{{ tracedAgentRuns }}</strong>
              <span>链路已关联</span>
            </article>
          </div>
          <div v-if="agentRuns.length === 0" class="empty-state">暂无运行记录</div>
          <div v-else class="agent-run-list">
            <article v-for="(run, runIndex) in agentRuns" :key="run.id">
              <span class="agent-run-index">RUN {{ String(runIndex + 1).padStart(2, "0") }}</span>
              <div>
                <strong>{{ run.input.text ?? "运营分析" }}</strong>
                <small>{{ run.id }} · {{ formatDate(run.created_at, true) }}</small>
              </div>
              <span class="status-pill" :class="statusTone(run.status)">
                {{ statusLabel(run.status) }}
              </span>
              <code>{{ run.trace_id ?? "等待 Trace" }}</code>
              <a
                v-if="run.trace_url"
                :href="run.trace_url"
                target="_blank"
                rel="noopener noreferrer"
              >打开 LangSmith</a>
              <span v-else>Trace 同步中</span>
            </article>
          </div>
        </section>
        <p v-if="error" class="login-error" role="alert"><span>!</span>{{ error }}</p>
      </div>

      <div v-else-if="activeView === 'orders'" class="page-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">ORDER MANAGEMENT</p>
            <h1>订单中心</h1>
            <p>查看投保人、保障方案、支付状态与订单全链路详情。</p>
          </div>
          <span class="page-count">{{ orders.length }}<small>全部订单</small></span>
        </section>
        <section class="data-panel">
          <div class="panel-heading">
            <div><h2>订单列表</h2><p>点击任一订单查看完整投保与支付信息</p></div>
            <span class="sync-label"><i></i> 已同步</span>
          </div>
          <div v-if="orders.length === 0" class="empty-state">暂无订单数据</div>
          <div v-else class="data-table orders-table">
            <div class="table-head">
              <span>订单 / 保障产品</span><span>投保关系</span><span>创建时间</span>
              <span>状态</span><span>订单金额</span><span>操作</span>
            </div>
            <button
              v-for="item in orders"
              :key="item.order_id"
              type="button"
              class="table-row"
              @click="openOrder(item)"
            >
              <span class="primary-cell">
                <strong>{{ item.order_no }}</strong>
                <small>{{ item.product_name }} · {{ item.plan_name }}</small>
              </span>
              <span class="person-cell">
                <strong>{{ relationshipLabel(item.relationship) }}</strong>
                <small>{{ item.applicant.name ?? "—" }} / {{ item.insured.name ?? "—" }}</small>
              </span>
              <time>{{ formatDate(item.created_at, true) }}</time>
              <span class="status-pill" :class="statusTone(item.status)">{{ statusLabel(item.status) }}</span>
              <b>{{ formatMoney(item.amount_cents) }}</b>
              <span class="detail-link">查看详情 →</span>
            </button>
          </div>
        </section>
      </div>

      <div v-else-if="activeView === 'policies'" class="page-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">POLICY MANAGEMENT</p>
            <h1>保单中心</h1>
            <p>集中查看保障对象、责任期间与电子保单详情。</p>
          </div>
          <span class="page-count">{{ policies.length }}<small>有效保单</small></span>
        </section>
        <section class="data-panel">
          <div class="panel-heading">
            <div><h2>保单列表</h2><p>点击任一保单查看完整承保信息</p></div>
            <span class="sync-label"><i></i> 已同步</span>
          </div>
          <div v-if="policies.length === 0" class="empty-state">暂无保单数据</div>
          <div v-else class="data-table policies-table">
            <div class="table-head">
              <span>保单 / 保障产品</span><span>被保人</span><span>保障期间</span>
              <span>状态</span><span>承保保费</span><span>操作</span>
            </div>
            <button
              v-for="item in policies"
              :key="item.policy_id"
              type="button"
              class="table-row"
              @click="openPolicy(item)"
            >
              <span class="primary-cell">
                <strong>{{ item.policy_no }}</strong>
                <small>{{ item.product_name }} · {{ item.plan_name }}</small>
              </span>
              <span class="person-cell">
                <strong>{{ item.insured.name ?? "—" }}</strong>
                <small>{{ relationshipLabel(item.relationship) }} · {{ maskIdNo(item.insured.id_no) }}</small>
              </span>
              <time>{{ formatDate(item.coverage_start) }} — {{ formatDate(item.coverage_end) }}</time>
              <span class="status-pill" :class="statusTone(item.status)">{{ statusLabel(item.status) }}</span>
              <b>{{ formatMoney(item.premium_cents) }}</b>
              <span class="detail-link">查看详情 →</span>
            </button>
          </div>
        </section>
      </div>

      <div v-else-if="activeView === 'services'" class="page-view service-center-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">POLICY SERVICE OPERATIONS</p>
            <h1>服务申请中心</h1>
            <p>围绕有效保单处理用户提交的医疗费用报销协助申请，并同步反馈处理意见。</p>
          </div>
          <span class="page-count">{{ serviceRequests.length }}<small>全部申请</small></span>
        </section>

        <section class="service-pulse">
          <article>
            <span>01</span>
            <div><small>待处理</small><strong>{{ pendingServiceCount }}</strong></div>
            <i class="pending"></i>
          </article>
          <article>
            <span>02</span>
            <div><small>已受理</small><strong>{{ acceptedServiceCount }}</strong></div>
            <i class="accepted"></i>
          </article>
          <article>
            <span>03</span>
            <div><small>未受理</small><strong>{{ rejectedServiceCount }}</strong></div>
            <i class="rejected"></i>
          </article>
          <article>
            <span>04</span>
            <div><small>处理完成率</small><strong>{{
              serviceRequests.length
                ? Math.round(((serviceRequests.length - pendingServiceCount) / serviceRequests.length) * 100)
                : 0
            }}%</strong></div>
            <i class="rate"></i>
          </article>
        </section>

        <section class="data-panel service-data-panel">
          <div class="panel-heading service-panel-heading">
            <div><h2>服务申请列表</h2><p>点击申请查看关联保单、票据信息与处理详情</p></div>
            <div class="service-toolbar">
              <label>
                <svg viewBox="0 0 24 24" aria-hidden="true">
                  <path d="m20 20-4.2-4.2M18 11a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z" />
                </svg>
                <input
                  v-model.trim="serviceSearch"
                  placeholder="申请号、保单号、客户或医院"
                />
              </label>
              <select v-model="serviceStatusFilter">
                <option value="ALL">全部状态</option>
                <option value="SUBMITTED">待处理</option>
                <option value="ACCEPTED">已受理</option>
                <option value="REJECTED">未受理</option>
              </select>
              <button type="button" @click="loadServiceRequests">刷新</button>
            </div>
          </div>
          <div v-if="filteredServiceRequests.length === 0" class="empty-state">
            暂无符合条件的服务申请
          </div>
          <div v-else class="data-table service-table">
            <div class="table-head">
              <span>申请 / 服务类型</span><span>关联保单</span><span>客户与被保人</span>
              <span>申请金额</span><span>提交时间</span><span>状态</span><span>操作</span>
            </div>
            <button
              v-for="item in filteredServiceRequests"
              :key="item.request_id"
              type="button"
              class="table-row"
              @click="openServiceRequest(item)"
            >
              <span class="primary-cell">
                <strong>{{ item.request_no }}</strong>
                <small>{{ serviceTypeLabel(item.request_type) }}</small>
              </span>
              <span class="primary-cell">
                <strong>{{ policyForService(item)?.policy_no ?? item.policy_id }}</strong>
                <small>{{ orderForService(item)?.product_name ?? "关联保单" }}</small>
              </span>
              <span class="person-cell">
                <strong>{{ orderForService(item)?.insured.name ?? customerForService(item)?.display_name ?? "客户" }}</strong>
                <small>{{ maskMobile(orderForService(item)?.contact.mobile ?? customerForService(item)?.mobile) }}</small>
              </span>
              <b>¥{{ item.confirmed_fields.amount_yuan ?? "0.00" }}</b>
              <time>{{ formatDate(item.submitted_at, true) }}</time>
              <span class="status-pill" :class="statusTone(item.status)">{{ statusLabel(item.status) }}</span>
              <span class="detail-link">处理详情 →</span>
            </button>
          </div>
        </section>
      </div>

      <div v-else-if="activeView === 'products'" class="page-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">PRODUCT GOVERNANCE</p>
            <h1>产品管理</h1>
            <p>创建保险产品，维护版本与保障计划，并控制客户侧销售状态。</p>
          </div>
          <div class="product-heading-actions">
            <button
              v-if="hasPermission('product:write')"
              type="button"
              @click="openProductCreator"
            >
              ＋ 新增产品
            </button>
            <span class="page-count">{{ managedProducts.length }}<small>保险产品</small></span>
          </div>
        </section>
        <section class="product-admin-grid">
          <article v-for="product in managedProducts" :key="product.product_id">
            <header>
              <span>{{ product.category === "MEDICAL" ? "医疗保障" : "意外保障" }}</span>
              <em :class="{ inactive: product.status !== 'ACTIVE' }">
                {{ product.status === "ACTIVE" ? "在售" : "已停用" }}
              </em>
            </header>
            <h2>{{ product.name }}</h2>
            <p>{{ product.summary }}</p>
            <div class="product-version-list">
              <div v-for="version in product.versions" :key="version.id">
                <strong>V{{ version.version_no }}</strong>
                <span>{{ version.status }} · {{ version.plan_count }} 个计划</span>
                <small>{{ formatDate(version.effective_from) }} — {{ formatDate(version.effective_to) }}</small>
              </div>
            </div>
            <footer>
              <small>{{ product.product_code }}</small>
              <div>
                <button type="button" class="product-manage-button" @click="openProductEditor(product)">
                  管理产品
                </button>
                <button
                  type="button"
                  :disabled="!hasPermission('product:write') || productSavingId === product.product_id"
                  @click="changeProductStatus(product)"
                >
                  {{
                    productSavingId === product.product_id
                      ? "正在更新…"
                      : product.status === "ACTIVE"
                        ? "停止销售"
                        : "恢复销售"
                  }}
                </button>
              </div>
            </footer>
          </article>
        </section>
      </div>

      <div v-else-if="activeView === 'knowledge'" class="page-view knowledge-view view-enter">
        <section class="page-heading knowledge-heading">
          <div>
            <p class="eyebrow">KNOWLEDGE OPERATIONS</p>
            <h1>知识中心</h1>
            <p>管理保险条款与服务规则，经过索引验证和人工发布后供智能体检索。</p>
          </div>
          <span class="page-count">
            {{ publishedKnowledgeCount }}<small>已发布知识</small>
          </span>
        </section>

        <section class="knowledge-pulse">
          <article>
            <span>01</span>
            <div><small>待整理</small><strong>{{ draftKnowledgeCount }}</strong></div>
            <i></i>
          </article>
          <article>
            <span>02</span>
            <div><small>待发布</small><strong>{{ indexedKnowledgeCount }}</strong></div>
            <i></i>
          </article>
          <article>
            <span>03</span>
            <div><small>正式知识</small><strong>{{ publishedKnowledgeCount }}</strong></div>
            <i></i>
          </article>
          <div class="knowledge-flow-note">
            <b>RAG 发布流水线</b>
            <small>上传 → 切分 → BGE-M3 → Milvus → 人工发布</small>
          </div>
        </section>

        <section class="knowledge-layout">
          <form
            v-if="hasPermission('knowledge:write')"
            class="knowledge-ingest"
            @submit.prevent="uploadKnowledgeDocument"
          >
            <header>
              <div>
                <span>INGEST</span>
                <h2>知识入库</h2>
              </div>
              <b>受控发布</b>
            </header>
            <p>上传原始文档只会创建草稿，不会立即影响智能体回答。</p>
            <label class="knowledge-field">
              <span>知识标题</span>
              <input
                v-model="knowledgeTitle"
                maxlength="200"
                placeholder="例如：安心医疗险保险条款"
              />
            </label>
            <div class="knowledge-form-grid">
              <label class="knowledge-field">
                <span>内容类型</span>
                <select v-model="knowledgeDocumentType">
                  <option value="INSURANCE_TERMS">保险条款</option>
                  <option value="PRODUCT_GUIDE">产品说明</option>
                  <option value="SERVICE_RULES">服务规则</option>
                  <option value="FAQ">常见问题</option>
                </select>
              </label>
              <label class="knowledge-field">
                <span>关联产品</span>
                <input
                  v-model="knowledgeProductCode"
                  maxlength="64"
                  placeholder="选填，如 MED-BASIC"
                />
              </label>
            </div>
            <label class="knowledge-dropzone">
              <input
                type="file"
                accept=".pdf,.md,.txt"
                @change="chooseKnowledgeFile"
              />
              <span class="knowledge-file-icon">↥</span>
              <strong>{{
                knowledgeUploadFile?.name ?? "选择知识文档"
              }}</strong>
              <small>TXT / Markdown / PDF，单个文件不超过 10MB</small>
            </label>
            <button
              class="knowledge-primary"
              type="submit"
              :disabled="
                knowledgeUploading ||
                !knowledgeTitle.trim() ||
                !knowledgeUploadFile
              "
            >
              {{ knowledgeUploading ? "正在上传…" : "上传为知识草稿" }}
              <span>→</span>
            </button>
            <p v-if="knowledgeFeedback" class="knowledge-feedback">
              <i></i>{{ knowledgeFeedback }}
            </p>
          </form>

          <section class="knowledge-library">
            <header>
              <div>
                <p class="eyebrow">PUBLISHED SOURCE CONTROL</p>
                <h2>知识资产</h2>
              </div>
              <button type="button" @click="loadKnowledgeDocuments">刷新</button>
            </header>

            <section v-if="knowledgeTestDocument" class="knowledge-search-test">
              <header>
                <div>
                  <span>RETRIEVAL LAB</span>
                  <strong>检索测试 · {{ knowledgeTestDocument.title }}</strong>
                </div>
                <button type="button" aria-label="关闭检索测试" @click="closeKnowledgeSearchTest">
                  ×
                </button>
              </header>
              <form @submit.prevent="runKnowledgeSearchTest">
                <input
                  v-model="knowledgeTestQuery"
                  maxlength="500"
                  placeholder="输入用户可能提出的问题，例如：医疗险等待期是多少天？"
                />
                <button
                  type="submit"
                  :disabled="knowledgeTestRunning || !knowledgeTestQuery.trim()"
                >
                  {{ knowledgeTestRunning ? "检索中…" : "执行混合检索" }}
                </button>
              </form>
              <p v-if="knowledgeTestFeedback" class="knowledge-search-feedback">
                <i></i>{{ knowledgeTestFeedback }}
              </p>
              <div v-if="knowledgeTestResults.length" class="knowledge-search-results">
                <article
                  v-for="(hit, hitIndex) in knowledgeTestResults"
                  :key="hit.chunk_id"
                >
                  <span>{{ String(hitIndex + 1).padStart(2, "0") }}</span>
                  <div>
                    <header>
                      <strong>Chunk {{ hit.ordinal }}</strong>
                      <small>相关度 {{ (hit.score * 100).toFixed(1) }}%</small>
                    </header>
                    <p>{{ hit.content }}</p>
                    <footer>
                      {{ hit.source_name }} · V{{ hit.document_version }}
                    </footer>
                  </div>
                </article>
              </div>
            </section>

            <div v-if="knowledgeDocuments.length" class="knowledge-document-list">
              <article
                v-for="document in knowledgeDocuments"
                :key="document.document_id"
                class="knowledge-document"
              >
                <div class="knowledge-document-index">
                  {{ String(document.version_no).padStart(2, "0") }}
                </div>
                <div class="knowledge-document-main">
                  <header>
                    <span>{{ knowledgeTypeLabel(document.document_type) }}</span>
                    <em :class="`knowledge-status ${document.status.toLowerCase()}`">
                      {{ statusLabel(document.status) }}
                    </em>
                  </header>
                  <h3>{{ document.title }}</h3>
                  <p>{{ document.excerpt }}</p>
                  <footer>
                    <span>{{ document.source_name }}</span>
                    <span>V{{ document.version_no }}</span>
                    <span>{{ document.chunk_count }} Chunks</span>
                    <span>
                      {{
                        document.search_test_count
                          ? `检索测试 ${document.search_test_count} 次`
                          : "尚未检索测试"
                      }}
                    </span>
                    <span>{{ formatDate(document.updated_at, true) }}</span>
                  </footer>
                </div>
                <div class="knowledge-document-action">
                  <small>{{ document.product_code || "通用知识" }}</small>
                  <button
                    v-if="
                      hasPermission('knowledge:write') &&
                      ['DRAFT', 'INACTIVE'].includes(document.status)
                    "
                    type="button"
                    :disabled="Boolean(knowledgeActionId)"
                    @click="executeKnowledgeAction(document, 'index')"
                  >
                    {{
                      knowledgeActionId === document.document_id
                        ? "索引中…"
                        : "生成索引"
                    }}
                  </button>
                  <button
                    v-if="
                      hasPermission('knowledge:write') &&
                      ['INDEXED', 'PUBLISHED'].includes(document.status)
                    "
                    type="button"
                    :disabled="Boolean(knowledgeActionId)"
                    @click="openKnowledgeSearchTest(document)"
                  >
                    检索测试
                  </button>
                  <button
                    v-if="
                      hasPermission('knowledge:publish') &&
                      document.status === 'INDEXED'
                    "
                    type="button"
                    class="publish"
                    :disabled="
                      Boolean(knowledgeActionId) ||
                      document.last_search_test_passed !== true
                    "
                    @click="executeKnowledgeAction(document, 'publish')"
                  >
                    {{
                      document.last_search_test_passed === true
                        ? "发布知识"
                        : "测试通过后发布"
                    }}
                  </button>
                  <button
                    v-if="
                      hasPermission('knowledge:publish') &&
                      document.status === 'PUBLISHED'
                    "
                    type="button"
                    class="disable"
                    :disabled="Boolean(knowledgeActionId)"
                    @click="executeKnowledgeAction(document, 'disable')"
                  >
                    停用
                  </button>
                  <span
                    v-if="
                      !hasPermission('knowledge:write') &&
                      !hasPermission('knowledge:publish')
                    "
                    class="knowledge-readonly"
                  >只读</span>
                </div>
              </article>
            </div>
            <div v-else class="knowledge-empty">
              <span>◇</span>
              <h3>还没有知识资产</h3>
              <p>上传第一份保险条款，建立可追溯的智能体知识来源。</p>
            </div>
          </section>
        </section>
      </div>

      <div v-else-if="activeView === 'attribution'" class="page-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">PROMOTION ATTRIBUTION</p>
            <h1>推广归因</h1>
            <p>按首次有效推广触点统计访问、线索、归因订单与保费贡献。</p>
          </div>
          <span class="page-count">{{ attributionOverview?.salesperson_count ?? 0 }}<small>推广人员</small></span>
        </section>
        <section class="attribution-metrics">
          <article><small>推广访问</small><strong>{{ attributionOverview?.visit_count ?? 0 }}</strong><span>访问转线索 {{ attributionOverview?.visit_conversion_rate ?? 0 }}%</span></article>
          <article><small>有效线索</small><strong>{{ attributionOverview?.lead_count ?? 0 }}</strong><span>线索转订单 {{ attributionOverview?.lead_conversion_rate ?? 0 }}%</span></article>
          <article><small>归因订单</small><strong>{{ attributionOverview?.order_count ?? 0 }}</strong><span>平均保费 {{ formatMoney(attributionOverview?.average_premium_cents ?? 0) }}</span></article>
          <article><small>归因保费</small><strong>{{ formatMoney(attributionOverview?.premium_cents ?? 0) }}</strong><span>订单保费贡献</span></article>
        </section>
        <section class="attribution-layout">
          <article class="data-panel attribution-ranking">
            <div class="panel-heading">
              <div><h2>推广员业绩</h2><p>当前数据范围：{{ profile.data_scope }}</p></div>
              <span class="sync-label"><i></i> 实时统计</span>
            </div>
            <div class="attribution-table">
              <div class="table-head">
                <span>推广员</span><span>访问 / 线索</span><span>订单 / 转化</span><span>保费 / 客单价</span>
              </div>
              <div v-for="item in attributionOverview?.items ?? []" :key="item.salesperson_id" class="table-row">
                <span><strong>{{ item.salesperson_name }}</strong><small>{{ item.salesperson_code }}</small></span>
                <span><strong>{{ item.visit_count }}</strong><small>{{ item.lead_count }} 人</small></span>
                <span><strong>{{ item.order_count }} 笔</strong><small>线索转化 {{ item.lead_conversion_rate }}%</small></span>
                <span><strong>{{ formatMoney(item.premium_cents) }}</strong><small>客单价 {{ formatMoney(item.average_premium_cents) }}</small></span>
              </div>
            </div>
          </article>
          <aside class="promotion-code-panel">
            <div><p class="eyebrow">PROMOTION LINKS</p><h2>推广码</h2></div>
            <article v-for="item in promotionCodes" :key="item.code">
              <span>有效推广码</span>
              <strong>{{ item.code }}</strong>
              <p>{{ item.salesperson_name }} · {{ promotionChannelLabel(item.channel) }}</p>
              <code>http://127.0.0.1:5173/?ref={{ item.code }}</code>
            </article>
          </aside>
        </section>
      </div>

      <div v-else-if="activeView === 'users'" class="page-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">USER DIRECTORY</p>
            <h1>用户维护</h1>
            <p>维护后台操作账号与投保客户状态，角色权限由统一权限体系控制。</p>
          </div>
          <span class="page-count">{{ adminUsers.length + customers.length }}<small>用户总数</small></span>
        </section>

        <section class="user-toolbar">
          <div class="user-tabs">
            <button
              type="button"
              :class="{ active: userSection === 'admins' }"
              @click="userSection = 'admins'"
            >
              后台账号 <span>{{ adminUsers.length }}</span>
            </button>
            <button
              type="button"
              :class="{ active: userSection === 'customers' }"
              @click="userSection = 'customers'"
            >
              投保客户 <span>{{ customers.length }}</span>
            </button>
          </div>
          <button
            v-if="userSection === 'admins' && hasPermission('user:write')"
            type="button"
            class="create-account-button"
            @click="openCreateAdmin"
          >
            <span>＋</span> 新增后台账号
          </button>
        </section>

        <section v-if="userSection === 'admins'" class="data-panel user-panel">
          <div class="panel-heading">
            <div><h2>后台账号</h2><p>账号状态、角色和数据范围在下次请求时立即生效</p></div>
            <span class="sync-label"><i></i> {{ adminUsers.length }} 个账号</span>
          </div>
          <div class="user-table">
            <div class="user-table-head">
              <span>账号信息</span><span>角色</span><span>数据范围</span><span>状态</span><span>操作</span>
            </div>
            <div v-for="user in adminUsers" :key="user.id" class="user-table-row">
              <div class="user-identity">
                <span>{{ user.display_name.slice(0, 1) }}</span>
                <div><strong>{{ user.display_name }}</strong><small>{{ user.username }}</small></div>
              </div>
              <span class="role-chip">{{ user.roles[0] ?? "—" }}</span>
              <code>{{ user.data_scope }}</code>
              <span class="account-status" :class="{ disabled: user.status !== 'ACTIVE' }">
                <i></i>{{ accountStatusLabel(user.status) }}
              </span>
              <button
                type="button"
                class="maintain-button"
                :disabled="!hasPermission('user:write')"
                @click="openEditAdmin(user)"
              >
                维护
              </button>
            </div>
          </div>
        </section>

        <section v-else class="data-panel user-panel">
          <div class="panel-heading">
            <div><h2>投保客户</h2><p>客户状态被停用后将无法继续登录 H5 端</p></div>
            <span class="sync-label"><i></i> {{ customers.length }} 位客户</span>
          </div>
          <div class="user-table customer-table">
            <div class="user-table-head">
              <span>客户信息</span><span>联系电话</span><span>注册时间</span><span>状态</span><span>操作</span>
            </div>
            <div v-for="customer in customers" :key="customer.id" class="user-table-row">
              <div class="user-identity">
                <span>{{ customer.display_name.slice(0, 1) }}</span>
                <div><strong>{{ customer.display_name }}</strong><small>{{ customer.id }}</small></div>
              </div>
              <code>{{ customer.mobile_masked }}</code>
              <time>{{ formatDate(customer.created_at) }}</time>
              <span class="account-status" :class="{ disabled: customer.status !== 'ACTIVE' }">
                <i></i>{{ accountStatusLabel(customer.status) }}
              </span>
              <button
                type="button"
                class="maintain-button"
                :disabled="!hasPermission('user:write')"
                @click="customerToToggle = customer"
              >
                {{ customer.status === "ACTIVE" ? "停用" : "启用" }}
              </button>
            </div>
          </div>
        </section>
      </div>

      <div v-else class="page-view view-enter">
        <section class="page-heading">
          <div>
            <p class="eyebrow">AUTHORIZATION CONTROL</p>
            <h1>角色权限</h1>
            <p>为不同业务角色配置数据范围与可访问能力，修改后立即写入权限库。</p>
          </div>
          <span class="page-count">{{ roles.length }}<small>业务角色</small></span>
        </section>
        <section class="role-summary">
          <article><small>角色数量</small><strong>{{ roles.length }}</strong><span>已启用角色</span></article>
          <article><small>权限能力</small><strong>{{ permissionCatalog.length }}</strong><span>可配置权限项</span></article>
          <article><small>数据范围</small><strong>{{ dataScopes.length }}</strong><span>分级数据边界</span></article>
        </section>
        <section class="role-manager">
          <aside class="role-list">
            <div class="role-list-heading">
              <p class="eyebrow">ROLE DIRECTORY</p>
              <h2>角色目录</h2>
            </div>
            <button
              v-for="role in roles"
              :key="role.code"
              type="button"
              :class="{ active: selectedRoleCode === role.code }"
              @click="chooseRole(role)"
            >
              <span>{{ role.name.slice(0, 1) }}</span>
              <div><strong>{{ role.name }}</strong><small>{{ role.code }}</small></div>
              <i v-if="role.protected">锁定</i>
              <b v-else>{{ role.permissions.length }}</b>
            </button>
          </aside>

          <article v-if="selectedRole" class="role-editor">
            <div class="role-editor-heading">
              <div>
                <p class="eyebrow">PERMISSION MATRIX</p>
                <h2>{{ selectedRole.name }}</h2>
                <p>{{ selectedRole.code }} · {{ selectedRole.status }}</p>
              </div>
              <span v-if="selectedRole.protected" class="protected-badge">系统保护角色</span>
              <span v-else class="editable-badge"><i></i> 可编辑</span>
            </div>

            <div class="scope-editor">
              <div>
                <strong>数据范围</strong>
                <p>控制该角色能够读取的业务数据边界</p>
              </div>
              <select
                v-model="editableDataScope"
                :disabled="selectedRole.protected || !canManageRoles"
              >
                <option v-for="scope in dataScopes" :key="scope.code" :value="scope.code">
                  {{ scope.name }}（{{ scope.code }}）
                </option>
              </select>
            </div>

            <div class="permission-editor">
              <div class="permission-editor-heading">
                <div><strong>功能权限</strong><p>勾选该角色可以访问的后台能力</p></div>
                <span>{{ editablePermissions.length }} / {{ permissionCatalog.length }}</span>
              </div>
              <div class="permission-matrix">
                <button
                  v-for="permission in permissionCatalog"
                  :key="permission.code"
                  type="button"
                  :class="{ selected: editablePermissions.includes(permission.code) }"
                  :disabled="selectedRole.protected || !canManageRoles"
                  @click="togglePermission(permission.code)"
                >
                  <i>{{ editablePermissions.includes(permission.code) ? "✓" : "" }}</i>
                  <span><strong>{{ permission.name }}</strong><small>{{ permission.group }} · {{ permission.code }}</small></span>
                </button>
              </div>
            </div>

            <footer class="role-editor-footer">
              <p v-if="roleMessage" :class="{ success: roleMessage.includes('已保存') }">
                {{ roleMessage }}
              </p>
              <p v-else-if="selectedRole.protected">超级管理员拥有全部权限，为避免系统失去管理入口不可修改。</p>
              <p v-else>权限变更将影响所有绑定该角色的后台账号。</p>
              <button
                type="button"
                :disabled="!roleDirty || roleSaving || selectedRole.protected || !canManageRoles"
                @click="saveRole"
              >
                {{ roleSaving ? "正在保存…" : "保存权限配置" }}
              </button>
            </footer>
          </article>
        </section>
      </div>
    </section>

    <div v-if="selectedOrder" class="detail-backdrop" @click.self="selectedOrder = null">
      <aside class="detail-drawer" role="dialog" aria-modal="true" aria-label="订单详情">
        <header class="detail-header">
          <div><p class="eyebrow">ORDER DETAIL</p><h2>订单详情</h2></div>
          <button type="button" aria-label="关闭订单详情" @click="selectedOrder = null">×</button>
        </header>
        <section class="detail-hero order-detail-hero">
          <div>
            <span class="status-pill" :class="statusTone(selectedOrder.status)">
              {{ statusLabel(selectedOrder.status) }}
            </span>
            <h3>{{ selectedOrder.product_name }}</h3>
            <p>{{ selectedOrder.plan_name }}</p>
          </div>
          <strong>{{ formatMoney(selectedOrder.amount_cents) }}</strong>
        </section>
        <section class="detail-section">
          <p class="detail-section-title">交易信息</p>
          <dl class="detail-grid">
            <div><dt>订单编号</dt><dd>{{ selectedOrder.order_no }}</dd></div>
            <div><dt>订单 ID</dt><dd>{{ selectedOrder.order_id }}</dd></div>
            <div><dt>创建时间</dt><dd>{{ formatDate(selectedOrder.created_at, true) }}</dd></div>
            <div><dt>关联保单</dt><dd>{{ selectedOrder.policy_no ?? "尚未生成" }}</dd></div>
          </dl>
        </section>
        <section class="detail-section">
          <p class="detail-section-title">投保关系</p>
          <dl class="detail-grid">
            <div><dt>投保关系</dt><dd>{{ relationshipLabel(selectedOrder.relationship) }}</dd></div>
            <div><dt>投保人</dt><dd>{{ selectedOrder.applicant.name ?? "—" }}</dd></div>
            <div><dt>投保人证件</dt><dd>{{ maskIdNo(selectedOrder.applicant.id_no) }}</dd></div>
            <div><dt>被保人</dt><dd>{{ selectedOrder.insured.name ?? "—" }}</dd></div>
            <div><dt>被保人证件</dt><dd>{{ maskIdNo(selectedOrder.insured.id_no) }}</dd></div>
            <div><dt>联系电话</dt><dd>{{ maskMobile(selectedOrder.contact.mobile) }}</dd></div>
          </dl>
        </section>
        <section class="detail-section">
          <p class="detail-section-title">支付信息</p>
          <dl v-if="selectedOrder.payment" class="detail-grid">
            <div><dt>支付流水</dt><dd>{{ selectedOrder.payment.payment_no }}</dd></div>
            <div><dt>支付状态</dt><dd>{{ statusLabel(selectedOrder.payment.status) }}</dd></div>
            <div><dt>支付渠道</dt><dd>{{ paymentProviderLabel(selectedOrder.payment.provider) }}</dd></div>
            <div><dt>支付时间</dt><dd>{{ formatDate(selectedOrder.payment.succeeded_at, true) }}</dd></div>
          </dl>
          <div v-else class="detail-empty">当前订单尚未创建支付流水</div>
        </section>
        <section class="order-timeline">
          <div class="complete"><i>✓</i><span><strong>提交投保</strong><small>{{ formatDate(selectedOrder.created_at, true) }}</small></span></div>
          <div :class="{ complete: selectedOrder.payment?.status === 'SUCCEEDED' }"><i>✓</i><span><strong>支付完成</strong><small>{{ selectedOrder.payment ? statusLabel(selectedOrder.payment.status) : "等待支付" }}</small></span></div>
          <div :class="{ complete: selectedOrder.status === 'ISSUED' }"><i>✓</i><span><strong>保单生成</strong><small>{{ selectedOrder.policy_no ?? "等待承保" }}</small></span></div>
        </section>
      </aside>
    </div>

    <div v-if="selectedPolicy" class="detail-backdrop" @click.self="selectedPolicy = null">
      <aside class="detail-drawer" role="dialog" aria-modal="true" aria-label="保单详情">
        <header class="detail-header">
          <div><p class="eyebrow">POLICY DETAIL</p><h2>电子保单详情</h2></div>
          <button type="button" aria-label="关闭保单详情" @click="selectedPolicy = null">×</button>
        </header>
        <section class="policy-certificate">
          <div class="certificate-mark">Z</div>
          <p>ZHIBAOTONG DIGITAL POLICY</p>
          <h3>{{ selectedPolicy.product_name }}</h3>
          <span>{{ selectedPolicy.plan_name }}</span>
          <strong>{{ selectedPolicy.policy_no }}</strong>
        </section>
        <section class="detail-section">
          <p class="detail-section-title">保障信息</p>
          <dl class="detail-grid">
            <div><dt>保障状态</dt><dd><span class="status-pill" :class="statusTone(selectedPolicy.status)">{{ statusLabel(selectedPolicy.status) }}</span></dd></div>
            <div><dt>承保保费</dt><dd>{{ formatMoney(selectedPolicy.premium_cents) }}</dd></div>
            <div><dt>保障起期</dt><dd>{{ formatDate(selectedPolicy.coverage_start) }}</dd></div>
            <div><dt>保障止期</dt><dd>{{ formatDate(selectedPolicy.coverage_end) }}</dd></div>
            <div><dt>签发时间</dt><dd>{{ formatDate(selectedPolicy.issued_at, true) }}</dd></div>
            <div><dt>关联订单</dt><dd>{{ selectedPolicy.order_no }}</dd></div>
          </dl>
        </section>
        <section class="detail-section">
          <p class="detail-section-title">投保关系</p>
          <dl class="detail-grid">
            <div><dt>投保关系</dt><dd>{{ relationshipLabel(selectedPolicy.relationship) }}</dd></div>
            <div><dt>投保人</dt><dd>{{ selectedPolicy.applicant.name ?? "—" }}</dd></div>
            <div><dt>投保人证件</dt><dd>{{ maskIdNo(selectedPolicy.applicant.id_no) }}</dd></div>
            <div><dt>被保人</dt><dd>{{ selectedPolicy.insured.name ?? "—" }}</dd></div>
            <div><dt>被保人证件</dt><dd>{{ maskIdNo(selectedPolicy.insured.id_no) }}</dd></div>
          </dl>
        </section>
        <footer class="certificate-footer">
          <span><i>✓</i> 电子签章有效</span>
          <small>智保通保险业务系统 · 数据以核心承保记录为准</small>
        </footer>
      </aside>
    </div>

    <div
      v-if="selectedServiceRequest"
      class="detail-backdrop"
      @click.self="closeServiceRequest"
    >
      <aside class="detail-drawer service-detail-drawer" role="dialog" aria-modal="true" aria-label="服务申请详情">
        <header class="detail-header">
          <div><p class="eyebrow">SERVICE REQUEST DETAIL</p><h2>服务申请详情</h2></div>
          <button type="button" aria-label="关闭服务申请详情" @click="closeServiceRequest">×</button>
        </header>

        <section class="detail-hero service-detail-hero">
          <div>
            <span class="status-pill" :class="statusTone(selectedServiceRequest.status)">
              {{ statusLabel(selectedServiceRequest.status) }}
            </span>
            <h3>{{ serviceTypeLabel(selectedServiceRequest.request_type) }}</h3>
            <p>{{ selectedServiceRequest.request_no }}</p>
          </div>
          <strong>¥{{ selectedServiceRequest.confirmed_fields.amount_yuan ?? "0.00" }}</strong>
        </section>

        <section class="detail-section">
          <p class="detail-section-title">申请与保单关系</p>
          <dl class="detail-grid">
            <div><dt>申请编号</dt><dd>{{ selectedServiceRequest.request_no }}</dd></div>
            <div><dt>提交时间</dt><dd>{{ formatDate(selectedServiceRequest.submitted_at, true) }}</dd></div>
            <div><dt>保单编号</dt><dd>{{ policyForService(selectedServiceRequest)?.policy_no ?? selectedServiceRequest.policy_id }}</dd></div>
            <div><dt>保障产品</dt><dd>{{ orderForService(selectedServiceRequest)?.product_name ?? "—" }}</dd></div>
            <div><dt>被保险人</dt><dd>{{ orderForService(selectedServiceRequest)?.insured.name ?? "—" }}</dd></div>
            <div><dt>联系电话</dt><dd>{{ maskMobile(selectedServiceRequest.confirmed_fields.contact_mobile) }}</dd></div>
          </dl>
        </section>

        <section class="detail-section service-material-detail">
          <div class="detail-section-heading">
            <p class="detail-section-title">原始票据与识别审核</p>
            <span>仅运营端可见</span>
          </div>

          <div class="service-material-audit">
            <figure class="service-material-figure">
              <div class="service-material-canvas">
                <img
                  v-if="serviceMaterialPreview"
                  :src="serviceMaterialPreview"
                  :alt="selectedServiceRequest.recognition?.source_name || '用户上传的原始票据'"
                />
                <div v-else-if="serviceMaterialLoading" class="service-material-state">
                  <i></i><strong>正在读取原始票据</strong><span>材料通过受控接口加载</span>
                </div>
                <div v-else class="service-material-state">
                  <strong>{{ serviceMaterialError || "暂无原始票据" }}</strong>
                  <span>
                    {{
                      selectedServiceRequest.recognition?.material_available
                        ? "请稍后重新打开详情"
                        : "历史测试数据或旧申请可能未留存原图"
                    }}
                  </span>
                </div>
              </div>
              <figcaption>
                <span>原始上传材料</span>
                <strong>{{ selectedServiceRequest.recognition?.source_name || "未记录文件名" }}</strong>
              </figcaption>
            </figure>

            <aside class="service-recognition-audit">
              <div class="service-confidence">
                <span>AI 识别可信度</span>
                <strong>
                  {{
                    selectedServiceRequest.recognition
                      ? `${Math.round(selectedServiceRequest.recognition.confidence * 100)}%`
                      : "—"
                  }}
                </strong>
              </div>
              <div class="service-confidence-track">
                <i
                  :style="{
                    width: `${Math.round((selectedServiceRequest.recognition?.confidence ?? 0) * 100)}%`,
                  }"
                ></i>
              </div>
              <p>可信度仅用于辅助运营人员确定核对优先级，不作为是否受理的自动判断依据。</p>
              <div
                v-if="selectedServiceRequest.recognition?.warnings.length"
                class="service-audit-warnings"
              >
                <span>模型核对提示</span>
                <ul>
                  <li
                    v-for="warning in selectedServiceRequest.recognition.warnings"
                    :key="warning"
                  >
                    {{ warning }}
                  </li>
                </ul>
              </div>
              <div v-else class="service-audit-clear">
                <i>✓</i><span>模型未返回额外核对提示</span>
              </div>
            </aside>
          </div>

          <div class="service-confirmed-heading">
            <strong>用户最终确认信息</strong>
            <span>以下字段可能经过用户修改，请结合原图审核</span>
          </div>
          <dl class="detail-grid">
            <div><dt>票据号码</dt><dd>{{ selectedServiceRequest.confirmed_fields.invoice_no ?? "—" }}</dd></div>
            <div><dt>医疗机构</dt><dd>{{ selectedServiceRequest.confirmed_fields.hospital_name ?? "—" }}</dd></div>
            <div><dt>就诊日期</dt><dd>{{ selectedServiceRequest.confirmed_fields.visit_date ?? "—" }}</dd></div>
            <div><dt>申请金额</dt><dd>¥{{ selectedServiceRequest.confirmed_fields.amount_yuan ?? "0.00" }}</dd></div>
            <div><dt>材料识别 ID</dt><dd>{{ selectedServiceRequest.recognition_id }}</dd></div>
            <div><dt>客户 ID</dt><dd>{{ selectedServiceRequest.user_id }}</dd></div>
          </dl>
        </section>

        <section
          v-if="selectedServiceRequest.status === 'SUBMITTED' && hasPermission('service:review')"
          class="detail-section service-review-panel"
        >
          <div class="detail-section-heading">
            <p class="detail-section-title">业务处理</p>
            <span>处理后结果同步至 H5</span>
          </div>
          <div class="service-decision-grid">
            <label :class="{ selected: serviceReviewDecision === 'ACCEPTED' }">
              <input v-model="serviceReviewDecision" type="radio" value="ACCEPTED" />
              <span>✓</span><strong>受理申请</strong><small>材料满足初步受理条件</small>
            </label>
            <label :class="{ selected: serviceReviewDecision === 'REJECTED' }">
              <input v-model="serviceReviewDecision" type="radio" value="REJECTED" />
              <span>×</span><strong>不予受理</strong><small>说明不符合受理条件的原因</small>
            </label>
          </div>
          <p class="service-decision-hint">
            受理后不生成处理意见；不予受理时，系统统一记录“不满足赔付条件”。
          </p>
          <p v-if="serviceReviewFeedback" class="service-review-feedback">
            {{ serviceReviewFeedback }}
          </p>
          <button
            type="button"
            class="service-review-submit"
            :disabled="serviceReviewLoading"
            @click="reviewServiceRequest"
          >
            {{ serviceReviewLoading ? "正在提交处理结果…" : "确认提交处理结果" }}
          </button>
        </section>

        <section v-else class="detail-section service-result-panel">
          <p class="detail-section-title">处理结果</p>
          <div>
            <span class="status-pill" :class="statusTone(selectedServiceRequest.status)">
              {{ statusLabel(selectedServiceRequest.status) }}
            </span>
            <p v-if="selectedServiceRequest.status === 'SUBMITTED'">
              当前申请等待处理；此账号仅有查看权限。
            </p>
            <template v-else>
              <p v-if="selectedServiceRequest.review_note">{{ selectedServiceRequest.review_note }}</p>
              <small>处理时间：{{ formatDate(selectedServiceRequest.reviewed_at, true) }}</small>
            </template>
          </div>
        </section>
      </aside>
    </div>

    <div v-if="productCreatorOpen" class="detail-backdrop" @click.self="productCreatorOpen = false">
      <aside class="product-creator" role="dialog" aria-modal="true" aria-label="新增保险产品">
        <header class="detail-header">
          <div>
            <p class="eyebrow">NEW INSURANCE PRODUCT</p>
            <h2>新增保险产品</h2>
          </div>
          <button type="button" aria-label="关闭新增产品" @click="productCreatorOpen = false">×</button>
        </header>

        <section class="creator-intro">
          <div class="creator-orbit"><i></i><span>NEW</span></div>
          <div>
            <small>PRODUCT LAUNCH WORKFLOW</small>
            <h3>从产品定义到首个保障计划</h3>
            <p>创建后默认为未上架状态，并生成可编辑的 V{{ createVersionNo || "1.0.0" }} 草稿。</p>
          </div>
        </section>

        <nav class="creator-steps" aria-label="新增产品步骤">
          <button type="button" :class="{ active: productCreatorStep === 1, complete: productCreatorStep > 1 }" @click="productCreatorStep = 1">
            <span>01</span><div><strong>产品定义</strong><small>名称与分类</small></div>
          </button>
          <i></i>
          <button type="button" :class="{ active: productCreatorStep === 2, complete: productCreatorStep > 2 }" :disabled="productCreatorStep < 2" @click="productCreatorStep = 2">
            <span>02</span><div><strong>首个版本</strong><small>规则与条款</small></div>
          </button>
          <i></i>
          <button type="button" :class="{ active: productCreatorStep === 3 }" :disabled="productCreatorStep < 3" @click="productCreatorStep = 3">
            <span>03</span><div><strong>保障计划</strong><small>保费与保额</small></div>
          </button>
        </nav>

        <form class="creator-form" @submit.prevent="createNewProduct">
          <section v-if="productCreatorStep === 1" class="creator-step-panel">
            <div class="product-section-heading">
              <div><small>STEP 01 · PRODUCT</small><h3>定义产品身份</h3></div>
              <span>产品编码创建后不可修改</span>
            </div>
            <div class="creator-grid">
              <label>
                <span>产品编码</span>
                <input
                  v-model.trim="createProductCode"
                  maxlength="32"
                  placeholder="例如 MED-FAMILY"
                  @blur="createProductCode = createProductCode.toUpperCase()"
                />
                <small>以字母开头，仅使用字母、数字和连字符</small>
              </label>
              <label>
                <span>产品分类</span>
                <select v-model="createProductCategory">
                  <option value="MEDICAL">医疗保障</option>
                  <option value="ACCIDENT">意外保障</option>
                </select>
              </label>
              <label class="wide">
                <span>产品名称</span>
                <input v-model.trim="createProductName" maxlength="128" placeholder="请输入面向客户展示的产品名称" />
              </label>
              <label class="wide">
                <span>产品简介</span>
                <textarea v-model.trim="createProductSummary" rows="5" maxlength="500" placeholder="简要说明保障定位、适用人群和核心价值"></textarea>
                <small>{{ createProductSummary.length }}/500 · 将展示在 H5 产品列表</small>
              </label>
            </div>
          </section>

          <section v-else-if="productCreatorStep === 2" class="creator-step-panel">
            <div class="product-section-heading">
              <div><small>STEP 02 · VERSION</small><h3>配置首个产品版本</h3></div>
              <span>版本创建后保持草稿状态</span>
            </div>
            <div class="creator-grid">
              <label><span>版本号</span><input v-model.trim="createVersionNo" placeholder="1.0.0" /></label>
              <label><span>计划生效时间</span><input v-model="createEffectiveFrom" type="datetime-local" /></label>
              <label><span>核保规则版本</span><input v-model.trim="createRuleVersion" /></label>
              <label><span>费率规则版本</span><input v-model.trim="createRateVersion" /></label>
              <label class="wide">
                <span>投保须知与条款摘要</span>
                <textarea v-model.trim="createTermsSummary" rows="6" maxlength="5000" placeholder="填写等待期、责任免除、投保条件等重要说明"></textarea>
              </label>
            </div>
          </section>

          <section v-else class="creator-step-panel">
            <div class="product-section-heading">
              <div><small>STEP 03 · PLAN</small><h3>配置首个保障计划</h3></div>
              <button type="button" @click="addCreateProductPlan">＋ 增加计划</button>
            </div>
            <div class="creator-plan-list">
              <article v-for="(plan, index) in createProductPlans" :key="index">
                <header>
                  <div><span>{{ String(index + 1).padStart(2, "0") }}</span><strong>保障计划</strong></div>
                  <button v-if="createProductPlans.length > 1" type="button" @click="removeCreateProductPlan(index)">移除</button>
                </header>
                <div class="creator-grid">
                  <label><span>计划编码</span><input v-model.trim="plan.code" placeholder="STANDARD" @blur="plan.code = plan.code.toUpperCase()" /></label>
                  <label><span>计划名称</span><input v-model.trim="plan.name" placeholder="标准计划" /></label>
                  <label><span>年保费（元）</span><input :value="plan.premium_cents / 100" type="number" min="0.01" step="0.01" @input="updatePlanAmount(plan, 'premium_cents', $event)" /></label>
                  <label><span>保障额度（元）</span><input :value="plan.coverage_amount_cents / 100" type="number" min="1" step="1" @input="updatePlanAmount(plan, 'coverage_amount_cents', $event)" /></label>
                  <label><span>最小投保年龄</span><input v-model.number="plan.min_age" type="number" min="0" max="120" /></label>
                  <label><span>最大投保年龄</span><input v-model.number="plan.max_age" type="number" min="0" max="120" /></label>
                  <label class="wide"><span>保障责任说明</span><textarea v-model.trim="plan.summary" rows="3" placeholder="说明该计划覆盖的核心保障责任"></textarea></label>
                </div>
              </article>
            </div>
          </section>

          <p v-if="productCreatorError" class="creator-error">{{ productCreatorError }}</p>
          <footer class="creator-footer">
            <button v-if="productCreatorStep > 1" type="button" @click="moveProductCreator(-1)">上一步</button>
            <span v-else>创建过程不会影响当前在售产品</span>
            <button
              v-if="productCreatorStep < 3"
              type="button"
              class="primary"
              :disabled="!productCreatorStepValid"
              @click="moveProductCreator(1)"
            >
              下一步
            </button>
            <button
              v-else
              type="submit"
              class="primary"
              :disabled="!productCreatorStepValid || productCreatorSaving"
            >
              {{ productCreatorSaving ? "正在创建…" : "创建产品与草稿" }}
            </button>
          </footer>
        </form>
      </aside>
    </div>

    <div v-if="productEditorOpen && selectedProduct" class="detail-backdrop" @click.self="productEditorOpen = false">
      <aside class="product-editor" role="dialog" aria-modal="true" aria-label="产品管理">
        <header class="detail-header">
          <div>
            <p class="eyebrow">PRODUCT CONTROL CENTER</p>
            <h2>产品配置中心</h2>
          </div>
          <button type="button" aria-label="关闭产品管理" @click="productEditorOpen = false">×</button>
        </header>

        <section class="product-editor-hero">
          <div>
            <span>{{ selectedProduct.category === "MEDICAL" ? "医疗保障" : "意外保障" }}</span>
            <h3>{{ selectedProduct.name }}</h3>
            <p>{{ selectedProduct.product_code }} · {{ selectedProduct.versions.length }} 个历史版本</p>
          </div>
          <em :class="{ inactive: selectedProduct.status !== 'ACTIVE' }">
            <i></i>{{ selectedProduct.status === "ACTIVE" ? "当前在售" : "已停止销售" }}
          </em>
        </section>

        <nav class="product-editor-tabs" aria-label="产品配置模块">
          <button :class="{ active: productEditorTab === 'profile' }" type="button" @click="productEditorTab = 'profile'">
            基本信息
          </button>
          <button :class="{ active: productEditorTab === 'versions' }" type="button" @click="productEditorTab = 'versions'">
            版本与计划
          </button>
          <button :class="{ active: productEditorTab === 'logs' }" type="button" @click="productEditorTab = 'logs'">
            操作记录
          </button>
        </nav>

        <p v-if="productEditorMessage" class="product-editor-message">{{ productEditorMessage }}</p>

        <form v-if="productEditorTab === 'profile'" class="product-profile-form" @submit.prevent="saveProductProfile">
          <div class="product-section-heading">
            <div><small>PRODUCT PROFILE</small><h3>产品基本信息</h3></div>
            <span>产品编码 {{ selectedProduct.product_code }} 创建后不可修改</span>
          </div>
          <label>
            <span>产品名称</span>
            <input v-model.trim="productName" :disabled="!hasPermission('product:write')" maxlength="128" />
          </label>
          <label>
            <span>产品分类</span>
            <select v-model="productCategory" :disabled="!hasPermission('product:write')">
              <option value="MEDICAL">医疗保障</option>
              <option value="ACCIDENT">意外保障</option>
            </select>
          </label>
          <label class="wide">
            <span>客户侧产品简介</span>
            <textarea v-model.trim="productSummary" :disabled="!hasPermission('product:write')" maxlength="500" rows="5"></textarea>
            <small>{{ productSummary.length }}/500 · 该内容会展示在 H5 产品列表</small>
          </label>
          <footer>
            <button type="submit" :disabled="!hasPermission('product:write') || productEditorSaving || !productName || !productSummary">
              {{ productEditorSaving ? "正在保存…" : "保存基本信息" }}
            </button>
          </footer>
        </form>

        <section v-else-if="productEditorTab === 'versions'" class="product-version-workspace">
          <aside class="product-version-rail">
            <div class="product-section-heading">
              <div><small>VERSION ARCHIVE</small><h3>产品版本</h3></div>
              <button v-if="hasPermission('product:write')" type="button" @click="startCreateVersion">＋</button>
            </div>
            <button
              v-for="version in selectedProduct.versions"
              :key="version.id"
              type="button"
              :class="{ active: selectedProductVersionId === version.id }"
              @click="selectProductVersion(version)"
            >
              <span><strong>V{{ version.version_no }}</strong><em :class="version.status.toLowerCase()">{{ statusLabel(version.status) }}</em></span>
              <small>{{ formatDate(version.effective_from) }} · {{ version.plan_count }} 个计划</small>
            </button>
          </aside>

          <div class="product-version-editor">
            <form v-if="creatingVersion" class="new-version-card" @submit.prevent="createProductVersion">
              <div class="product-section-heading">
                <div><small>NEW VERSION</small><h3>创建版本草稿</h3></div>
                <button type="button" @click="creatingVersion = false">取消</button>
              </div>
              <div class="version-form-grid">
                <label><span>新版本号</span><input v-model.trim="newVersionNo" placeholder="例如 3.0.0" /></label>
                <label><span>计划生效时间</span><input v-model="newVersionEffectiveFrom" type="datetime-local" /></label>
                <label class="wide">
                  <span>复制已有版本</span>
                  <select v-model="newVersionSourceId">
                    <option value="">不复制，创建空白版本</option>
                    <option v-for="version in selectedProduct.versions" :key="version.id" :value="version.id">
                      V{{ version.version_no }} · {{ statusLabel(version.status) }}
                    </option>
                  </select>
                </label>
              </div>
              <button class="primary-action" type="submit" :disabled="productEditorSaving || !newVersionNo || !newVersionEffectiveFrom">
                {{ productEditorSaving ? "正在创建…" : "创建草稿并继续配置" }}
              </button>
            </form>

            <template v-else-if="selectedProductVersion">
              <section class="version-control-bar">
                <div>
                  <small>SELECTED VERSION</small>
                  <h3>V{{ selectedProductVersion.version_no }}</h3>
                  <span :class="['version-status', selectedProductVersion.status.toLowerCase()]">{{ statusLabel(selectedProductVersion.status) }}</span>
                </div>
                <div v-if="hasPermission('product:write')" class="version-actions">
                  <button v-if="selectedProductVersion.status === 'DRAFT'" type="button" @click="executeVersionAction('delete')">删除草稿</button>
                  <button v-if="selectedProductVersion.status === 'DRAFT'" type="button" class="primary" @click="executeVersionAction('publish')">发布版本</button>
                  <button v-if="selectedProductVersion.status === 'PUBLISHED'" type="button" @click="executeVersionAction('retire')">终止版本</button>
                </div>
              </section>

              <div class="immutable-notice" :class="{ editable: selectedProductVersion.status === 'DRAFT' }">
                <i>{{ selectedProductVersion.status === "DRAFT" ? "✎" : "✓" }}</i>
                <span>
                  <strong>{{ selectedProductVersion.status === "DRAFT" ? "草稿可编辑" : "已发布版本已锁定" }}</strong>
                  <small>{{ selectedProductVersion.status === "DRAFT" ? "保存草稿不会影响当前在售版本" : "如需调整保障责任，请复制并创建新版本" }}</small>
                </span>
              </div>

              <div class="version-form-grid">
                <label><span>销售生效时间</span><input v-model="versionEffectiveFrom" type="datetime-local" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                <label><span>销售截止时间</span><input v-model="versionEffectiveTo" type="datetime-local" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                <label><span>核保规则版本</span><input v-model.trim="versionRule" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                <label><span>费率规则版本</span><input v-model.trim="versionRate" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                <label class="wide">
                  <span>投保须知与条款摘要</span>
                  <textarea v-model.trim="versionTerms" rows="5" maxlength="5000" :disabled="selectedProductVersion.status !== 'DRAFT'"></textarea>
                </label>
              </div>

              <div class="plan-editor-heading">
                <div><small>COVERAGE PLANS</small><h3>保障计划</h3></div>
                <button v-if="selectedProductVersion.status === 'DRAFT' && hasPermission('product:write')" type="button" @click="addProductPlan">＋ 添加计划</button>
              </div>
              <div class="plan-editor-list">
                <article v-for="(plan, index) in versionPlans" :key="plan.id ?? index">
                  <header>
                    <span>PLAN {{ String(index + 1).padStart(2, "0") }}</span>
                    <select v-model="plan.status" :disabled="selectedProductVersion.status !== 'DRAFT'">
                      <option value="ACTIVE">有效计划</option>
                      <option value="INACTIVE">停用计划</option>
                    </select>
                    <button v-if="selectedProductVersion.status === 'DRAFT' && versionPlans.length > 1" type="button" @click="removeProductPlan(index)">移除</button>
                  </header>
                  <div class="plan-form-grid">
                    <label><span>计划编码</span><input v-model.trim="plan.code" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                    <label><span>计划名称</span><input v-model.trim="plan.name" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                    <label><span>年保费（元）</span><input :value="plan.premium_cents / 100" type="number" min="0" step="0.01" :disabled="selectedProductVersion.status !== 'DRAFT'" @input="updatePlanAmount(plan, 'premium_cents', $event)" /></label>
                    <label><span>保障额度（元）</span><input :value="plan.coverage_amount_cents / 100" type="number" min="0" step="1" :disabled="selectedProductVersion.status !== 'DRAFT'" @input="updatePlanAmount(plan, 'coverage_amount_cents', $event)" /></label>
                    <label><span>最小投保年龄</span><input v-model.number="plan.min_age" type="number" min="0" max="120" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                    <label><span>最大投保年龄</span><input v-model.number="plan.max_age" type="number" min="0" max="120" :disabled="selectedProductVersion.status !== 'DRAFT'" /></label>
                    <label class="wide"><span>保障责任说明</span><textarea v-model.trim="plan.summary" rows="3" :disabled="selectedProductVersion.status !== 'DRAFT'"></textarea></label>
                  </div>
                </article>
              </div>
              <footer v-if="selectedProductVersion.status === 'DRAFT' && hasPermission('product:write')" class="version-save-footer">
                <span>发布前请确认生效时间、投保须知、保费和保额</span>
                <button type="button" :disabled="productEditorSaving" @click="saveProductVersion">
                  {{ productEditorSaving ? "正在保存…" : "保存版本草稿" }}
                </button>
              </footer>
            </template>
          </div>
        </section>

        <section v-else class="product-log-panel">
          <div class="product-section-heading">
            <div><small>AUDIT TRAIL</small><h3>产品操作记录</h3></div>
            <span>记录由后端自动生成，不可修改</span>
          </div>
          <div v-if="selectedProduct.recent_logs.length" class="product-log-list">
            <article v-for="log in selectedProduct.recent_logs" :key="log.id">
              <i></i>
              <div>
                <strong>{{ productLogLabel(log.action) }}</strong>
                <p>{{ log.actor_name }} · {{ formatDate(log.created_at, true) }}</p>
              </div>
              <code v-if="log.detail.version_no">V{{ log.detail.version_no }}</code>
            </article>
          </div>
          <div v-else class="product-log-empty">尚无人工维护记录，后续操作会自动记录在这里。</div>
        </section>
      </aside>
    </div>

    <div v-if="accountEditorOpen" class="detail-backdrop" @click.self="accountEditorOpen = false">
      <aside class="account-editor" role="dialog" aria-modal="true" aria-label="后台账号维护">
        <header class="detail-header">
          <div>
            <p class="eyebrow">ADMIN ACCOUNT</p>
            <h2>{{ editingAdmin ? "维护后台账号" : "新增后台账号" }}</h2>
          </div>
          <button type="button" aria-label="关闭账号维护" @click="accountEditorOpen = false">×</button>
        </header>
        <form class="account-form" @submit.prevent="saveAdminAccount">
          <label>
            <span>登录账号</span>
            <input
              v-model.trim="accountUsername"
              :disabled="Boolean(editingAdmin)"
              autocomplete="off"
              placeholder="例如 service01"
            />
          </label>
          <label>
            <span>账号姓名</span>
            <input v-model.trim="accountDisplayName" autocomplete="off" placeholder="请输入账号姓名" />
          </label>
          <label v-if="!editingAdmin">
            <span>初始密码</span>
            <input
              v-model="accountPassword"
              type="password"
              autocomplete="new-password"
              placeholder="至少8位"
            />
          </label>
          <label>
            <span>业务角色</span>
            <select v-model="accountRoleCode">
              <option v-for="role in roles" :key="role.code" :value="role.code">
                {{ role.name }}（{{ role.code }}）
              </option>
            </select>
          </label>
          <label v-if="editingAdmin">
            <span>账号状态</span>
            <select v-model="accountStatus" :disabled="editingAdmin.id === profile.id">
              <option value="ACTIVE">启用</option>
              <option value="DISABLED">停用</option>
            </select>
            <small v-if="editingAdmin.id === profile.id">当前登录账号不能停用</small>
          </label>
          <p v-if="accountError" class="form-message error">{{ accountError }}</p>
          <button
            type="submit"
            class="account-primary"
            :disabled="
              accountSaving ||
              !accountDisplayName ||
              !accountRoleCode ||
              (!editingAdmin && (!accountUsername || accountPassword.length < 8))
            "
          >
            {{ accountSaving ? "正在保存…" : editingAdmin ? "保存账号设置" : "创建后台账号" }}
          </button>
        </form>
        <section v-if="editingAdmin" class="password-reset-panel">
          <div><strong>重置登录密码</strong><p>新密码保存后立即生效</p></div>
          <input
            v-model="passwordResetValue"
            type="password"
            autocomplete="new-password"
            placeholder="输入至少8位新密码"
          />
          <button
            type="button"
            :disabled="passwordResetValue.length < 8"
            @click="resetAdminPassword"
          >
            重置密码
          </button>
          <p v-if="passwordResetMessage" class="form-message">{{ passwordResetMessage }}</p>
        </section>
      </aside>
    </div>

    <div v-if="customerToToggle" class="confirm-backdrop">
      <section class="confirm-dialog" role="dialog" aria-modal="true" aria-label="确认用户状态变更">
        <span class="confirm-symbol">!</span>
        <p class="eyebrow">ACCOUNT STATUS</p>
        <h2>{{ customerToToggle.status === "ACTIVE" ? "确认停用该客户？" : "确认启用该客户？" }}</h2>
        <p>
          {{ customerToToggle.display_name }}（{{ customerToToggle.mobile_masked }}）
          {{ customerToToggle.status === "ACTIVE" ? "将无法继续登录和发起投保。" : "将恢复登录和投保能力。" }}
        </p>
        <div>
          <button type="button" @click="customerToToggle = null">取消</button>
          <button type="button" class="danger" :disabled="customerSaving" @click="confirmCustomerStatusChange">
            {{ customerSaving ? "处理中…" : "确认变更" }}
          </button>
        </div>
      </section>
    </div>
  </main>
</template>
