<script setup>
import { computed, reactive, ref } from "vue";
import AccountSecurity from "@/components/common/AccountSecurity.vue";
import ExcelActionBar from "@/components/common/ExcelActionBar.vue";
import LedgerPager from "@/components/common/LedgerPager.vue";
import StatusBadge from "@/components/common/StatusBadge.vue";
import QualificationLedger from "@/components/admin/QualificationLedger.vue";
import { useLedger } from "@/composables/useLedger";
import { api } from "@/lib/api";
import { adminLevelLabel, auditDetailLabel } from "@/lib/business-labels";
import { downloadSelectedExcel } from "@/lib/export-selection";
import { formatDate } from "@/lib/format";
import { sessionStore } from "@/stores/session";
import { uiStore } from "@/stores/ui";

const props = defineProps({
  page: { type: String, required: true },
  data: { type: Object, default: () => ({}) },
});
const emit = defineEmits(["reload"]);
const busy = ref(false);
const error = ref("");
const issued = ref(null);
const schoolForm = reactive({
  name: "",
  code: "",
  address: "",
  isSourceSchool: true,
  isAdmissionSchool: false,
  active: true,
});
const classForm = reactive({ grade: "", name: "", active: true });
const adminForm = reactive({
  displayName: "",
  username: "",
  password: "",
  adminLevel: "school",
  schoolId: "",
  classId: "",
});
const archiveForm = reactive({
  scopeType: "class",
  scopeValue: "",
  archived: true,
});
const reviewNotes = reactive({});
const batchQuotas = reactive({});
const candidateSelected = ref([]);
const registrationSelected = ref([]);
const paymentSelected = ref([]);

const dashboardMetricDefinitions = [
  ["candidates", "考生总数"],
  ["pendingCandidates", "资料待审核"],
  ["registrations", "报名记录"],
  ["pendingRegistrations", "报名待审核"],
  ["pendingPayments", "待缴费"],
  ["pendingFlows", "待办流程"],
  ["publishedExams", "已发布考试"],
  ["notices", "通知公告"],
];
const dashboardMetrics = computed(() =>
  dashboardMetricDefinitions.map(([key, label]) => ({
    key,
    label,
    value: props.data.metrics?.[key] ?? 0,
  })),
);

const classById = computed(
  () => new Map((props.data.classes || []).map((item) => [item.id, item])),
);
const paymentSource = computed(
  () => props.data.registrations || props.data.payments || [],
);
const schoolLedger = useLedger(() => props.data.schools || [], {
  searchText: (item) =>
    [
      item.name,
      item.code,
      item.address,
      item.isSourceSchool && "生源校",
      item.isAdmissionSchool && "招生校",
    ]
      .filter(Boolean)
      .join(" "),
  filters: {
    status: (item, value) => (value === "active" ? item.active : !item.active),
    type: (item, value) =>
      value === "source" ? item.isSourceSchool : item.isAdmissionSchool,
  },
});
const adminLedger = useLedger(() => props.data.admins || [], {
  searchText: (item) =>
    [
      item.displayName,
      item.username,
      item.levelName,
      item.adminLevel,
      item.schoolName,
      item.className,
    ].join(" "),
  filters: {
    status: (item, value) => (value === "active" ? item.active : !item.active),
    level: (item, value) => item.adminLevel === value,
  },
});
const batchLedger = useLedger(() => props.data.batches || [], {
  searchText: (item) =>
    [
      item.id,
      item.schoolName,
      item.requesterName,
      item.status,
      ...(item.quotas || []).map((row) => row.className),
    ].join(" "),
  filters: { status: (item, value) => item.status === value },
});
const candidateLedger = useLedger(() => props.data.candidates || [], {
  searchText: (item) =>
    [
      item.name,
      item.candidateNumber,
      item.idNumberMasked,
      item.school,
      item.grade,
      item.status,
      ...(item.registrations || []).flatMap((registration) => [
        registration.exam?.name,
        ...(registration.subjects || []).map((subject) => subject.name),
      ]),
    ].join(" "),
  filters: {
    school: (item, value) => item.school === value,
    grade: (item, value) => candidateClass(item)?.grade === value,
    class: (item, value) =>
      (candidateClass(item)?.name || item.grade) === value,
    exam: (item, value) =>
      (item.registrations || []).some(
        (registration) =>
          registration.exam?.id === value || registration.examId === value,
      ),
    status: (item, value) =>
      value === "archived"
        ? item.accountArchived
        : !item.accountArchived && item.status === value,
  },
});
const registrationLedger = useLedger(() => props.data.registrations || [], {
  searchText: (item) =>
    [
      item.candidate?.name,
      item.candidateName,
      item.registrationNumber,
      item.exam?.name,
      item.examName,
      item.schoolName,
      item.gradeName,
      item.className,
      ...(item.subjects || []).map((subject) => subject.name),
    ].join(" "),
  filters: {
    status: (item, value) => item.status === value,
    exam: (item, value) => (item.exam?.id || item.examId) === value,
    subject: (item, value) =>
      (item.subjects || []).some(
        (subject) => subject.id === value || subject.name === value,
      ),
    school: (item, value) => item.schoolName === value,
    grade: (item, value) => item.gradeName === value,
    class: (item, value) => item.className === value,
  },
});
const paymentLedger = useLedger(paymentSource, {
  searchText: (item) =>
    [
      item.candidate?.name,
      item.candidateName,
      item.registrationNumber,
      item.exam?.name,
      item.examName,
      item.schoolName,
      item.gradeName,
      item.className,
      ...(item.subjects || []).map((subject) => subject.name),
    ].join(" "),
  filters: {
    status: (item, value) => item.paymentStatus === value,
    exam: (item, value) => (item.exam?.id || item.examId) === value,
    school: (item, value) => item.schoolName === value,
    grade: (item, value) => item.gradeName === value,
    class: (item, value) => item.className === value,
  },
});

