<script setup>
import { computed, reactive, ref } from "vue";
import NoticeRichTextEditor from "@/components/admin/NoticeRichTextEditor.vue";
import ExcelActionBar from "@/components/common/ExcelActionBar.vue";
import LedgerPager from "@/components/common/LedgerPager.vue";
import StatusBadge from "@/components/common/StatusBadge.vue";
import { useLedger } from "@/composables/useLedger";
import { api } from "@/lib/api";
import { adminLevelLabel, workflowTypeLabel } 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";
import { chinaRegions } from "@/data/china-regions";

const props = defineProps({
  page: { type: String, required: true },
  data: { type: Object, default: () => ({}) },
});
const emit = defineEmits(["reload"]);
const busy = ref(false);
const error = ref("");
const search = ref("");
const centerSelected = ref([]);
const centerRequestSelected = ref([]);
const notes = reactive({});
const notice = reactive({
  id: "",
  category: "报名通知",
  status: "draft",
  title: "",
  summary: "",
  content: "",
  pinned: false,
});
const center = reactive({
  id: "",
  schoolId: "",
  code: "",
  name: "",
  provinceCode: "",
  cityCode: "",
  districtCode: "",
  address: "",
  managerName: "",
  managerPhone: "",
  contact: "",
  emergencyPhone: "",
  gateOpenTime: "",
  status: "active",
  transport: "",
  notes: "",
  rooms: [
    {
      code: "",
      name: "",
      building: "",
      floor: "",
      capacity: 30,
      seatPlan: "",
      roomType: "standard",
      status: "active",
      notes: "",
    },
  ],
});
const rule = reactive({
  id: "",
  name: "固定报名号规则",
  separator: "-",
  year: true,
  school_code: true,
  gender: false,
  literal: false,
  literalValue: "",
  yearWidth: 4,
  sequenceWidth: 4,
});
const centerCities = computed(
  () =>
    chinaRegions.find((item) => item.code === center.provinceCode)?.cities ||
    [],
);
const centerDistricts = computed(
  () =>
    centerCities.value.find((item) => item.code === center.cityCode)
      ?.districts || [],
);
const centerLedger = useLedger(() => props.data.centers || [], {
  filters: {
    status: (item, value) => item.status === value,
    school: (item, value) => item.schoolId === value,
  },
  searchText: (item) =>
    [
      item.code,
      item.name,
      item.schoolName,
      item.address,
      item.managerName,
      item.managerPhone,
      ...(item.rooms || []).map(
        (room) => `${room.code} ${room.name} ${room.building}`,
      ),
    ].join(" "),
});
const centerRequestLedger = useLedger(() => props.data.changeRequests || [], {
  filters: {
    status: (item, value) => item.status === value,
    type: (item, value) => item.requestType === value,
  },
  searchText: (item) =>
    [item.name, item.code, item.schoolName, item.requestType, item.status].join(
      " ",
    ),
});
const flowLedger = useLedger(() => props.data.instances || [], {
  filters: {
    status: (item, value) => item.status === value,
    type: (item, value) => item.businessType === value,
  },
  searchText: (item) =>
    [
      item.candidateName,
      item.centerName,
      item.schoolName,
      item.examName,
      item.className,
      item.assignee?.displayName,
      item.businessType,
    ].join(" "),
});
const noticeLedger = useLedger(() => props.data.notices || [], {
  filters: {
    status: (item, value) => item.status === value,
    category: (item, value) => item.category === value,
  },
  searchText: (item) =>
    [item.title, item.summary, item.category, item.author].join(" "),
});
const publicationLedger = useLedger(() => props.data.publications || [], {
  filters: {
    visibility: (item, value) =>
      value === "visible"
        ? isPublicationVisible(item)
        : !isPublicationVisible(item),
    type: (item, value) => item.sourceType === value,
  },
  searchText: (item) =>
    [item.title, item.sourceType, item.examName, item.schoolName].join(" "),
});
const noticeCategories = computed(() => [
  ...new Set(
    [
      "报名通知",
      "考试须知",
      "考点公告",
      "成绩通知",
      "系统公告",
      ...(props.data.notices || []).map((item) => item.category),
    ].filter(Boolean),
  ),
]);
const publicationTypes = computed(() => [
  ...new Set(
    (props.data.publications || [])
      .map((item) => item.sourceType)
      .filter(Boolean),
  ),
]);
function publicationTypeLabel(value) {
  return (
    {
      plan: "招生计划",
      qualification: "指标资格",
      admission: "录取结果",
      cutoff: "录取分数线",
      reporting: "报到情况",
    }[value] || value
  );
}
function isPublicationVisible(item) {
  if (typeof item?.publicVisible === "boolean") return item.publicVisible;
  if (typeof item?.visible === "boolean") return item.visible;
  return item?.status === "visible";
}
const flowTypes = computed(() => [
  ...new Set(
    (props.data.instances || [])
      .map((item) => item.businessType)
      .filter(Boolean),
  ),
]);

