# TAC 验证弹窗遮罩清理 Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 在 TAC 验证被取消、初始化异常或接口失败时,主动关闭认证页遮罩并清空残留的验证码 DOM。 **Architecture:** `public/js/captcha-pages.js` 作为验证码弹窗生命周期的唯一拥有者,新增打开和关闭挂载容器的函数。由于原 `public.css` 不是 UTF-8,无法使用补丁安全编辑,`public/css/captcha-modal.css` 作为新增公共样式文件只根据 `is-active` 类决定遮罩显示;三张认证页继续复用同一个页面级挂载点。 **Tech Stack:** 原生 JavaScript、Node `assert` 测试、layui 提示、TAC `public/tac/js/tac.min.js`。 ## Global Constraints - 计划创建时间:2026-07-10 09:10:03 +08:00。 - 仅修改 TAC 遮罩生命周期,不修改 `public/tac/js/tac.min.js`,不改变后端验证码类型选择。 - 新增或修改的 JS、CSS 注释使用中文;样式只写入 CSS 文件。 - 失败提示继续使用 `MessageUtil`/layui,禁止回退到浏览器原生 `alert`。 - 503/CORS 是服务端或跨域环境可用性问题;本任务只保证前端失败后不遗留遮罩。 --- ### Task 1: 用例锁定与生命周期工具 **Files:** - Modify: `tests/captcha-pages.test.js` - Modify: `public/js/captcha-pages.js` **Interfaces:** - Produces: `openCaptchaBox(box)`,清空旧内容、激活容器、设置 `aria-hidden="false"`。 - Produces: `closeCaptchaBox(box)`,清空内容、取消激活、设置 `aria-hidden="true"`。 - [x] **Step 1: 写失败测试** ```js CaptchaPages.openCaptchaBox(box); assert.strictEqual(box.classList.contains('is-active'), true); assert.strictEqual(box.attributes['aria-hidden'], 'false'); CaptchaPages.closeCaptchaBox(box); assert.strictEqual(box.classList.contains('is-active'), false); assert.strictEqual(box.attributes['aria-hidden'], 'true'); assert.strictEqual(box.innerHTML, ''); ``` - [x] **Step 2: 运行失败测试** Run: `node tests\\captcha-pages.test.js` Expected: FAIL,提示 `openCaptchaBox is not a function`。 - [x] **Step 3: 实现最小生命周期函数** ```js function openCaptchaBox(box) { if (!box) return; box.innerHTML = ''; box.classList.add('is-active'); box.setAttribute('aria-hidden', 'false'); } function closeCaptchaBox(box) { if (!box) return; box.innerHTML = ''; box.classList.remove('is-active'); box.setAttribute('aria-hidden', 'true'); } ``` - [x] **Step 4: 将关闭函数接入 TAC 成功、取消、初始化异常和 Promise 捕获路径** ```js if (captchaInstance && captchaInstance.destroyWindow) captchaInstance.destroyWindow(); closeCaptchaBox(box); ``` - [x] **Step 5: 运行单元测试与语法检查** Run: `node tests\\captcha-pages.test.js; node --check public\\js\\captcha-pages.js` Expected: 两条命令均通过。 ### Task 2: CSS 显示状态收口 **Files:** - Create: `public/css/captcha-modal.css` - Modify: `login.html` - Modify: `register.html` - Modify: `forgot-password.html` - Test: `tests/auth-page-structure.test.js` **Interfaces:** - Consumes: `.auth-captcha-modal.is-active`。 - Produces: 默认隐藏、激活后显示的页面级遮罩。 - [x] **Step 1: 写失败结构测试** ```js assert.ok(publicCss.includes('.auth-captcha-modal.is-active')); assert.ok(publicCss.includes('display: none;')); ``` - [x] **Step 2: 运行失败测试** Run: `node tests\\auth-page-structure.test.js` Expected: FAIL,缺少 `.auth-captcha-modal.is-active` 规则。 - [x] **Step 3: 实现 CSS 状态规则** ```css .auth-captcha-modal { display: none; } .auth-captcha-modal.is-active { display: grid; } ``` - [x] **Step 4: 运行结构测试** Run: `node tests\\auth-page-structure.test.js` Expected: PASS。 ### Task 3: 全量验证与记录 **Files:** - Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` - Modify: `docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md` - [x] **Step 1: 运行验证码相关和全量 Node 测试** Run: `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }` Expected: 所有测试通过。 - [x] **Step 2: 记录完成时间、修改文件和验证结果** ```markdown - [x] 完成时间:YYYY-MM-DD HH:mm:ss +08:00 - [x] 验证:相关测试、语法检查、全量 Node 测试均通过。 ``` ## 自检 - 需求覆盖:取消后遮罩清理、失败后遮罩清理、成功后遮罩清理、CSS 显示状态和回归测试均有对应任务。 - 占位扫描:无 `TODO`、`TBD` 或未定义接口。 - 接口一致性:JS 导出名、测试调用名和 CSS 类名统一为 `openCaptchaBox`、`closeCaptchaBox`、`is-active`。 ## 完成记录 - 完成时间:2026-07-10 09:15:55 +08:00。 - 修改文件:`public/js/captcha-pages.js`、`public/css/captcha-modal.css`、`login.html`、`register.html`、`forgot-password.html`、`tests/captcha-pages.test.js`、`tests/auth-page-structure.test.js`。 - 验证:`node tests\\captcha-pages.test.js`、`node tests\\auth-page-structure.test.js`、`node --check public\\js\\captcha-pages.js` 和全量 `tests/*.test.js` 均通过。 - 已知边界:测试域名的 503、CORS 或 TLS 可用性问题仍需后端或网关处理;该前端修复保证取消或异常后不残留页面遮罩。