const availableClasses = computed(() => {
  if (
    props.page === "organization" ||
    sessionStore.state.user?.adminLevel === "school"
  )
    return props.data.classes || [];
  return (props.data.classes || []).filter(
    (item) => !adminForm.schoolId || item.schoolId === adminForm.schoolId,
  );
});
const candidateSchools = computed(() =>
  unique((props.data.candidates || []).map((item) => item.school)),
);
const candidateGrades = computed(() =>
  unique(
    (props.data.candidates || []).map((item) => candidateClass(item)?.grade),
  ),
);
const candidateClasses = computed(() =>
  unique(
    (props.data.candidates || []).map(
      (item) => candidateClass(item)?.name || item.grade,
    ),
  ),
);
const registrationExams = computed(() =>
  uniqueObjects(
    (props.data.registrations || []).map(
      (item) => item.exam || { id: item.examId, name: item.examName },
    ),
  ),
);
const registrationSubjects = computed(() =>
  uniqueObjects(
    (props.data.registrations || []).flatMap((item) => item.subjects || []),
    "name",
  ),
);
const registrationSchools = computed(() =>
  unique((props.data.registrations || []).map((item) => item.schoolName)),
);
const registrationGrades = computed(() =>
  unique((props.data.registrations || []).map((item) => item.gradeName)),
);
const registrationClasses = computed(() =>
  unique((props.data.registrations || []).map((item) => item.className)),
);
const paymentExams = computed(() =>
  uniqueObjects(
    paymentSource.value.map(
      (item) => item.exam || { id: item.examId, name: item.examName },
    ),
  ),
);
const paymentSchools = computed(() =>
  unique(paymentSource.value.map((item) => item.schoolName)),
);
const paymentGrades = computed(() =>
  unique(paymentSource.value.map((item) => item.gradeName)),
);
const paymentClasses = computed(() =>
  unique(paymentSource.value.map((item) => item.className)),
);
const paidCount = computed(
  () =>
    paymentSource.value.filter((item) => item.paymentStatus === "paid").length,
);
const paymentTotal = computed(() =>
  paymentSource.value.reduce(
    (sum, item) => sum + Number(item.amountDue || 0),
    0,
  ),
);
const paidTotal = computed(() =>
  paymentSource.value
    .filter((item) => item.paymentStatus === "paid")
    .reduce((sum, item) => sum + Number(item.amountDue || 0), 0),
);

function unique(values) {
  return [...new Set(values.filter(Boolean))].sort((a, b) =>
    String(a).localeCompare(String(b), "zh-CN"),
  );
}
function uniqueObjects(values, fallbackKey = "id") {
  const map = new Map();
  for (const value of values.filter(Boolean)) {
    const key = value[fallbackKey] || value.name;
    if (key && !map.has(key)) map.set(key, value);
  }
  return [...map.values()];
}
function candidateClass(item) {
  return classById.value.get(item.classId);
}
function candidateStatusLabel(value) {
  return (
    {
      pending: "待审核",
      school_review: "学校审核",
      approved: "已通过",
      rejected: "已退回",
      archived: "已归档",
    }[value] || value
  );
}
function canReviewCandidate(item) {
  return (
    item.profileCompleted !== false &&
    !item.accountArchived &&
    item.status === "pending" &&
    (!item.workflow || item.workflow.status === "pending") &&
    (sessionStore.state.user?.adminLevel === "super" ||
      !item.workflow?.assignee ||
      item.workflow.assignee.id === sessionStore.state.user?.id)
  );
}
function canReviewRegistration(item) {
  return (
    item.status === "pending" &&
    !item.exam?.archivedAt &&
    (!item.workflow || item.workflow.status === "pending") &&
    (sessionStore.state.user?.adminLevel === "super" ||
      !item.workflow?.assignee ||
      item.workflow.assignee.id === sessionStore.state.user?.id)
  );
}
function toggleCurrentPage(selection, rows, eligible = () => true) {
  const ids = rows.filter(eligible).map((item) => item.id);
  const current = Array.isArray(selection) ? selection : selection.value;
  const allSelected = ids.length && ids.every((id) => current.includes(id));
  const next = allSelected
    ? current.filter((id) => !ids.includes(id))
    : [...new Set([...current, ...ids])];
  if (Array.isArray(selection)) selection.splice(0, selection.length, ...next);
  else selection.value = next;
}

function exportCandidatesSelected() {
  downloadSelectedExcel("candidates", candidateSelected.value);
}
function exportRegistrationsSelected() {
  downloadSelectedExcel("registrations", registrationSelected.value);
}
function exportPaymentsSelected() {
  downloadSelectedExcel("payments", paymentSelected.value);
}