function togglePageSelection(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 > 0 && 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;
}

async function act(action, success) {
  busy.value = true;
  error.value = "";
  try {
    const result = await action();
    if (success) uiStore.notify(success);
    emit("reload");
    return result;
  } catch (requestError) {
    error.value = requestError.message;
    return null;
  } finally {
    busy.value = false;
  }
}
function resetNotice() {
  Object.assign(notice, {
    id: "",
    category: "报名通知",
    status: "draft",
    title: "",
    summary: "",
    content: "",
    pinned: false,
  });
}
function editNotice(item) {
  Object.assign(notice, {
    id: item.id,
    category: item.category,
    status: item.status,
    title: item.title,
    summary: item.summary || "",
    content: item.contentHtml || item.content || "",
    pinned: Boolean(item.pinned),
  });
  window.scrollTo({ top: 0, behavior: "smooth" });
}
async function saveNotice() {
  const plainText = notice.content
    .replace(/<[^>]*>/g, " ")
    .replace(/&nbsp;/g, " ")
    .replace(/\s+/g, " ")
    .trim();
  if (!notice.title.trim() || !plainText) {
    error.value = "请填写通知标题和正文";
    return;
  }
  if (notice.content.length > 20_000) {
    error.value = "通知正文内容过长，请精简至 20000 个字符以内";
    return;
  }
  const editing = Boolean(notice.id);
  const result = await act(
    () =>
      api(
        notice.id ? `/api/admin/notices/${notice.id}` : "/api/admin/notices",
        {
          method: notice.id ? "PATCH" : "POST",
          body: {
            title: notice.title.trim(),
            summary: notice.summary.trim(),
            category: notice.category,
            status: notice.status,
            content: notice.content,
            pinned: notice.pinned,
          },
        },
      ),
    notice.status === "published"
      ? editing
        ? "已发布通知已更新"
        : "通知已发布"
      : editing
        ? "通知草稿已更新"
        : "通知草稿已保存",
  );
  if (result) resetNotice();
}
function toggleNotice(item) {
  act(
    () =>
      api(`/api/admin/notices/${item.id}`, {
        method: "PATCH",
        body: { status: item.status === "published" ? "draft" : "published" },
      }),
    item.status === "published" ? "通知已撤回" : "通知已发布",
  );
}
function togglePublication(item) {
  const currentlyVisible = isPublicationVisible(item);
  act(
    () =>
      api(`/api/admin/publications/${item.sourceType}/${item.id}`, {
        method: "PATCH",
        body: { publicVisible: !currentlyVisible },
      }),
    currentlyVisible ? "系统公示已隐藏" : "系统公示已公开",
  );
}
function addRoom() {
  center.rooms.push({
    code: "",
    name: "",
    building: "",
    floor: "",
    capacity: 30,
    seatPlan: "",
    roomType: "standard",
    status: "active",
    notes: "",
  });
}
function resetCenter() {
  Object.assign(center, {
    id: "",
    schoolId: "",
    code: "",
    name: "",
    provinceCode: "",
    cityCode: "",
    districtCode: "",
    address: "",
    managerName: "",
    managerPhone: "",
    contact: "",
    emergencyPhone: "",
    gateOpenTime: "",
    status: "active",
    transport: "",
    notes: "",
    rooms: [
      {
        code: "",
        name: "",
        building: "",
        floor: "",
        capacity: 30,
        seatPlan: "",
        roomType: "standard",
        status: "active",
        notes: "",
      },
    ],
  });
}
function editCenter(item) {
  Object.assign(center, {
    ...item,
    id: item.id,
    rooms: (item.rooms || []).map((room) => ({ ...room })),
  });
  window.scrollTo({ top: 0, behavior: "smooth" });
}
function saveCenter() {
  const editing = Boolean(center.id);
  act(
    () =>
      api(editing ? `/api/admin/centers/${center.id}` : "/api/admin/centers", {
        method: editing ? "PATCH" : "POST",
        body: {
          ...center,
          rooms: center.rooms.map((room) => ({
            ...room,
            capacity: Number(room.capacity),
          })),
        },
      }),
    editing ? "考点变更已提交审批" : "新考点档案已提交审批",
  );
}
function reviewCenterRequest(item, status) {
  act(
    () =>
      api(`/api/admin/center-change-requests/${item.id}`, {
        method: "PATCH",
        body: { status, reviewNote: notes[item.id] || "" },
      }),
    status === "approved" ? "考点变更已通过" : "考点变更已退回",
  );
}
async function importCenters({ file, input }) {
  await act(
    () =>
      api("/api/admin/excel/centers", {
        method: "POST",
        headers: {
          "Content-Type":
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        },
        body: file,
      }),
    "考点考场 Excel 已导入",
  );
  input.value = "";
}
function exportCentersSelected() {
  downloadSelectedExcel("centers", centerSelected.value);
}
async function bulkCenterRequests(status) {
  const rows = (props.data.changeRequests || []).filter(
    (item) =>
      centerRequestSelected.value.includes(item.id) &&
      item.status === "pending",
  );
  if (!rows.length) return;
  const note = window.prompt(
    status === "rejected" ? "请填写统一退回原因" : "填写统一审批意见（可留空）",
    "",
  );
  if (note === null || (status === "rejected" && !note.trim())) return;
  busy.value = true;
  error.value = "";
  let completed = 0;
  try {
    for (const item of rows) {
      await api(`/api/admin/center-change-requests/${item.id}`, {
        method: "PATCH",
        body: { status, reviewNote: note },
      });
      completed += 1;
    }
    uiStore.notify(`已处理 ${completed} 条考点变更申请`);
    centerRequestSelected.value = [];
    emit("reload");
  } catch (requestError) {
    error.value = `${completed} 条已完成；${requestError.message}`;
  } finally {
    busy.value = false;
  }
}
function flowPath(item) {
  return item.businessType === "profile_change"
    ? `/api/admin/candidates/${item.businessId}`
    : item.businessType === "registration_review"
      ? `/api/admin/registrations/${item.businessId}`
      : item.businessType === "center_change"
        ? `/api/admin/center-change-requests/${item.businessId}`
        : item.businessType === "candidate_account_batch"
          ? `/api/admin/candidate-account-batches/${item.businessId}`
          : `/api/admin/score-appeals/${item.businessId}`;
}
function processFlow(item, status) {
  const body = { status, reviewNote: notes[item.id] || "" };
  if (item.businessType === "score_appeal" && status === "approved")
    body.reviewedScore = Number(
      window.prompt("请输入复议后的成绩", item.appealResult?.score ?? "") ||
        item.appealResult?.score,
    );
  act(
    () => api(flowPath(item), { method: "PATCH", body }),
    status === "approved" ? "流程已通过当前步骤" : "流程已退回",
  );
}
function transferFlow(item) {
  const assigneeId = window.prompt("请输入目标管理员 ID", "") || "";
  if (assigneeId)
    act(
      () =>
        api(`/api/admin/workflow-instances/${item.id}/transfer`, {
          method: "PATCH",
          body: { assigneeId, note: notes[item.id] || "" },
        }),
      "流程已转交",
    );
}
function saveWorkflow(workflow) {
  act(
    () =>
      api(`/api/admin/workflows/${workflow.businessType}`, {
        method: "PUT",
        body: {
          name: workflow.name,
          steps: workflow.steps.map((step) => ({
            name: step.name,
            adminLevel: step.adminLevel,
          })),
        },
      }),
    "审批流程已保存",
  );
}
function addWorkflowStep(workflow) {
  workflow.steps.push({
    name: "新增审批步骤",
    adminLevel: "school",
    position: workflow.steps.length + 1,
  });
}
function saveRule() {
  const definitions = [
    { type: "year", include: rule.year, width: Number(rule.yearWidth) },
    { type: "school_code", include: rule.school_code },
    { type: "gender", include: rule.gender },
    { type: "literal", include: rule.literal, value: rule.literalValue },
    { type: "sequence", include: true, width: Number(rule.sequenceWidth) },
  ];
  const segments = definitions
    .filter((item) => item.include)
    .map((item, index) => ({
      type: item.type,
      position: index + 1,
      value: item.value || "",
      width: item.width || 0,
    }));
  act(
    () =>
      api("/api/admin/number-rules", {
        method: "POST",
        body: {
          id: rule.id || undefined,
          name: rule.name,
          separator: rule.separator,
          segments,
        },
      }),
    "报名号规则已启用",
  );
}
if (props.page === "number-rules" && props.data.activeRule) {
  const active = props.data.activeRule;
  rule.id = active.id;
  rule.name = active.name;
  rule.separator = active.separator;
  for (const segment of active.segments || []) {
    rule[segment.type] = true;
    if (segment.type === "literal") rule.literalValue = segment.value;
    if (segment.type === "year") rule.yearWidth = segment.width;
    if (segment.type === "sequence") rule.sequenceWidth = segment.width;
  }
}
</script>

<template>
  <div class="admin-system-workspace">
    <div v-if="error" class="form-error">{{ error }}</div>
    <section
      v-if="page === 'centers' && data.centers?.length"
      class="record-panel center-edit-picker"
    >
      <header>
        <div>
          <h2>维护已有考点</h2>
          <p>选择考点后，下面的档案表单会切换为变更申请。</p>
        </div>
        <button
          v-if="center.id"
          class="app-button"
          type="button"
          @click="resetCenter"
        >
          取消编辑
        </button>
      </header>
      <div class="chip-list">
        <button
          v-for="item in data.centers"
          :key="item.id"
          type="button"
          :class="{ active: center.id === item.id }"
          @click="editCenter(item)"
        >
          {{ item.code }} · {{ item.name }}
        </button>
      </div>
      <div v-if="center.id" class="form-callout">
        <strong>正在提交“{{ center.name }}”的变更</strong>
        <p>审批通过前，当前正式考点档案不会变化。</p>
      </div>
    </section>
    <template v-if="page === 'notices'"
      ><form
        class="business-form notice-editor-vue notice-editor-studio"
        @submit.prevent="saveNotice"
      >
        <header class="notice-editor-studio__header">
          <div>
            <p>官方通知发布台</p>
            <h2>{{ notice.id ? "编辑人工通知" : "起草新的通知公告" }}</h2>
            <span
              >正文由 CKEditor 编辑，发布前由服务端统一清理不安全内容。</span
            >
          </div>
          <div class="notice-editor-studio__identity">
            <small>{{ notice.id ? "正在修改" : "新建文稿" }}</small>
            <strong>{{ notice.id || "尚未生成文号" }}</strong>
          </div>
        </header>
        <div class="notice-editor-studio__body">
          <section class="notice-editor-studio__manuscript">
            <label class="notice-title-field"
              ><span>通知标题</span
              ><input
                v-model="notice.title"
                maxlength="120"
                placeholder="输入完整、明确的通知标题"
                required
            /></label>
            <label
              ><span>首页摘要</span
              ><textarea
                v-model="notice.summary"
                rows="2"
                maxlength="260"
                placeholder="用于首页和公告目录，留空时将自动截取正文"
              ></textarea>
            </label>
            <label class="notice-editor-label"><span>通知正文</span></label>
            <NoticeRichTextEditor v-model="notice.content" :disabled="busy" />
          </section>
          <aside class="notice-editor-studio__settings">
            <div>
              <small>发布设置</small>
              <h3>公开范围与状态</h3>
              <p>保存草稿不会出现在公开首页；立即发布后公众可直接查看。</p>
            </div>
            <label
              ><span>公告分类</span
              ><select v-model="notice.category">
                <option
                  v-for="item in noticeCategories"
                  :key="item"
                  :value="item"
                >
                  {{ item }}
                </option>
              </select></label
            ><label
              ><span>保存后的状态</span
              ><select v-model="notice.status">
                <option value="draft">草稿，不公开</option>
                <option value="published">正式发布</option>
              </select></label
            ><label class="notice-pin-control"
              ><input v-model="notice.pinned" type="checkbox" />
              <span
                ><strong>公开首页置顶</strong
                ><small>仅对已发布通知生效</small></span
              ></label
            >
            <div
              class="notice-release-state"
              :class="{ 'is-published': notice.status === 'published' }"
            >
              <i></i>
              <span
                ><strong>{{
                  notice.status === "published" ? "将正式发布" : "将保存为草稿"
                }}</strong
                ><small>{{
                  notice.status === "published"
                    ? "提交后立即进入公开公告目录"
                    : "可继续编辑，不会对公众显示"
                }}</small></span
              >
            </div>
          </aside>
        </div>
        <footer class="notice-editor-studio__actions">
          <button
            v-if="notice.id"
            type="button"
            class="app-button"
            :disabled="busy"
            @click="resetNotice"
          >
            取消编辑
          </button>
          <button
            type="button"
            class="app-button"
            :disabled="busy"
            @click="resetNotice"
          >
            清空文稿
          </button>
          <button class="app-button app-button--primary" :disabled="busy">
            {{
              busy
                ? "正在保存…"
                : notice.status === "published"
                  ? notice.id
                    ? "更新已发布通知"
                    : "发布通知"
                  : notice.id
                    ? "更新草稿"
                    : "保存草稿"
            }}
          </button>
        </footer>
      </form>
      <section class="record-panel notice-ledger-panel">
        <header>
          <div>
            <h2>人工通知</h2>
            <p>
              筛选结果 {{ noticeLedger.total }} / 共
              {{ data.notices?.length || 0 }} 条
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>搜索通知</span
            ><input v-model="noticeLedger.query" placeholder="标题、摘要、作者"
          /></label>
          <label
            ><span>发布状态</span
            ><select v-model="noticeLedger.filters.status">
              <option value="">全部状态</option>
              <option value="draft">草稿</option>
              <option value="published">已发布</option>
            </select></label
          ><label
            ><span>公告分类</span
            ><select v-model="noticeLedger.filters.category">
              <option value="">全部分类</option>
              <option
                v-for="item in noticeCategories"
                :key="item"
                :value="item"
              >
                {{ item }}
              </option>
            </select></label
          ><button
            type="button"
            class="table-action"
            @click="noticeLedger.clear"
          >
            清除筛选
          </button>
        </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 noticeLedger.rows" :key="item.id">
                <td>
                  {{ item.title }}<small>{{ item.summary }}</small>
                </td>
                <td>{{ item.category }}</td>
                <td><StatusBadge :value="item.status" /></td>
                <td>{{ item.pinned ? "是" : "否" }}</td>
                <td>
                  {{ formatDate(item.publishAt || item.createdAt, true) }}
                </td>
                <td>
                  <button
                    type="button"
                    class="table-action"
                    @click="editNotice(item)"
                  >
                    编辑</button
                  ><a
                    v-if="item.status === 'published'"
                    class="table-action"
                    :href="'/announcements/' + item.id"
                    target="_blank"
                    rel="noopener"
                    >预览</a
                  ><button
                    type="button"
                    class="table-action"
                    @click="toggleNotice(item)"
                  >
                    {{ item.status === "published" ? "撤回" : "发布" }}
                  </button>
                </td>
              </tr>
              <tr v-if="!noticeLedger.rows.length">
                <td colspan="6" class="ledger-empty">没有符合条件的人工通知</td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="noticeLedger.page"
          v-model:page-size="noticeLedger.pageSize"
          :total="noticeLedger.total"
        />
      </section>
      <section class="record-panel notice-ledger-panel">
        <header>
          <div>
            <h2>系统自动公示</h2>
            <p>
              筛选结果 {{ publicationLedger.total }} / 共
              {{ data.publications?.length || 0 }} 条；业务事实不可编辑。
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>搜索公示</span
            ><input
              v-model="publicationLedger.query"
              placeholder="标题、考试、学校"
          /></label>
          <label
            ><span>公示类型</span
            ><select v-model="publicationLedger.filters.type">
              <option value="">全部类型</option>
              <option
                v-for="item in publicationTypes"
                :key="item"
                :value="item"
              >
                {{ publicationTypeLabel(item) }}
              </option>
            </select></label
          ><label
            ><span>公开状态</span
            ><select v-model="publicationLedger.filters.visibility">
              <option value="">全部状态</option>
              <option value="visible">正在公开</option>
              <option value="hidden">已隐藏</option>
            </select></label
          ><button
            type="button"
            class="table-action"
            @click="publicationLedger.clear"
          >
            清除筛选
          </button>
        </div>
        <div class="table-scroll">
          <table>
            <thead>
              <tr>
                <th>标题</th>
                <th>类型</th>
                <th>生成方式</th>
                <th>公开状态</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody>
              <tr
                v-for="item in publicationLedger.rows"
                :key="item.sourceType + '-' + item.id"
              >
                <td>{{ item.title }}</td>
                <td>{{ publicationTypeLabel(item.sourceType) }}</td>
                <td>系统自动生成</td>
                <td>
                  <span
                    class="status-badge"
                    :class="
                      isPublicationVisible(item)
                        ? 'is-approved'
                        : 'is-archived'
                    "
                    >{{
                      isPublicationVisible(item) ? "正在公开" : "已隐藏"
                    }}</span
                  >
                </td>
                <td>
                  <button
                    type="button"
                    class="table-action"
                    @click="togglePublication(item)"
                  >
                    {{ isPublicationVisible(item) ? "隐藏" : "公开" }}
                  </button>
                </td>
              </tr>
              <tr v-if="!publicationLedger.rows.length">
                <td colspan="5" class="ledger-empty">没有符合条件的系统公示</td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="publicationLedger.page"
          v-model:page-size="publicationLedger.pageSize"
          :total="publicationLedger.total"
        /></section
    ></template>

    <template v-else-if="page === 'centers'"
      ><ExcelActionBar
        resource="centers"
        label="考点考场档案"
        @import="importCenters" />
      <form
        class="business-form center-editor-vue"
        @submit.prevent="saveCenter"
      >
        <p>受控考点档案</p>
        <h2>提交新考点档案</h2>
        <span>提交后进入考点考场变更审批，通过前不会改动正式档案。</span
        ><label v-if="sessionStore.state.user?.adminLevel === 'super'"
          ><span>所属学校</span
          ><select v-model="center.schoolId" required>
            <option value="">请选择</option>
            <option
              v-for="school in data.schools"
              :key="school.id"
              :value="school.id"
            >
              {{ school.name }}
            </option>
          </select></label
        >
        <div class="form-grid">
          <label
            ><span>考点代码</span
            ><input v-model="center.code" required /></label
          ><label
            ><span>考点名称</span
            ><input v-model="center.name" required /></label
          ><label
            ><span>省份</span
            ><select
              v-model="center.provinceCode"
              required
              @change="
                center.cityCode = '';
                center.districtCode = '';
              "
            >
              <option value="">请选择</option>
              <option
                v-for="item in chinaRegions"
                :key="item.code"
                :value="item.code"
              >
                {{ item.name }}
              </option>
            </select></label
          ><label
            ><span>城市</span
            ><select
              v-model="center.cityCode"
              required
              :disabled="!center.provinceCode"
              @change="center.districtCode = ''"
            >
              <option value="">请选择</option>
              <option
                v-for="item in centerCities"
                :key="item.code"
                :value="item.code"
              >
                {{ item.name }}
              </option>
            </select></label
          ><label
            ><span>区县</span
            ><select
              v-model="center.districtCode"
              required
              :disabled="!center.cityCode"
            >
              <option value="">请选择</option>
              <option
                v-for="item in centerDistricts"
                :key="item.code"
                :value="item.code"
              >
                {{ item.name }}
              </option>
            </select></label
          ><label
            ><span>详细地址</span
            ><input v-model="center.address" required /></label
          ><label
            ><span>负责人</span><input v-model="center.managerName" /></label
          ><label
            ><span>负责人手机</span
            ><input v-model="center.managerPhone" /></label
          ><label><span>值班电话</span><input v-model="center.contact" /></label
          ><label
            ><span>应急电话</span
            ><input v-model="center.emergencyPhone" /></label
          ><label
            ><span>开放时间</span
            ><input v-model="center.gateOpenTime" type="time"
          /></label>
        </div>
        <label
          ><span>交通与入场提示</span
          ><textarea v-model="center.transport"></textarea>
        </label>
        <section class="room-editor-list">
          <header>
            <strong>考场明细</strong
            ><button type="button" @click="addRoom">＋ 添加考场</button>
          </header>
          <article v-for="(room, index) in center.rooms" :key="index">
            <div class="form-grid">
              <label
                ><span>场地代码</span
                ><input v-model="room.code" required /></label
              ><label
                ><span>考场名称</span
                ><input v-model="room.name" required /></label
              ><label
                ><span>楼栋</span
                ><input v-model="room.building" required /></label
              ><label><span>楼层</span><input v-model="room.floor" /></label
              ><label
                ><span>容量</span
                ><input
                  v-model="room.capacity"
                  type="number"
                  min="1"
                  required /></label
              ><label
                ><span>考场类型</span
                ><select v-model="room.roomType">
                  <option value="standard">标准考场</option>
                  <option value="computer">机考考场</option>
                  <option value="accessible">无障碍考场</option>
                  <option value="spare">备用考场</option>
                </select></label
              >
            </div>
            <button type="button" @click="center.rooms.splice(index, 1)">
              移除
            </button>
          </article>
        </section>
        <button class="app-button app-button--primary">提交审批</button>
      </form>
      <section class="record-panel">
        <header>
          <div>
            <h2>正式考点与考场</h2>
            <p>
              筛选结果 {{ centerLedger.total }} / 共
              {{ data.centers?.length || 0 }} 个考点
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>关键词</span
            ><input
              v-model="centerLedger.query"
              placeholder="代码、考点、学校、地址、负责人、考场" /></label
          ><label
            ><span>所属学校</span
            ><select v-model="centerLedger.filters.school">
              <option value="">全部学校</option>
              <option
                v-for="school in data.schools"
                :key="school.id"
                :value="school.id"
              >
                {{ school.name }}
              </option>
            </select></label
          ><label
            ><span>状态</span
            ><select v-model="centerLedger.filters.status">
              <option value="">全部状态</option>
              <option value="active">启用</option>
              <option value="inactive">停用</option>
            </select></label
          ><button class="table-action" @click="centerLedger.clear">
            清除筛选
          </button>
        </div>
        <div class="ledger-bulk">
          <label
            ><input
              type="checkbox"
              @change="togglePageSelection(centerSelected, centerLedger.rows)"
            />
            选择当前页</label
          ><strong>已选 {{ centerSelected.length }} 个</strong
          ><button
            :disabled="!centerSelected.length"
            @click="exportCentersSelected"
          >
            导出选中项 XLSX
          </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 centerLedger.rows" :key="item.id">
                <td>
                  <input
                    v-model="centerSelected"
                    type="checkbox"
                    :value="item.id"
                  />
                </td>
                <td>
                  {{ item.name }}<small>{{ item.code }}</small>
                </td>
                <td>{{ item.schoolName }}</td>
                <td>{{ item.address }}</td>
                <td>
                  {{ item.rooms?.length }} 个 / {{ item.totalCapacity }} 席
                </td>
                <td>
                  {{ item.managerName }}<small>{{ item.managerPhone }}</small>
                </td>
                <td><StatusBadge :value="item.status" /></td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="centerLedger.page"
          v-model:page-size="centerLedger.pageSize"
          :total="centerLedger.total"
        />
      </section>
      <section class="record-panel">
        <header>
          <div>
            <h2>考点变更审批台账</h2>
            <p>
              筛选结果 {{ centerRequestLedger.total }} / 共
              {{ data.changeRequests?.length || 0 }} 条
            </p>
          </div>
        </header>
        <div class="ledger-toolbar ledger-toolbar--wide">
          <label
            ><span>关键词</span
            ><input
              v-model="centerRequestLedger.query"
              placeholder="考点、代码、学校" /></label
          ><label
            ><span>申请类型</span
            ><select v-model="centerRequestLedger.filters.type">
              <option value="">全部类型</option>
              <option value="create">新增</option>
              <option value="update">修改</option>
            </select></label
          ><label
            ><span>审批状态</span
            ><select v-model="centerRequestLedger.filters.status">
              <option value="">全部状态</option>
              <option value="pending">待审批</option>
              <option value="approved">已通过</option>
              <option value="rejected">已退回</option>
            </select></label
          >
        </div>
        <div class="ledger-bulk">
          <label
            ><input
              type="checkbox"
              @change="
                togglePageSelection(
                  centerRequestSelected,
                  centerRequestLedger.rows,
                  (item) => item.status === 'pending',
                )
              "
            />
            选择当前页待审项</label
          ><strong>已选 {{ centerRequestSelected.length }} 条</strong
          ><button
            :disabled="!centerRequestSelected.length || busy"
            @click="bulkCenterRequests('rejected')"
          >
            批量退回</button
          ><button
            :disabled="!centerRequestSelected.length || busy"
            @click="bulkCenterRequests('approved')"
          >
            批量通过
          </button>
        </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 centerRequestLedger.rows" :key="item.id">
                <td>
                  <input
                    v-model="centerRequestSelected"
                    type="checkbox"
                    :value="item.id"
                    :disabled="item.status !== 'pending'"
                  />
                </td>
                <td>{{ item.requestType === "create" ? "新增" : "修改" }}</td>
                <td>
                  {{ item.name }}<small>{{ item.schoolName }}</small>
                </td>
                <td>{{ item.rooms?.length }} 个</td>
                <td><StatusBadge :value="item.status" /></td>
                <td>
                  <div v-if="item.status === 'pending'" class="row-decision">
                    <input
                      v-model="notes[item.id]"
                      placeholder="审批意见"
                    /><button @click="reviewCenterRequest(item, 'rejected')">
                      退回</button
                    ><button @click="reviewCenterRequest(item, 'approved')">
                      通过
                    </button>
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <LedgerPager
          v-model:page="centerRequestLedger.page"
          v-model:page-size="centerRequestLedger.pageSize"
          :total="centerRequestLedger.total"
        /></section
    ></template>

    <template v-else-if="page === 'flows'"
      ><div class="ledger-toolbar ledger-toolbar--wide">
        <label
          ><span>关键词</span
          ><input
            v-model="flowLedger.query"
            placeholder="考生、学校、考试、责任人或流程" /></label
        ><label
          ><span>业务类型</span
          ><select v-model="flowLedger.filters.type">
            <option value="">全部类型</option>
            <option v-for="type in flowTypes" :key="type" :value="type">
              {{ workflowTypeLabel(type) }}
            </option>
          </select></label
        ><label
          ><span>流程状态</span
          ><select v-model="flowLedger.filters.status">
            <option value="">全部状态</option>
            <option value="pending">处理中</option>
            <option value="approved">已通过</option>
            <option value="rejected">已退回</option>
          </select></label
        ><button class="table-action" @click="flowLedger.clear">
          清除筛选
        </button>
      </div>
      <section class="workflow-grid-vue">
        <article
          v-for="item in flowLedger.rows"
          :key="item.id"
          class="record-panel flow-card-vue"
        >
          <header>
            <div>
              <span>{{ workflowTypeLabel(item.businessType) }}</span>
              <h2>
                {{
                  item.candidateName ||
                  item.centerName ||
                  item.schoolName ||
                  item.id
                }}
              </h2>
              <p>{{ item.examName }} {{ item.className }}</p>
            </div>
            <StatusBadge :value="item.status" />
          </header>
          <div class="workflow-track-vue">
            <span
              v-for="step in item.steps"
              :key="step.position"
              :class="{
                done: step.position < item.currentStep,
                current:
                  step.position === item.currentStep &&
                  item.status === 'pending',
              }"
              ><i>{{
                step.position < item.currentStep ? "✓" : step.position
              }}</i
              ><b>{{ step.name }}</b
              ><small>{{ adminLevelLabel(step.adminLevel) }}</small></span
            >
          </div>
          <footer>
            <div>
              <strong
                >当前责任人：{{
                  item.assignee?.displayName || "流程已结束"
                }}</strong
              ><input v-model="notes[item.id]" placeholder="处理意见" />
            </div>
            <button
              v-if="item.status === 'pending'"
              class="table-action"
              @click="transferFlow(item)"
            >
              转交</button
            ><button
              v-if="item.status === 'pending'"
              class="table-action"
              @click="processFlow(item, 'rejected')"
            >
              退回</button
            ><button
              v-if="item.status === 'pending'"
              class="table-action"
              @click="processFlow(item, 'approved')"
            >
              通过
            </button>
          </footer>
        </article>
      </section>
      <LedgerPager
        v-model:page="flowLedger.page"
        v-model:page-size="flowLedger.pageSize"
        :total="flowLedger.total"
    /></template>

    <template v-else-if="page === 'flow-design'"
      ><section class="workflow-design-grid-vue">
        <form
          v-for="workflow in data.workflows"
          :key="workflow.businessType"
          class="business-form"
          @submit.prevent="saveWorkflow(workflow)"
        >
          <header>
            <div>
              <p>{{ workflowTypeLabel(workflow.businessType) }}</p>
              <h2>{{ workflow.name }}</h2>
            </div>
            <button
              type="button"
              class="app-button"
              @click="addWorkflowStep(workflow)"
            >
              添加步骤
            </button>
          </header>
          <label
            ><span>流程名称</span><input v-model="workflow.name" required
          /></label>
          <div
            v-for="(step, index) in workflow.steps"
            :key="index"
            class="workflow-step-row-vue"
          >
            <b>{{ index + 1 }}</b
            ><input v-model="step.name" required /><select
              v-model="step.adminLevel"
            >
              <option value="class">班级管理员</option>
              <option value="school">校级管理员</option>
              <option value="super">超级管理员</option></select
            ><button type="button" @click="workflow.steps.splice(index, 1)">
              ×
            </button>
          </div>
          <button class="app-button app-button--primary">保存流程</button>
        </form>
      </section></template
    >

    <template v-else-if="page === 'number-rules'"
      ><section class="account-number-principle-vue">
        <span>一名考生 · 一个报名号</span>
        <h2>超级管理员只设计号码规则</h2>
        <p>学校按班级提交申领，最终批准后系统才创建长期考生账户。</p>
      </section>
      <div class="number-rule-layout-vue">
        <form class="business-form" @submit.prevent="saveRule">
          <h2>报名号组成</h2>
          <div class="form-grid">
            <label
              ><span>规则名称</span
              ><input v-model="rule.name" required /></label
            ><label
              ><span>分隔符</span><input v-model="rule.separator" maxlength="3"
            /></label>
          </div>
          <div class="rule-segment-grid">
            <label
              ><input v-model="rule.year" type="checkbox" /> 年份
              <input
                v-model="rule.yearWidth"
                type="number"
                min="2"
                max="6" /></label
            ><label
              ><input v-model="rule.school_code" type="checkbox" />
              学校代码</label
            ><label
              ><input v-model="rule.gender" type="checkbox" /> 性别代码（M/F/X）</label
            ><label
              ><input v-model="rule.literal" type="checkbox" /> 固定值
              <input v-model="rule.literalValue" /></label
            ><label
              ><input type="checkbox" checked disabled /> 流水号
              <input
                v-model="rule.sequenceWidth"
                type="number"
                min="1"
                max="12"
            /></label>
          </div>
          <button class="app-button app-button--primary">保存并启用规则</button>
        </form>
        <aside>
          <span>审批后账户样例</span
          ><strong>{{ data.preview || "2026-HZ01-X-0001" }}</strong>
          <p>报名号创建后保持不变。</p>
        </aside>
      </div></template
    >
  </div>
</template>