async function act(action, success, reload = true) {
  busy.value = true;
  error.value = "";
  try {
    const result = await action();
    if (success) uiStore.notify(success);
    if (reload) emit("reload");
    return result;
  } catch (requestError) {
    error.value = requestError.message;
    return null;
  } finally {
    busy.value = false;
  }
}
function createSchool() {
  return act(
    () => api("/api/admin/schools", { method: "POST", body: schoolForm }),
    "学校档案已创建",
  );
}
function toggleSchool(item) {
  return act(
    () =>
      api(`/api/admin/schools/${item.id}`, {
        method: "PATCH",
        body: { active: !item.active },
      }),
    item.active ? "学校已停用" : "学校已启用",
  );
}
function createClass() {
  return act(
    () => api("/api/admin/classes", { method: "POST", body: classForm }),
    "班级已创建",
  );
}
function toggleClass(item) {
  return act(
    () =>
      api(`/api/admin/classes/${item.id}`, {
        method: "PATCH",
        body: { active: !item.active },
      }),
    item.active ? "班级已停用" : "班级已启用",
  );
}
function createAdmin() {
  const body = { ...adminForm };
  if (sessionStore.state.user?.adminLevel === "school")
    Object.assign(body, {
      adminLevel: "class",
      schoolId: sessionStore.state.user.schoolId,
    });
  return act(
    () => api("/api/admin/admins", { method: "POST", body }),
    "管理员已创建",
  );
}
function toggleAdmin(item) {
  return act(
    () =>
      api(`/api/admin/admins/${item.id}`, {
        method: "PATCH",
        body: { active: !item.active, classId: item.classId },
      }),
    item.active ? "管理员已停用" : "管理员已启用",
  );
}
async function resetAdmin(item) {
  const result = await act(
    () =>
      api(`/api/admin/admins/${item.id}/reset-password`, { method: "POST" }),
    "",
    false,
  );
  if (result)
    issued.value = {
      title: "管理员临时密码",
      account: result.username,
      password: result.temporaryPassword,
    };
}
function setSelfRegistration(enabled) {
  return act(
    () =>
      api("/api/admin/settings/self-registration", {
        method: "PUT",
        body: { enabled },
      }),
    enabled ? "自主注册已开启" : "自主注册已关闭",
  );
}
function submitBatch() {
  const quotas = (props.data.classes || [])
    .map((item) => ({
      classId: item.id,
      count: Number(batchQuotas[item.id] || 0),
    }))
    .filter((item) => item.count > 0);
  if (!quotas.length) {
    error.value = "请至少为一个班级填写申领数量";
    return;
  }
  return act(
    () =>
      api("/api/admin/candidate-account-batches", {
        method: "POST",
        body: { quotas },
      }),
    "批量报名号申领已提交",
  );
}
function reviewBatch(batch, status) {
  return act(
    () =>
      api(`/api/admin/candidate-account-batches/${batch.id}`, {
        method: "PATCH",
        body: { status, reviewNote: reviewNotes[batch.id] || "" },
      }),
    status === "approved" ? "批次审批已通过" : "批次已退回",
  );
}
function reviewCandidate(item, status) {
  return act(
    () =>
      api(`/api/admin/candidates/${item.id}`, {
        method: "PATCH",
        body: { status, reviewNote: reviewNotes[item.id] || "" },
      }),
    status === "approved" ? "考生资料已通过当前步骤" : "考生资料已退回",
  );
}
async function resetCandidate(item) {
  const result = await act(
    () =>
      api(`/api/admin/candidates/${item.id}/reset-password`, {
        method: "POST",
      }),
    "",
    false,
  );
  if (result)
    issued.value = {
      title: "考生临时密码",
      account: result.candidateNumber,
      password: result.temporaryPassword,
    };
}
function archiveCandidates() {
  return act(
    () =>
      api("/api/admin/candidate-accounts/archive", {
        method: "POST",
        body: archiveForm,
      }),
    archiveForm.archived ? "范围内账户已归档" : "范围内账户已恢复",
  );
}
function reviewRegistration(item, status) {
  return act(
    () =>
      api(`/api/admin/registrations/${item.id}`, {
        method: "PATCH",
        body: { status, reviewNote: reviewNotes[item.id] || "" },
      }),
    status === "approved" ? "考试报名已通过当前步骤" : "考试报名已退回",
  );
}
function updatePayment(item, status) {
  return act(
    () =>
      api(`/api/admin/payments/${item.id}`, {
        method: "PATCH",
        body: { status },
      }),
    "缴费状态已更新",
  );
}
function saveQualification(group, item, eligible) {
  return act(
    () =>
      api(
        `/api/admin/indicator-qualifications/${group.exam.id}/${item.userId}`,
        { method: "PUT", body: { eligible } },
      ),
    "指标资格已确认",
  );
}
function bulkQualification(group, eligible, userIds) {
  return act(
    () =>
      api(`/api/admin/indicator-qualifications/${group.exam.id}/bulk`, {
        method: "PUT",
        body: { userIds, eligible },
      }),
    `已批量设置 ${userIds.length} 人资格`,
  );
}

async function bulkReview(kind, ids, status) {
  const source =
    kind === "candidates"
      ? props.data.candidates || []
      : props.data.registrations || [];
  const eligible =
    kind === "candidates" ? canReviewCandidate : canReviewRegistration;
  const eligibleIds = source
    .filter((item) => ids.includes(item.id) && eligible(item))
    .map((item) => item.id);
  if (!eligibleIds.length) {
    error.value = "选中记录中没有处于当前账号可处理步骤的数据";
    return;
  }
  const rejected = status === "rejected";
  const note = window.prompt(
    rejected ? "请填写批量退回原因（必填）" : "填写批量审核意见（可留空）",
    "",
  );
  if (note === null || (rejected && !note.trim())) return;
  busy.value = true;
  error.value = "";
  let completed = 0;
  try {
    for (const id of eligibleIds) {
      try {
        await api(`/api/admin/${kind}/${id}`, {
          method: "PATCH",
          body: { status, reviewNote: note },
        });
        completed++;
      } catch (requestError) {
        error.value = requestError.message;
      }
    }
    uiStore.notify(`已处理 ${completed} 条记录`);
    emit("reload");
    if (kind === "candidates") candidateSelected.value = [];
    else registrationSelected.value = [];
  } finally {
    busy.value = false;
  }
}
async function bulkPayments(status) {
  const items = paymentSource.value.filter(
    (item) =>
      paymentSelected.value.includes(item.id) &&
      item.paymentStatus !== status &&
      !item.exam?.archivedAt,
  );
  if (
    !items.length ||
    !window.confirm(
      `确认将 ${items.length} 条缴费记录改为“${status === "paid" ? "已缴费" : "待缴费"}”吗？`,
    )
  )
    return;
  busy.value = true;
  error.value = "";
  let completed = 0;
  try {
    for (const item of items) {
      try {
        await api(`/api/admin/payments/${item.id}`, {
          method: "PATCH",
          body: { status },
        });
        completed++;
      } catch (requestError) {
        error.value = requestError.message;
      }
    }
    uiStore.notify(`已更新 ${completed} 条缴费记录`);
    paymentSelected.value = [];
    emit("reload");
  } finally {
    busy.value = false;
  }
}
async function importExcel(resource, payload) {
  const input = payload?.input || payload?.target;
  const file = payload?.file || input?.files?.[0];
  if (!file) return;
  const result = await act(
    () =>
      api(`/api/admin/excel/${resource}`, {
        method: "POST",
        headers: {
          "Content-Type":
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        },
        body: file,
      }),
    resource === "account_quotas" ? "" : "Excel 已导入",
    resource !== "account_quotas",
  );
  if (resource === "account_quotas" && result) {
    for (const key of Object.keys(batchQuotas)) batchQuotas[key] = 0;
    for (const item of result.quotas || [])
      batchQuotas[item.classId] = item.count;
    uiStore.notify(
      `已读取 ${result.quotas?.length || 0} 个班级配额，请核对后提交`,
    );
  }
  if (input) input.value = "";
}
</script>

<template>
  <div class="admin-core-workspace">
    <div v-if="error" class="form-error">{{ error }}</div>
    <section v-if="issued" class="issued-credential">
      <div>
        <span>一次性登录凭据</span>
        <h2>{{ issued.title }}</h2>
        <p>请通过线下安全渠道交付；关闭后不再展示。</p>
      </div>
      <dl>
        <div>
          <dt>登录账号</dt>
          <dd>{{ issued.account }}</dd>
        </div>
        <div>
          <dt>临时密码</dt>
          <dd>{{ issued.password }}</dd>
        </div>
      </dl>
      <button class="app-button" @click="issued = null">我已保存</button>
    </section>

    <template v-if="page === 'dashboard'">
      <section class="scope-banner-vue">
        <span>{{ adminLevelLabel(sessionStore.state.user?.adminLevel) }}</span>
        <div>
          <strong>{{ data.scopeLabel }}</strong
          ><small>以下指标已按当前管理员数据范围过滤</small>
        </div>
      </section>
      <section class="record-metrics">
        <article v-for="metric in dashboardMetrics" :key="metric.key">
          <span>{{ metric.label }}</span
          ><strong>{{ metric.value }}</strong>
        </article>
      </section>
      <section class="record-panel audit-ledger">
        <header>
          <div>
            <h2>最近操作</h2>
            <p>系统审计日志</p>
          </div>
        </header>
        <div v-for="log in data.logs" :key="log.id" class="dashboard-row">
          <b>{{ String(log.actorName || "系").slice(0, 1) }}</b
          ><span
            ><strong>{{ log.actorName || "系统" }} · {{ log.action }}</strong
            ><small>{{ auditDetailLabel(log.detail) }}</small></span
          ><time>{{ formatDate(log.createdAt, true) }}</time>
        </div>
      </section>
    </template>

    <template v-else-if="page === 'schools'">
      <form
        class="business-form admin-create-strip"
        @submit.prevent="createSchool"
      >
        <header>
          <div>
            <p>组织机构</p>
            <h2>新增学校档案</h2>
          </div>
        </header>
        <div class="form-grid">
          <label
            ><span>学校名称</span
            ><input v-model="schoolForm.name" required /></label
          ><label
            ><span>学校代码</span
            ><input v-model="schoolForm.code" required /></label
          ><label
            ><span>地址</span><input v-model="schoolForm.address"
          /></label>
        </div>
        <div class="check-row">
          <label
            ><input v-model="schoolForm.isSourceSchool" type="checkbox" />
            生源学校</label
          ><label
            ><input v-model="schoolForm.isAdmissionSchool" type="checkbox" />
            招生学校</label
          ><label
            ><input v-model="schoolForm.active" type="checkbox" />
            创建后启用</label
          >
        </div>
        <button class="app-button app-button--primary" :disabled="busy">
          创建学校
        </button>
      </form>
      <section class="record-panel">
        <header>
          <div>
            <h2>学校名录</h2>
            <p>
              筛选结果 {{ schoolLedger.total }} / 共
              {{ data.schools?.length || 0 }} 所
            </p>
          </div>
        </header>
        <div class="ledger-toolbar">
          <label
            ><span>关键词</span
            ><input
              v-model="schoolLedger.query"
              placeholder="学校名称、代码、类型或地址" /></label
          ><label
            ><span>状态</span
            ><select v-model="schoolLedger.filters.status">
              <option value="">全部状态</option>
              <option value="active">已启用</option>
              <option value="disabled">已停用</option>
            </select></label
          ><label
            ><span>学校类型</span
            ><select v-model="schoolLedger.filters.type">
              <option value="">全部类型</option>
              <option value="source">生源学校</option>
              <option value="admission">招生学校</option>
            </select></label
          >
        </div>
        <div class="table-scroll">
          <table>
            <thead>
              <tr>
                <th>学校 / 代码</th>
                <th>类型</th>
                <th>地址</th>
                <th>班级</th>
                <th>考生</th>
                <th>状态</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in schoolLedger.rows" :key="item.id">
                <td>
                  <strong>{{ item.name }}</strong
                  ><small>{{ item.code }}</small>
                </td>
                <td>
                  {{
                    [
                      item.isSourceSchool && "生源校",
                      item.isAdmissionSchool && "招生校",
                    ]
                      .filter(Boolean)
                      .join(" / ")
                  }}
                </td>
                <td>{{ item.address || "未填写" }}</td>
                <td>{{ item.classCount }}</td>
                <td>{{ item.candidateCount }}</td>
                <td>
                  <StatusBadge :value="item.active ? 'active' : 'disabled'" />
                </td>
                <td>
                  <button class="table-action" @click="toggleSchool(item)">
                    {{ item.active ? "停用" : "启用" }}
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="schoolLedger.page"
          v-model:page-size="schoolLedger.pageSize"
          :total="schoolLedger.total"
        />
      </section>
    </template>

    <template v-else-if="page === 'organization'">
      <ExcelActionBar
        resource="classes"
        label="班级台账"
        @import="importExcel('classes', $event)"
      />
      <ExcelActionBar
        resource="class_admins"
        label="班级管理员"
        @import="importExcel('class_admins', $event)"
      />
      <form
        class="business-form admin-create-strip"
        @submit.prevent="createClass"
      >
        <h2>新增本校班级</h2>
        <div class="form-grid">
          <label
            ><span>年级</span
            ><input
              v-model="classForm.grade"
              required
              placeholder="例如：九年级" /></label
          ><label
            ><span>班级名称</span
            ><input v-model="classForm.name" required placeholder="例如：1 班"
          /></label>
        </div>
        <button class="app-button app-button--primary">创建班级</button>
      </form>
      <section class="organization-card-grid">
        <article
          v-for="item in data.classes"
          :key="item.id"
          class="record-panel org-card"
        >
          <header>
            <div>
              <span>{{ item.grade }}</span>
              <h2>{{ item.name }}</h2>
            </div>
            <StatusBadge :value="item.active ? 'active' : 'disabled'" />
          </header>
          <strong>{{ item.candidateCount }} 名考生</strong>
          <div
            v-for="admin in item.admins"
            :key="admin.id"
            class="dashboard-row"
          >
            <b>{{ admin.displayName?.slice(0, 1) }}</b
            ><span
              ><strong>{{ admin.displayName }}</strong
              ><small>{{ admin.username }}</small></span
            ><StatusBadge :value="admin.active ? 'active' : 'disabled'" />
          </div>
          <footer>
            <button class="table-action" @click="toggleClass(item)">
              {{ item.active ? "停用班级" : "启用班级" }}
            </button>
          </footer>
        </article>
      </section>
      <form
        class="business-form admin-create-strip"
        @submit.prevent="createAdmin"
      >
        <h2>新增班级管理员</h2>
        <div class="form-grid">
          <label
            ><span>姓名</span
            ><input v-model="adminForm.displayName" required /></label
          ><label
            ><span>账号</span
            ><input v-model="adminForm.username" required /></label
          ><label
            ><span>初始密码</span
            ><input
              v-model="adminForm.password"
              type="password"
              minlength="8"
              required /></label
          ><label
            ><span>绑定班级</span
            ><select v-model="adminForm.classId" required>
              <option value="">请选择</option>
              <option
                v-for="item in data.classes"
                :key="item.id"
                :value="item.id"
              >
                {{ item.grade }} · {{ item.name }}
              </option>
            </select></label
          >
        </div>
        <button class="app-button app-button--primary">创建班级管理员</button>
      </form>
    </template>

    <template v-else-if="page === 'admins'">
      <form
        class="business-form admin-create-strip"
        @submit.prevent="createAdmin"
      >
        <header>
          <div>
            <p>账号权限</p>
            <h2>创建管理员</h2>
          </div>
          <button
            type="button"
            class="app-button"
            @click="setSelfRegistration(!data.selfRegistrationEnabled)"
          >
            {{ data.selfRegistrationEnabled ? "关闭自主注册" : "开启自主注册" }}
          </button>
        </header>
        <div class="form-grid">
          <label
            ><span>姓名</span
            ><input v-model="adminForm.displayName" required /></label
          ><label
            ><span>登录账号</span
            ><input v-model="adminForm.username" required /></label
          ><label
            ><span>初始密码</span
            ><input
              v-model="adminForm.password"
              type="password"
              minlength="8"
              required /></label
          ><label
            ><span>管理员层级</span
            ><select v-model="adminForm.adminLevel">
              <option value="super">超级管理员</option>
              <option value="school">校级管理员</option>
              <option value="class">班级管理员</option>
            </select></label
          ><label v-if="adminForm.adminLevel !== 'super'"
            ><span>绑定学校</span
            ><select v-model="adminForm.schoolId" required>
              <option value="">请选择</option>
              <option
                v-for="school in data.schools"
                :key="school.id"
                :value="school.id"
              >
                {{ school.name }}
              </option>
            </select></label
          ><label v-if="adminForm.adminLevel === 'class'"
            ><span>绑定班级</span
            ><select v-model="adminForm.classId" required>
              <option value="">请选择</option>
              <option
                v-for="item in availableClasses"
                :key="item.id"
                :value="item.id"
              >
                {{ item.grade }} · {{ item.name }}
              </option>
            </select></label
          >
        </div>
        <button class="app-button app-button--primary">创建管理员</button>
      </form>
      <section class="record-panel">
        <header>
          <div>
            <h2>管理员账户</h2>
            <p>
              筛选结果 {{ adminLedger.total }} / 共
              {{ data.admins?.length || 0 }} 个；停用不删除历史记录。
            </p>
          </div>
        </header>
        <div class="ledger-toolbar">
          <label
            ><span>关键词</span
            ><input
              v-model="adminLedger.query"
              placeholder="姓名、账号、学校或班级" /></label
          ><label
            ><span>层级</span
            ><select v-model="adminLedger.filters.level">
              <option value="">全部层级</option>
              <option value="super">超级管理员</option>
              <option value="school">校级管理员</option>
              <option value="class">班级管理员</option>
            </select></label
          ><label
            ><span>状态</span
            ><select v-model="adminLedger.filters.status">
              <option value="">全部状态</option>
              <option value="active">已启用</option>
              <option value="disabled">已停用</option>
            </select></label
          >
        </div>
        <div class="table-scroll">
          <table>
            <thead>
              <tr>
                <th>管理员</th>
                <th>账号</th>
                <th>层级</th>
                <th>范围</th>
                <th>状态</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in adminLedger.rows" :key="item.id">
                <td>{{ item.displayName }}</td>
                <td>{{ item.username }}</td>
                <td>{{ item.levelName || adminLevelLabel(item.adminLevel) }}</td>
                <td>
                  {{ item.schoolName || "全局" }} {{ item.className || "" }}
                </td>
                <td>
                  <StatusBadge :value="item.active ? 'active' : 'disabled'" />
                </td>
                <td>
                  <button
                    class="table-action"
                    :disabled="item.id === sessionStore.state.user?.id"
                    @click="resetAdmin(item)"
                  >
                    重置密码</button
                  ><button
                    class="table-action"
                    :disabled="item.id === sessionStore.state.user?.id"
                    @click="toggleAdmin(item)"
                  >
                    {{ item.active ? "停用" : "启用" }}
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="adminLedger.page"
          v-model:page-size="adminLedger.pageSize"
          :total="adminLedger.total"
        />
      </section>
    </template>

    <template v-else-if="page === 'account-batches'">
      <ExcelActionBar
        resource="account_quotas"
        label="班级申领配额"
        @import="importExcel('account_quotas', $event)"
      />
      <form class="business-form" @submit.prevent="submitBatch">
        <p>报名号申领</p>
        <h2>按班级申领报名号</h2>
        <div class="quota-grid-vue">
          <label v-for="item in data.classes" :key="item.id"
            ><span
              ><strong>{{ item.name }}</strong
              ><small>{{ item.grade }}</small></span
            ><input
              v-model="batchQuotas[item.id]"
              type="number"
              min="0"
              max="200"
          /></label>
        </div>
        <button class="app-button app-button--primary">提交批量申领</button>
      </form>
      <section class="record-panel">
        <header>
          <div>
            <h2>申领批次与返回结果</h2>
            <p>
              筛选结果 {{ batchLedger.total }} / 共
              {{ data.batches?.length || 0 }} 批
            </p>
          </div>
        </header>
        <div class="ledger-toolbar">
          <label
            ><span>关键词</span
            ><input
              v-model="batchLedger.query"
              placeholder="批次、学校、班级或提交人" /></label
          ><label
            ><span>审批状态</span
            ><select v-model="batchLedger.filters.status">
              <option value="">全部状态</option>
              <option value="pending">审批中</option>
              <option value="approved">已通过</option>
              <option value="rejected">已退回</option>
            </select></label
          >
        </div>
        <div class="batch-ledger-vue">
          <article
            v-for="batch in batchLedger.rows"
            :key="batch.id"
            class="record-panel batch-card-vue"
          >
            <header>
              <div>
                <span>{{ batch.id }}</span>
                <h2>
                  {{ batch.schoolName }} · {{ batch.totalCount }} 个报名号
                </h2>
              </div>
              <StatusBadge :value="batch.status" />
            </header>
            <div class="chip-list">
              <span v-for="quota in batch.quotas" :key="quota.classId"
                >{{ quota.className }}<small>{{ quota.count }} 人</small></span
              >
            </div>
            <div v-if="batch.status === 'pending'" class="row-decision">
              <input
                v-model="reviewNotes[batch.id]"
                placeholder="审批意见"
              /><button @click="reviewBatch(batch, 'rejected')">退回</button
              ><button @click="reviewBatch(batch, 'approved')">通过</button>
            </div>
            <ExcelActionBar
              v-if="batch.status === 'approved'"
              resource="account_results"
              label="账号下发清单"
              :template="false"
              :importable="false"
              :batch-id="batch.id"
            />
          </article>
        </div>
        <LedgerPager
          v-model:page="batchLedger.page"
          v-model:page-size="batchLedger.pageSize"
          :total="batchLedger.total"
        />
      </section>
    </template>

    <template v-else-if="page === 'candidates'">
      <ExcelActionBar
        resource="candidates"
        label="考生资料"
        @import="importExcel('candidates', $event)"
      />
      <form
        v-if="sessionStore.state.user?.adminLevel === 'school'"
        class="archive-console-vue"
        @submit.prevent="archiveCandidates"
      >
        <div>
          <p>账号归档</p>
          <h2>按班级或年级归档账户</h2>
          <span>只冻结登录，不删除报名、准考证、成绩和审计记录。</span>
        </div>
        <select v-model="archiveForm.scopeType">
          <option value="class">按班级</option>
          <option value="grade">按年级</option></select
        ><select v-model="archiveForm.scopeValue" required>
          <option value="">请选择范围</option>
          <option
            v-for="item in archiveForm.scopeType === 'class'
              ? data.classes
              : [...new Set((data.classes || []).map((row) => row.grade))].map(
                  (grade) => ({ id: grade, name: grade }),
                )"
            :key="item.id"
            :value="item.id"
          >
            {{ item.grade ? `${item.grade} · ${item.name}` : item.name }}
          </option></select
        ><select v-model="archiveForm.archived">
          <option :value="true">归档账户</option>
          <option :value="false">恢复账户</option></select
        ><button class="app-button app-button--primary">执行</button>
      </form>
      <section class="record-panel candidate-ledger">
        <header>
          <div>
            <h2>考生资料审核台账</h2>
            <p>
              筛选结果 {{ candidateLedger.total }} / 共
              {{ data.candidates?.length || 0 }} 人
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>关键词</span
            ><input
              v-model="candidateLedger.query"
              placeholder="姓名、报名号、证件号、科目" /></label
          ><label
            ><span>学校</span
            ><select v-model="candidateLedger.filters.school">
              <option value="">全部学校</option>
              <option v-for="item in candidateSchools" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>年级</span
            ><select v-model="candidateLedger.filters.grade">
              <option value="">全部年级</option>
              <option v-for="item in candidateGrades" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>班级</span
            ><select v-model="candidateLedger.filters.class">
              <option value="">全部班级</option>
              <option v-for="item in candidateClasses" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>关联考试</span
            ><select v-model="candidateLedger.filters.exam">
              <option value="">全部考试</option>
              <option
                v-for="exam in data.exams"
                :key="exam.id"
                :value="exam.id"
              >
                {{ exam.name }}
              </option>
            </select></label
          ><label
            ><span>资料状态</span
            ><select v-model="candidateLedger.filters.status">
              <option value="">全部状态</option>
              <option
                v-for="status in [
                  'pending',
                  'approved',
                  'rejected',
                  'archived',
                ]"
                :key="status"
                :value="status"
              >
                {{ candidateStatusLabel(status) }}
              </option>
            </select></label
          ><button class="table-action" @click="candidateLedger.clear">
            清除筛选
          </button>
        </div>
        <div class="ledger-bulk">
          <label
            ><input
              type="checkbox"
              @change="
                toggleCurrentPage(candidateSelected, candidateLedger.rows)
              "
            />
            选择当前页可处理项</label
          ><strong>已选 {{ candidateSelected.length }} 人</strong
          ><button
            class="table-action"
            :disabled="!candidateSelected.length || busy"
            @click="bulkReview('candidates', candidateSelected, 'rejected')"
          >
            批量退回</button
          ><button
            class="table-action table-action--primary"
            :disabled="!candidateSelected.length || busy"
            @click="bulkReview('candidates', candidateSelected, 'approved')"
          >
            批量通过</button
          ><button
            class="table-action"
            :disabled="!candidateSelected.length"
            @click="exportCandidatesSelected"
          >
            导出选中项
          </button>
        </div>
        <div class="table-scroll">
          <table>
            <thead>
              <tr>
                <th>选择</th>
                <th>考生</th>
                <th>证件</th>
                <th>学校班级</th>
                <th>关联考试</th>
                <th>账户</th>
                <th>状态</th>
                <th>审核</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in candidateLedger.rows" :key="item.id">
                <td>
                  <input
                    v-model="candidateSelected"
                    type="checkbox"
                    :value="item.id"
                  />
                </td>
                <td>
                  <strong>{{ item.name }}</strong
                  ><small>{{ item.candidateNumber }}</small>
                </td>
                <td>{{ item.idNumberMasked }}</td>
                <td>
                  {{ item.school
                  }}<small
                    >{{ candidateClass(item)?.grade }} ·
                    {{ candidateClass(item)?.name || item.grade }}</small
                  >
                </td>
                <td>
                  <span
                    v-for="registration in item.registrations"
                    :key="registration.id"
                    class="table-stack"
                    >{{ registration.exam?.name }} ·
                    {{
                      registration.subjects
                        ?.map((subject) => subject.name)
                        .join("、")
                    }}</span
                  ><span v-if="!item.registrations?.length">暂无报名</span>
                </td>
                <td>
                  {{
                    item.accountArchived
                      ? "已归档"
                      : item.mustChangePassword
                        ? "待首次改密"
                        : "正常"
                  }}
                </td>
                <td><StatusBadge :value="item.status" /></td>
                <td>
                  <div class="row-decision">
                    <input
                      v-model="reviewNotes[item.id]"
                      placeholder="审核意见"
                    /><button @click="reviewCandidate(item, 'rejected')">
                      退回</button
                    ><button @click="reviewCandidate(item, 'approved')">
                      通过</button
                    ><button
                      v-if="sessionStore.state.user?.adminLevel === 'super'"
                      @click="resetCandidate(item)"
                    >
                      重置密码
                    </button>
                  </div>
                </td>
              </tr>
              <tr v-if="!candidateLedger.rows.length">
                <td class="table-empty" colspan="8">没有符合当前条件的考生</td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="candidateLedger.page"
          v-model:page-size="candidateLedger.pageSize"
          :total="candidateLedger.total"
        />
      </section>
    </template>

    <template v-else-if="page === 'indicator-qualifications'">
      <QualificationLedger
        v-for="group in data.exams"
        :key="group.exam?.id || group.id"
        :group="group"
        :busy="busy"
        @save="saveQualification(group, $event.item, $event.eligible)"
        @bulk="bulkQualification(group, $event.eligible, $event.userIds)"
      />
    </template>

    <template v-else-if="page === 'registrations'">
      <ExcelActionBar
        resource="registrations"
        label="考试报名台账"
        :template="false"
        :importable="false"
      />
      <section class="record-panel">
        <header>
          <div>
            <h2>考试报名审核台账</h2>
            <p>
              筛选结果 {{ registrationLedger.total }} / 共
              {{ data.registrations?.length || 0 }} 条
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>关键词</span
            ><input
              v-model="registrationLedger.query"
              placeholder="考生、报名号、考试或科目" /></label
          ><label
            ><span>状态</span
            ><select v-model="registrationLedger.filters.status">
              <option value="">全部状态</option>
              <option value="pending">待审核</option>
              <option value="approved">已通过</option>
              <option value="rejected">已退回</option>
            </select></label
          ><label
            ><span>考试</span
            ><select v-model="registrationLedger.filters.exam">
              <option value="">全部考试</option>
              <option
                v-for="exam in registrationExams"
                :key="exam.id"
                :value="exam.id"
              >
                {{ exam.name }}
              </option>
            </select></label
          ><label
            ><span>科目</span
            ><select v-model="registrationLedger.filters.subject">
              <option value="">全部科目</option>
              <option
                v-for="subject in registrationSubjects"
                :key="subject.id || subject.name"
                :value="subject.id || subject.name"
              >
                {{ subject.name }}
              </option>
            </select></label
          ><label
            ><span>学校</span
            ><select v-model="registrationLedger.filters.school">
              <option value="">全部学校</option>
              <option v-for="item in registrationSchools" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>年级</span
            ><select v-model="registrationLedger.filters.grade">
              <option value="">全部年级</option>
              <option v-for="item in registrationGrades" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>班级</span
            ><select v-model="registrationLedger.filters.class">
              <option value="">全部班级</option>
              <option v-for="item in registrationClasses" :key="item">
                {{ item }}
              </option>
            </select></label
          >
        </div>
        <div class="ledger-bulk">
          <label
            ><input
              type="checkbox"
              @change="
                toggleCurrentPage(registrationSelected, registrationLedger.rows)
              "
            />
            选择当前页可处理项</label
          ><strong>已选 {{ registrationSelected.length }} 条</strong
          ><button
            class="table-action"
            :disabled="!registrationSelected.length || busy"
            @click="
              bulkReview('registrations', registrationSelected, 'rejected')
            "
          >
            批量退回</button
          ><button
            class="table-action table-action--primary"
            :disabled="!registrationSelected.length || busy"
            @click="
              bulkReview('registrations', registrationSelected, 'approved')
            "
          >
            批量通过</button
          ><button
            class="table-action"
            :disabled="!registrationSelected.length"
            @click="exportRegistrationsSelected"
          >
            导出选中项
          </button>
        </div>
        <div class="table-scroll">
          <table>
            <thead>
              <tr>
                <th>选择</th>
                <th>考生</th>
                <th>学校班级</th>
                <th>考试 / 科目</th>
                <th>缴费</th>
                <th>状态</th>
                <th>审核</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in registrationLedger.rows" :key="item.id">
                <td>
                  <input
                    v-model="registrationSelected"
                    type="checkbox"
                    :value="item.id"
                  />
                </td>
                <td>
                  <strong>{{
                    item.candidate?.name || item.candidateName
                  }}</strong
                  ><small>{{ item.registrationNumber }}</small>
                </td>
                <td>
                  {{ item.schoolName
                  }}<small>{{ item.gradeName }} · {{ item.className }}</small>
                </td>
                <td>
                  {{ item.exam?.name || item.examName
                  }}<small>{{
                    item.subjects?.map((row) => row.name).join("、")
                  }}</small>
                </td>
                <td><StatusBadge :value="item.paymentStatus" /></td>
                <td><StatusBadge :value="item.status" /></td>
                <td>
                  <div class="row-decision">
                    <input
                      v-model="reviewNotes[item.id]"
                      placeholder="审核意见"
                    /><button @click="reviewRegistration(item, 'rejected')">
                      退回</button
                    ><button @click="reviewRegistration(item, 'approved')">
                      通过
                    </button>
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="registrationLedger.page"
          v-model:page-size="registrationLedger.pageSize"
          :total="registrationLedger.total"
        />
      </section>
    </template>

    <template v-else-if="page === 'payments'">
      <ExcelActionBar
        resource="payments"
        label="缴费名单"
        :template="false"
        :importable="false"
      />
      <section class="record-metrics">
        <article>
          <span>报名人数</span><strong>{{ paymentSource.length }}</strong>
        </article>
        <article>
          <span>待缴费</span
          ><strong>{{ paymentSource.length - paidCount }}</strong>
        </article>
        <article>
          <span>已缴费</span><strong>{{ paidCount }}</strong>
        </article>
        <article>
          <span>应缴合计</span><strong>¥ {{ paymentTotal.toFixed(2) }}</strong>
        </article>
        <article>
          <span>已缴合计</span><strong>¥ {{ paidTotal.toFixed(2) }}</strong>
        </article>
      </section>
      <section class="record-panel">
        <header>
          <div>
            <h2>线下缴费台账</h2>
            <p>
              筛选结果 {{ paymentLedger.total }} / 共
              {{ paymentSource.length }} 条
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>关键词</span
            ><input
              v-model="paymentLedger.query"
              placeholder="考生、报名号、考试、科目" /></label
          ><label
            ><span>缴费状态</span
            ><select v-model="paymentLedger.filters.status">
              <option value="">全部状态</option>
              <option value="unpaid">待缴费</option>
              <option value="paid">已缴费</option>
            </select></label
          ><label
            ><span>考试</span
            ><select v-model="paymentLedger.filters.exam">
              <option value="">全部考试</option>
              <option
                v-for="exam in paymentExams"
                :key="exam.id"
                :value="exam.id"
              >
                {{ exam.name }}
              </option>
            </select></label
          ><label
            ><span>学校</span
            ><select v-model="paymentLedger.filters.school">
              <option value="">全部学校</option>
              <option v-for="item in paymentSchools" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>年级</span
            ><select v-model="paymentLedger.filters.grade">
              <option value="">全部年级</option>
              <option v-for="item in paymentGrades" :key="item">
                {{ item }}
              </option>
            </select></label
          ><label
            ><span>班级</span
            ><select v-model="paymentLedger.filters.class">
              <option value="">全部班级</option>
              <option v-for="item in paymentClasses" :key="item">
                {{ item }}
              </option>
            </select></label
          >
        </div>
        <div class="ledger-bulk">
          <label
            ><input
              type="checkbox"
              @change="
                toggleCurrentPage(
                  paymentSelected,
                  paymentLedger.rows,
                  (item) => true,
                )
              "
            />
            选择当前页</label
          ><strong>已选 {{ paymentSelected.length }} 条</strong
          ><button
            v-if="data.canUpdatePayment !== false"
            class="table-action"
            :disabled="!paymentSelected.length || busy"
            @click="bulkPayments('unpaid')"
          >
            批量改为待缴费</button
          ><button
            v-if="data.canUpdatePayment !== false"
            class="table-action table-action--primary"
            :disabled="!paymentSelected.length || busy"
            @click="bulkPayments('paid')"
          >
            批量标记已缴费</button
          ><button
            class="table-action"
            :disabled="!paymentSelected.length"
            @click="exportPaymentsSelected"
          >
            导出选中项
          </button>
        </div>
        <div class="table-scroll">
          <table>
            <thead>
              <tr>
                <th>选择</th>
                <th>考生 / 报名号</th>
                <th>学校班级</th>
                <th>考试 / 科目</th>
                <th>应缴金额</th>
                <th>状态</th>
                <th>确认记录</th>
                <th>更新</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in paymentLedger.rows" :key="item.id">
                <td>
                  <input
                    v-model="paymentSelected"
                    type="checkbox"
                    :value="item.id"
                  />
                </td>
                <td>
                  <strong>{{
                    item.candidate?.name || item.candidateName
                  }}</strong
                  ><small>{{ item.registrationNumber }}</small>
                </td>
                <td>
                  {{ item.schoolName
                  }}<small>{{ item.gradeName }} · {{ item.className }}</small>
                </td>
                <td>
                  {{ item.exam?.name || item.examName
                  }}<small>{{
                    item.subjects?.map((subject) => subject.name).join("、")
                  }}</small>
                </td>
                <td>¥ {{ Number(item.amountDue || 0).toFixed(2) }}</td>
                <td><StatusBadge :value="item.paymentStatus" /></td>
                <td>
                  {{ formatDate(item.paidAt, true) }}<small>{{ item.paidByName }}</small>
                </td>
                <td>
                  <template
                    v-if="
                      data.canUpdatePayment !== false && !item.exam?.archivedAt
                    "
                    ><button
                      class="table-action"
                      @click="updatePayment(item, 'unpaid')"
                    >
                      标记待缴</button
                    ><button
                      class="table-action table-action--primary"
                      @click="updatePayment(item, 'paid')"
                    >
                      确认已缴
                    </button></template
                  ><span v-else>只读</span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="paymentLedger.page"
          v-model:page-size="paymentLedger.pageSize"
          :total="paymentLedger.total"
        />
      </section>
    </template>

    <AccountSecurity
      v-else-if="page === 'security'"
      :status="data"
      @updated="emit('reload')"
    />
  </div>
</template>
