修改功,现在补齐页面

This commit is contained in:
2026-07-11 21:36:03 +08:00
parent db397a7917
commit a6038be376
143 changed files with 7218 additions and 11912 deletions
@@ -1,128 +0,0 @@
# Auth Layer Message 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:** 让登录、注册、忘记密码三页的提示统一使用 layui `layer.msg`,不再出现浏览器原生 alert,并补好认证页提示和 TAC 弹窗样式。
**Architecture:** 页面加载 `public/layui/css/layui.css``public/layui/layui.js``auth-pages.js``captcha-pages.js` 只负责调用提示,不写样式;提示皮肤与 TAC 弹窗样式写入 `public/css/public.css`
**Tech Stack:** 原生 HTML/CSS/JavaScript、layui layer、public/tac、Node 断言测试。
## Global Constraints
- 计划开始时间:2026-07-09 17:14:08 +08:00。
- 只处理登录、注册、忘记密码三页的提示与弹窗样式问题。
- 不修改 `public/tac/js/tac.min.js`
- 不使用 HTML 原生 `alert` 作为用户提示。
- 样式写在 CSS 文件里,JS 只切 class 或调用组件。
- 新增或修改的 HTML、CSS、JS 注释使用中文,代码保持可读。
---
### Task 1: 补充失败测试
**Files:**
- Modify: `tests/auth-page-structure.test.js`
- Create: `tests/auth-message.test.js`
**Interfaces:**
- Consumes: 当前三页 HTML 与认证脚本。
- Produces: 能捕获缺少 layui 和原生 alert 兜底的测试。
- [x] **Step 1: 添加页面结构断言**
检查 `login.html``register.html``forgot-password.html` 必须加载 `public/layui/css/layui.css``public/layui/layui.js`,且 `layui.js``auth-pages.js` 前面。
- [x] **Step 2: 添加提示脚本断言**
检查 `public/js/auth-pages.js``public/js/captcha-pages.js` 不再包含 `root.alert`,并检查 `public/css/public.css` 提供 `.auth-layer-message`
- [x] **Step 3: 验证红灯**
运行:
```powershell
node tests\auth-page-structure.test.js
node tests\auth-message.test.js
```
预期:两个测试均失败,分别提示缺少 layui 和仍存在 `root.alert`
### Task 2: 接入 layui 并替换原生提示
**Files:**
- Modify: `login.html`
- Modify: `register.html`
- Modify: `forgot-password.html`
- Modify: `public/js/auth-pages.js`
- Modify: `public/js/captcha-pages.js`
**Interfaces:**
- Consumes: `layui.layer.msg(message, options)`
- Produces: `showMessage(message)` 统一使用 layer,缺少 layui 时只输出控制台警告,不再弹原生 alert。
- [x] **Step 1: 三页加载 layui 样式和脚本**
在三页 `<head>` 中加入 `public/layui/css/layui.css`,在 `auth-pages.js` 前加入 `public/layui/layui.js`
- [x] **Step 2: 修改认证脚本提示**
`auth-pages.js``showMessage` 改为优先调用 `layui.layer.msg(message, { skin: 'auth-layer-message' })`,没有 layui 时使用 `console.warn`
- [x] **Step 3: 修改验证码脚本提示**
`captcha-pages.js``showMessage` 同步改为同样的 layer 提示策略。
### Task 3: 补齐公共样式
**Files:**
- Modify: `public/css/public.css`
**Interfaces:**
- Consumes: layui layer 生成的 `.auth-layer-message`
- Produces: 认证页统一提示皮肤和移动端更稳的 TAC 弹窗尺寸。
- [x] **Step 1: 添加 layui 消息皮肤**
在公共 CSS 中添加 `.auth-layer-message`,控制圆角、背景、文字和阴影。
- [x] **Step 2: 微调 TAC 弹窗移动端尺寸**
继续使用 `.auth-captcha-modal` 页面级挂载,限制 TAC 宽度不超过视口,避免盖层内容顶到边缘。
### Task 4: 验证并记录完成
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
**Interfaces:**
- Consumes: Task 1-3 的实现结果。
- Produces: PC-012 完成记录。
- [x] **Step 1: 跑焦点测试**
运行:
```powershell
node tests\auth-page-structure.test.js
node tests\auth-message.test.js
```
预期:全部通过。
- [x] **Step 2: 跑相关测试和语法检查**
运行:
```powershell
node tests\auth-pages.test.js
node tests\captcha-pages.test.js
node --check public\js\auth-pages.js
node --check public\js\captcha-pages.js
```
预期:全部通过。
- [x] **Step 3: 更新追踪记录**
把 PC-012 标记为已完成,写入完成时间、修改文件、验证命令和剩余风险。
@@ -1,761 +0,0 @@
# PC API Replan Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 按最新 `genealogy-pc-openapi.yaml` 重新整理 PC/H5 页面接口对接,只接入 `paths` 中真实存在的验证码、PC 认证、PC 文件和行政区划接口,并清除旧 APP 路径作为 PC 合同的风险。
**Architecture:** 根目录 `config.js` 是接口基础地址、客户端 ID、租户、token key 的唯一配置入口;`utils` 只放跨页面公共 JS`public/js/api-client.js` 只暴露 PC YAML `paths` 中存在的接口和受控的“PC 接口未提供”错误。页面业务 JS 继续放在 `public/js`,页面只能调用 PC 已确认方法;PC YAML 未覆盖的页面保留静态/本地展示或显示接口待确认状态。
**Tech Stack:** 静态 HTML、原生 JS、jQuery/Layui、Node 测试脚本、`public/tac` 天爱验证码静态资源。
## Global Constraints
- 接口依据只使用 `genealogy-pc-openapi.yaml``paths`
- 开发环境接口基础地址固定为 `https://test-genealogy-api.ddxcjp.cn`
- PC 默认 `clientid` 使用用户最新截图确认值:`ced7e5f0498645c6ec642dcf450b036f`,客户端 Key 为 `web_pc`
- 登录后请求使用 `Authorization` header,PC 认证和文件请求同时携带 `clientid` header。
- 样式写入 CSS 文件,不用 JS 注入样式。
- 新增或修改的 HTML、JS、CSS 注释使用中文。
- `utils` 只放跨页面公共 JS;页面业务 JS 继续放 `public/js`
- PC YAML 没有明确接口的页面不套用 APP 路径,不把 schema/requestBodies 当成可调用接口。
- 验证码只使用后台已配置的 PC/H5 场景;当前明确场景为 `WEB_H5_LOGIN``WEB_H5_REGISTER``WEB_H5_FORGOT_PASSWORD`
- 换绑手机、注销账号的 PC/H5 验证码场景未确认,不套用 `APP_*` 场景。
---
## File Structure
- Create or Modify: `config.js`
- 唯一负责环境切换、开发接口地址、PC clientid、tenantId、token key、token header name。
- Create or Modify: `utils/StorageUtil.js`
- 负责 localStorage 安全读写。
- Create or Modify: `utils/FormUtil.js`
- 负责表单读取、空值清理、数字转换、布尔转换。
- Create or Modify: `utils/RequestUtil.js`
- 负责 URL 拼接、header 合并、FormData 判断、JSON 解包、业务错误对象。
- Modify: `public/js/api-client.js`
- 只把 PC YAML `paths` 中存在的接口作为真实请求方法;旧 APP 路径不得作为 fallback。
- Modify: `public/js/auth-pages.js`
- 登录使用 `WEB_H5_LOGIN`;注册使用 `WEB_H5_REGISTER`;找回密码使用 `WEB_H5_FORGOT_PASSWORD`
- Modify: `public/js/captcha-pages.js`
-`/captcha/require``/captcha/challenge``/captcha/verify` 组织 TAC 调用。
- Modify: `public/js/profile-pages.js`
- 用户资料提交字段以 `ProfileUpdateBody` schema 为准:`provinceCode/cityCode/districtCode`
- Modify: `public/js/security-pages.js`
- 修改密码、换绑手机、注销账号只调用 PC 认证接口;涉及验证码的动作保留 PC/H5 场景待确认记录。
- Modify: `public/js/upload-pages.js`
- 上传、分片上传、文件引用只调用 `/genealogy/pc/files/*`
- Modify: `public/js/region-pages.js`
- 地区选择只调用 `/genealogy/region/*`
- Modify: related HTML files
- API 页面按顺序加载 `config.js``utils/*.js``public/js/api-client.js`、页面业务脚本。
- Modify: `tests/*.test.js`
- 测试只验证 PC YAML 已确认接口;旧 APP 路径测试改为“不可作为 PC 合同”。
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- 每项开始/完成都回填时间、文件、接口、验证结果。
---
### Task 1: 配置入口与公共工具合同
**Files:**
- Create or Modify: `config.js`
- Create or Modify: `utils/StorageUtil.js`
- Create or Modify: `utils/FormUtil.js`
- Create or Modify: `utils/RequestUtil.js`
- Test: `tests/config.test.js`
- Test: `tests/utils.test.js`
**Interfaces:**
- Produces: `GenealogyConfig.getConfig(): { env, apiBaseUrl, clientId, tenantId, tokenKey, tokenHeaderName }`
- Produces: `GenealogyConfig.getApiBaseUrl(): string`
- Produces: `GenealogyConfig.getClientId(): string`
- Produces: `GenealogyConfig.getTenantId(): string`
- Produces: `GenealogyConfig.getTokenKey(): string`
- Produces: `StorageUtil.read(storage, key): string|null`
- Produces: `StorageUtil.write(storage, key, value): void`
- Produces: `StorageUtil.remove(storage, key): void`
- Produces: `FormUtil.trimOrUndefined(value): string|undefined`
- Produces: `FormUtil.toNumberOrUndefined(value): number|undefined`
- Produces: `FormUtil.toBoolean(value): boolean`
- Produces: `FormUtil.getFormValues(form): object`
- Produces: `RequestUtil.createRequester(options)(method, path, requestOptions): Promise<any>`
- [ ] **Step 1: Write config test**
Create or replace `tests/config.test.js` with:
```js
const assert = require('assert');
const configFactory = require('../config.js');
const store = {};
const config = configFactory({
localStorage: {
getItem: (key) => Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null,
setItem: (key, value) => { store[key] = String(value); }
}
});
assert.strictEqual(config.getApiBaseUrl(), 'https://test-genealogy-api.ddxcjp.cn');
assert.strictEqual(config.getClientId(), 'ced7e5f0498645c6ec642dcf450b036f');
assert.strictEqual(config.getTenantId(), '000000');
assert.strictEqual(config.getTokenKey(), 'genealogy_auth_token');
assert.strictEqual(config.getConfig().tokenHeaderName, 'Authorization');
store.genealogy_api_base_url = 'https://custom.example.test';
assert.strictEqual(config.getApiBaseUrl(), 'https://custom.example.test');
console.log('config tests passed');
```
- [ ] **Step 2: Run config test**
Run: `node tests\config.test.js`
Expected before implementation: FAIL if `config.js` is absent or lacks these methods.
Expected after implementation: PASS with `config tests passed`.
- [ ] **Step 3: Implement `config.js`**
Use this implementation:
```js
(function (root, factory) {
// 全局配置同时支持浏览器页面和 Node 测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory;
return;
}
root.GenealogyConfig = factory(root);
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var DEFAULT_ENV = 'development';
var API_BASE_URLS = {
development: 'https://test-genealogy-api.ddxcjp.cn',
production: ''
};
var CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f';
var TENANT_ID = '000000';
var TOKEN_KEY = 'genealogy_auth_token';
var TOKEN_HEADER_NAME = 'Authorization';
var ENV_KEY = 'genealogy_env';
var BASE_URL_KEY = 'genealogy_api_base_url';
function readStorage(key) {
try {
return root.localStorage && root.localStorage.getItem(key);
} catch (error) {
return null;
}
}
function getEnv() {
return readStorage(ENV_KEY) || DEFAULT_ENV;
}
function getApiBaseUrl() {
return readStorage(BASE_URL_KEY) || API_BASE_URLS[getEnv()] || API_BASE_URLS.development;
}
function getConfig() {
return {
env: getEnv(),
apiBaseUrl: getApiBaseUrl(),
clientId: CLIENT_ID,
tenantId: TENANT_ID,
tokenKey: TOKEN_KEY,
tokenHeaderName: TOKEN_HEADER_NAME
};
}
return {
getConfig: getConfig,
getApiBaseUrl: getApiBaseUrl,
getClientId: function () { return CLIENT_ID; },
getTenantId: function () { return TENANT_ID; },
getTokenKey: function () { return TOKEN_KEY; },
getTokenHeaderName: function () { return TOKEN_HEADER_NAME; }
};
});
```
- [ ] **Step 4: Write utilities test**
Create or replace `tests/utils.test.js` with focused utility assertions:
```js
const assert = require('assert');
const StorageUtil = require('../utils/StorageUtil.js');
const FormUtil = require('../utils/FormUtil.js');
const RequestUtil = require('../utils/RequestUtil.js');
assert.strictEqual(FormUtil.trimOrUndefined(' 张三 '), '张三');
assert.strictEqual(FormUtil.trimOrUndefined(' '), undefined);
assert.strictEqual(FormUtil.toNumberOrUndefined('12'), 12);
assert.strictEqual(FormUtil.toNumberOrUndefined(''), undefined);
assert.strictEqual(FormUtil.toBoolean('on'), true);
assert.strictEqual(FormUtil.toBoolean('0'), false);
const store = {};
const storage = {
getItem: (key) => Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null,
setItem: (key, value) => { store[key] = String(value); },
removeItem: (key) => { delete store[key]; }
};
StorageUtil.write(storage, 'token', 'abc');
assert.strictEqual(StorageUtil.read(storage, 'token'), 'abc');
StorageUtil.remove(storage, 'token');
assert.strictEqual(StorageUtil.read(storage, 'token'), null);
const calls = [];
const requester = RequestUtil.createRequester({
baseUrl: 'https://test-genealogy-api.ddxcjp.cn',
clientId: 'pc-client',
tokenHeaderName: 'Authorization',
getToken: () => 'token-x',
fetchImpl: async (url, options) => {
calls.push({ url, options });
return {
ok: true,
status: 200,
json: async () => ({ code: 200, data: { ok: true } })
};
}
});
requester('GET', '/genealogy/pc/auth/profile', {
query: { keyword: '汤 氏' }
}).then((data) => {
assert.deepStrictEqual(data, { ok: true });
assert.strictEqual(calls[0].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile?keyword=%E6%B1%A4+%E6%B0%8F');
assert.strictEqual(calls[0].options.headers.clientid, 'pc-client');
assert.strictEqual(calls[0].options.headers.Authorization, 'Bearer token-x');
console.log('utils tests passed');
});
```
- [ ] **Step 5: Implement utility files**
Implement the utilities with Chinese comments around storage try/catch, FormData detection, JSON response parsing and `code !== 200` error creation. `RequestUtil.createRequester` must omit `Authorization` when `requestOptions.auth === false`.
- [ ] **Step 6: Run utility tests**
Run: `node tests\utils.test.js`
Expected: PASS with `utils tests passed`.
---
### Task 2: API 客户端收紧到 PC YAML `paths`
**Files:**
- Modify: `public/js/api-client.js`
- Test: `tests/api-client.test.js`
**Interfaces:**
- Consumes: `GenealogyConfig`
- Consumes: `StorageUtil`
- Consumes: `RequestUtil.createRequester`
- Produces: `GenealogyApi.createClient(options)`
- Produces: `GenealogyApi.defaultClient`
- Produces PC methods:
- `login(body)`, `register(body)`, `loginBySms(body)`, `sendSmsCode(body)`
- `getProfile()`, `updateProfile(body)`, `changePassword(body)`, `resetPassword(body)`, `changePhone(body)`, `deactivateAccount(body)`, `logout()`
- `uploadFile(formData)`, `initResumableUpload(body)`, `uploadChunk(formData)`, `completeResumableUpload(body)`, `bindFileReference(body)`, `releaseFileReference(query)`
- `getRegionChildren(parentCode)`, `getRegionPath(regionCode)`, `searchRegions(query)`, `getRegion(regionCode)`
- `captchaRequire(query)`, `captchaChallenge(body)`, `captchaVerify(body)`, `legacyCaptcha()`
- Produces unsupported method behavior: methods for PC YAML 未覆盖业务 throw `Error` with `code === 'PC_API_NOT_AVAILABLE'` and no network request.
- [ ] **Step 1: Replace API client test**
Rewrite `tests/api-client.test.js` so it verifies only PC-confirmed paths and unsupported APP-era methods:
```js
const assert = require('assert');
const GenealogyApi = require('../public/js/api-client.js');
const calls = [];
const client = GenealogyApi.createClient({
baseUrl: 'https://test-genealogy-api.ddxcjp.cn',
clientId: 'client-x',
tenantId: 'tenant-x',
tokenKey: 'token-key',
tokenHeaderName: 'Authorization',
storage: { getItem: () => 'abc-token' },
fetchImpl: async (url, options) => {
calls.push({ url, options });
return {
ok: true,
status: 200,
json: async () => ({ code: 200, data: { url } })
};
}
});
(async () => {
await client.login({ tenantId: 'tenant-x', phone: '13800000000', password: 'md5' });
assert.strictEqual(calls[0].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/login');
assert.strictEqual(calls[0].options.headers.clientid, 'client-x');
assert.strictEqual(calls[0].options.headers.Authorization, undefined);
await client.getProfile();
assert.strictEqual(calls[1].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile');
assert.strictEqual(calls[1].options.headers.Authorization, 'Bearer abc-token');
await client.updateProfile({ nickName: '张三', provinceCode: '510000', cityCode: '510100', districtCode: '510104' });
assert.strictEqual(calls[2].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile');
assert.strictEqual(calls[2].options.method, 'PUT');
await client.uploadFile(new FormData());
assert.strictEqual(calls[3].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/files/upload');
await client.getRegionChildren('51');
assert.strictEqual(calls[4].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/region/children?parentCode=51');
await client.captchaRequire({ sceneCode: 'WEB_H5_LOGIN', subject: '13800000000' });
assert.strictEqual(calls[5].url, 'https://test-genealogy-api.ddxcjp.cn/captcha/require?tenantId=tenant-x&clientId=client-x&sceneCode=WEB_H5_LOGIN&subject=13800000000');
assert.strictEqual(calls[5].options.headers.Authorization, undefined);
assert.throws(
() => client.listGenealogies(),
(error) => error.code === 'PC_API_NOT_AVAILABLE'
);
assert.strictEqual(calls.length, 6);
console.log('api-client tests passed');
})();
```
- [ ] **Step 2: Run API client test**
Run: `node tests\api-client.test.js`
Expected before implementation: FAIL while old APP paths or old scene tests remain.
Expected after implementation: PASS with `api-client tests passed`.
- [ ] **Step 3: Implement confirmed PC methods**
In `public/js/api-client.js`, remove APP auth/files paths. Map methods exactly:
```js
login: function (body) {
return request('POST', '/genealogy/pc/auth/login', { body: body, auth: false });
},
register: function (body) {
return request('POST', '/genealogy/pc/auth/register', { body: body, auth: false });
},
sendSmsCode: function (body) {
return request('POST', '/genealogy/pc/auth/sms/code', { body: body, auth: false });
},
loginBySms: function (body) {
return request('POST', '/genealogy/pc/auth/login/sms', { body: body, auth: false });
},
getProfile: function () {
return request('GET', '/genealogy/pc/auth/profile');
},
updateProfile: function (body) {
return request('PUT', '/genealogy/pc/auth/profile', { body: body });
}
```
Add the remaining PC auth/files/region/captcha methods with the same direct mapping from YAML `paths`.
- [ ] **Step 4: Implement unsupported method guard**
For old business methods whose PC paths do not exist, keep the method name only if current pages call it, but route it to:
```js
function createUnavailableMethod(name) {
return function () {
var error = new Error('PC 接口文档未提供该业务接口:' + name);
error.code = 'PC_API_NOT_AVAILABLE';
throw error;
};
}
```
Use a Chinese comment above the mapping: `// PC YAML 未提供这些业务 paths,保留方法名只用于阻止旧 APP 路径继续发请求。`
- [ ] **Step 5: Verify the PC auth/files contract**
Run: `node tests\pc-api-contract.test.js`
Expected: PC 请求契约扫描通过。
- [ ] **Step 6: Run API client test again**
Run: `node tests\api-client.test.js`
Expected: PASS with `api-client tests passed`.
---
### Task 3: 登录验证码与 `public/tac` 调用
**Files:**
- Modify: `public/js/auth-pages.js`
- Modify: `public/js/captcha-pages.js`
- Modify: `login.html`
- Modify: `register.html`
- Modify: `forgot-password.html`
- Test: `tests/auth-pages.test.js`
- Test: `tests/captcha-pages.test.js`
**Interfaces:**
- Consumes: `public/tac/css/tac.css`
- Consumes: `public/tac/js/tac.min.js`
- Consumes: `GenealogyApi.defaultClient.captchaRequire/challenge/verify`
- Produces: `AuthPages.getCaptchaScene('login') === 'WEB_H5_LOGIN'`
- Produces: `AuthPages.getCaptchaScene('register') === 'WEB_H5_REGISTER'`
- Produces: `AuthPages.getCaptchaScene('password-reset') === 'WEB_H5_FORGOT_PASSWORD'`
- Produces: `CaptchaPages.buildChallengeBody(options, api)`
- Produces: `CaptchaPages.buildVerifyBody(options, api)`
- Produces: `CaptchaPages.ensureToken(form, options)`
- [ ] **Step 1: Replace auth scene tests**
In `tests/auth-pages.test.js`, require these expectations:
```js
assert.strictEqual(AuthPages.getCaptchaScene('login'), 'WEB_H5_LOGIN');
assert.strictEqual(AuthPages.getCaptchaScene('register'), 'WEB_H5_REGISTER');
assert.strictEqual(AuthPages.getCaptchaScene('password-reset'), 'WEB_H5_FORGOT_PASSWORD');
```
- [ ] **Step 2: Replace captcha tests**
In `tests/captcha-pages.test.js`, all challenge/verify body tests must use `WEB_H5_LOGIN`:
```js
assert.deepStrictEqual(CaptchaPages.buildChallengeBody({
sceneCode: 'WEB_H5_LOGIN',
subject: '13800000000'
}, fakeApi), {
tenantId: 'tenant-x',
clientId: 'client-x',
sceneCode: 'WEB_H5_LOGIN',
subject: '13800000000'
});
```
- [ ] **Step 3: Run auth and captcha tests**
Run:
```powershell
node tests\auth-pages.test.js
node tests\captcha-pages.test.js
```
Expected before implementation: FAIL if `PC_LOGIN` or `PC_REGISTER` remains.
Expected after implementation: PASS.
- [ ] **Step 4: Implement scene mapping**
In `public/js/auth-pages.js`:
```js
function getCaptchaScene(formType) {
// PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。
var map = {
login: 'WEB_H5_LOGIN',
register: 'WEB_H5_REGISTER',
'password-reset': 'WEB_H5_FORGOT_PASSWORD'
};
return map[formType] || '';
}
```
If `sceneCode` is empty, `ensureCaptcha` must return `true` without calling `/captcha/require` and must not fabricate `validToken`.
- [ ] **Step 5: Verify TAC assets in auth pages**
Run:
```powershell
Select-String -Path login.html,register.html,forgot-password.html -Pattern 'public/tac/css/tac.css|public/tac/js/tac.min.js|public/js/captcha-pages.js|name="validToken"|data-captcha-box'
```
Expected:
- `login.html` has TAC CSS, TAC JS, `captcha-pages.js`, hidden `validToken`, and `[data-captcha-box]`.
- `register.html` and `forgot-password.html` may keep TAC assets and hidden fields, but no PC/H5 scene is configured until backend confirms scene code.
---
### Task 4: 认证与资料页面只接 PC auth schema
**Files:**
- Modify: `public/js/auth-pages.js`
- Modify: `public/js/profile-pages.js`
- Modify: `public/js/security-pages.js`
- Modify: `login.html`
- Modify: `register.html`
- Modify: `forgot-password.html`
- Modify: `profile.html`
- Modify: `profile-data.html`
- Modify: `profile-security.html`
- Test: `tests/auth-pages.test.js`
- Test: `tests/profile-pages.test.js`
- Test: `tests/security-pages.test.js`
**Interfaces:**
- Consumes: `GenealogyApi.defaultClient`
- Produces request bodies:
- `PasswordLoginBody`: `grantType/tenantId/phone/password/validToken`
- `PasswordRegisterBody`: `grantType/tenantId/phone/password/nickName/registerSource/validToken`
- `PasswordResetBody`: `tenantId/phone/smsCode/newPassword/validToken`
- `ProfileUpdateBody`: `nickName/avatarOssId/sex/birthday/provinceCode/cityCode/districtCode`
- [ ] **Step 1: Update body-builder tests**
`tests/auth-pages.test.js` must assert `registerSource: 'PC'` for register body and no `APP_*` scene in any body.
`tests/profile-pages.test.js` must assert profile update uses `provinceCode/cityCode/districtCode`, not `regionCode/addressDetail`.
- [ ] **Step 2: Run focused tests**
Run:
```powershell
node tests\auth-pages.test.js
node tests\profile-pages.test.js
node tests\security-pages.test.js
```
Expected before implementation: FAIL where old scene/body fields remain.
Expected after implementation: PASS.
- [ ] **Step 3: Implement auth body builders**
Use PC YAML schema fields only. For register:
```js
function buildRegisterBody(values, config) {
var body = {
grantType: 'password',
tenantId: config.tenantId,
phone: values.phone,
password: values.password,
registerSource: 'PC'
};
if (values.nickName) body.nickName = values.nickName;
if (values.validToken) body.validToken = values.validToken;
return body;
}
```
- [ ] **Step 4: Implement profile body builder**
Use schema fields:
```js
function buildProfileUpdateBody(values) {
return removeEmpty({
nickName: values.nickName,
avatarOssId: toNumberOrUndefined(values.avatarOssId),
sex: values.sex,
birthday: values.birthday,
provinceCode: values.provinceCode,
cityCode: values.cityCode,
districtCode: values.districtCode
});
}
```
- [ ] **Step 5: Verify page script order**
Run:
```powershell
Select-String -Path login.html,register.html,forgot-password.html,profile.html,profile-data.html,profile-security.html -Pattern 'config.js|utils/StorageUtil.js|utils/FormUtil.js|utils/RequestUtil.js|public/js/api-client.js'
```
Expected: every page that uses `GenealogyApi` loads config and utils before `api-client.js`.
---
### Task 5: 文件上传和文件引用只切 PC files
**Files:**
- Modify: `public/js/upload-pages.js`
- Modify: upload-related page scripts that call `GenealogyApi.defaultClient.uploadFile`
- Test: `tests/upload-pages.test.js`
- Test: `tests/api-client.test.js`
**Interfaces:**
- Consumes: `GenealogyApi.defaultClient.uploadFile(formData)`
- Consumes: `GenealogyApi.defaultClient.initResumableUpload(body)`
- Consumes: `GenealogyApi.defaultClient.uploadChunk(formData)`
- Consumes: `GenealogyApi.defaultClient.completeResumableUpload(body)`
- Consumes: `GenealogyApi.defaultClient.bindFileReference(body)`
- Consumes: `GenealogyApi.defaultClient.releaseFileReference(query)`
- [ ] **Step 1: Add upload tests**
`tests/upload-pages.test.js` must verify:
- single upload calls `/genealogy/pc/files/upload`
- resumable init calls `/genealogy/pc/files/resumable/init`
- file reference body requires `bizType/bizTable/bizId/bizField`
- [ ] **Step 2: Run upload tests**
Run: `node tests\upload-pages.test.js`
Expected before implementation: FAIL if old APP file paths remain.
Expected after implementation: PASS.
- [ ] **Step 3: Implement upload mapping**
Only map file capability. Do not submit article/feed/album/ceremony/growth/memo business forms unless PC YAML provides their business endpoints.
- [ ] **Step 4: Verify the PC file contract**
Run: `node tests\pc-api-contract.test.js`
Expected: PC 请求契约扫描通过。
---
### Task 6: 行政区划接口和资料地区字段
**Files:**
- Modify: `public/js/region-pages.js`
- Modify: `public/js/profile-pages.js`
- Test: `tests/region-pages.test.js`
- Test: `tests/profile-pages.test.js`
**Interfaces:**
- Consumes: `GET /genealogy/region/children`
- Consumes: `GET /genealogy/region/path/{regionCode}`
- Consumes: `GET /genealogy/region/search`
- Consumes: `GET /genealogy/region/{regionCode}`
- Produces profile fields: `provinceCode/cityCode/districtCode`
- [ ] **Step 1: Add region tests**
`tests/region-pages.test.js` must assert:
- empty parent loads province list with no required parent code.
- search requires `keyword`.
- selected province/city/district writes `provinceCode/cityCode/districtCode` to the form.
- [ ] **Step 2: Run region tests**
Run: `node tests\region-pages.test.js`
Expected before implementation: FAIL if form still expects `regionCode/addressDetail`.
Expected after implementation: PASS.
- [ ] **Step 3: Implement region mapping**
Use `GenealogyApi.defaultClient.getRegionChildren`, `getRegionPath`, `searchRegions`, `getRegion`. Do not create genealogy submit behavior here.
---
### Task 7: PC YAML 未覆盖页面降级为接口待确认
**Files:**
- Modify: page scripts that currently call APP-era business methods:
- `public/js/genealogy-pages.js`
- `public/js/join-apply-pages.js`
- `public/js/member-admin-pages.js`
- `public/js/article-pages.js`
- `public/js/feed-pages.js`
- `public/js/album-pages.js`
- `public/js/ceremony-pages.js`
- `public/js/growth-pages.js`
- `public/js/memo-pages.js`
- `public/js/generation-pages.js`
- `public/js/lineage-pages.js`
- `public/js/notification-pages.js`
- `public/js/help-pages.js`
- `public/js/promotion-pages.js`
- `public/js/feedback-pages.js`
- `public/js/vip-pages.js`
- Test: `tests/unsupported-pages.test.js`
**Interfaces:**
- Consumes: `PC_API_NOT_AVAILABLE` errors from `GenealogyApi`
- Produces: visible or console-safe page state that says PC interface is not configured, without issuing APP requests.
- [ ] **Step 1: Add unsupported-page smoke test**
Create `tests/unsupported-pages.test.js` to load each page script in Node with a fake client method that throws `{ code: 'PC_API_NOT_AVAILABLE' }`. Assert each script handles the error without rethrowing from its init function.
- [ ] **Step 2: Run unsupported-page test**
Run: `node tests\unsupported-pages.test.js`
Expected before implementation: FAIL for scripts that assume APP methods are valid.
Expected after implementation: PASS.
- [ ] **Step 3: Implement shared page fallback**
In each affected page script, catch `PC_API_NOT_AVAILABLE` and render a small existing-style empty state. Add a Chinese comment: `// PC YAML 未提供该业务接口,页面先展示接口待确认状态。`
- [ ] **Step 4: Verify no obsolete business path is emitted from the API client**
Run: `node tests\pc-api-contract.test.js`
Expected: PC 请求契约扫描通过。
---
### Task 8: 追踪表和执行记录
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
**Interfaces:**
- Consumes: Task 1-7 verification results.
- Produces: updated status rows with exact timestamps, changed files, interface paths and verification commands.
- [ ] **Step 1: Mark task start before edits**
Before executing each PC task, update the row status from `待开始` to `进行中` with exact time from:
```powershell
Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz"
```
- [ ] **Step 2: Mark task completion after verification**
After the task's verification commands pass, update status to `已完成` and record:
- completion time
- modified files
- interface paths
- verification commands
- verification result
- [ ] **Step 3: Keep missing interfaces in coverage matrix**
Any page whose main business path is absent from `genealogy-pc-openapi.yaml` remains in `PC YAML 未覆盖` or `接口待确认` and must not be promoted to full integration.
---
## Verification Gate
Run these before marking the replan execution complete:
```powershell
node tests\config.test.js
node tests\utils.test.js
node tests\api-client.test.js
node tests\auth-pages.test.js
node tests\captcha-pages.test.js
node tests\profile-pages.test.js
node tests\security-pages.test.js
node tests\upload-pages.test.js
node tests\region-pages.test.js
node tests\unsupported-pages.test.js
Get-ChildItem -Path public\js -Filter *.js | Where-Object { $_.Name -notin @('jquery360.js','jquery.min.js') } | Sort-Object Name | ForEach-Object { node --check $_.FullName }
node tests\pc-api-contract.test.js
git diff --check
```
Expected:
- All listed Node tests pass.
- Business JS syntax check passes.
- PC 请求契约扫描通过。
- 认证和验证码代码/tests 不含过期场景默认值。
- `git diff --check` passes, or if this directory is not a Git repository, record the exact output.
## Self-Review
- Spec coverage: this plan covers latest PC YAML `paths`, test domain, config.js, utils/public-js separation, CSS/no style injection, Chinese comments, TAC login scene, profile schema mismatch, and PC-missing business interfaces.
- Placeholder scan: no implementation task asks the worker to invent missing PC endpoints or use APP paths as compatibility.
- Type consistency: `GenealogyConfig`, `StorageUtil`, `FormUtil`, `RequestUtil`, `GenealogyApi.createClient`, `AuthPages.getCaptchaScene`, and `CaptchaPages.ensureToken` names are consistent across tasks.
@@ -1,127 +0,0 @@
# PC Auth Three Pages Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:**`genealogy-pc-openapi.yaml` 和 Apifox 截图,把登录、注册、忘记密码三页与 PC 认证接口、验证中心接口对齐。
**Architecture:** 根目录 `config.js` 继续作为环境、`clientid``tenantId` 的唯一配置入口;`utils` 继续存放跨页面公共工具;页面业务逻辑集中在 `public/js/auth-pages.js`,接口调用集中在 `public/js/api-client.js`,验证码适配集中在 `public/js/captcha-pages.js`。页面差异样式只写入对应 CSS 文件。
**Tech Stack:** 原生 HTML/CSS/JavaScript、`public/tac` 滑动验证、Node 断言测试。
## Global Constraints
- 接口依据只使用 `genealogy-pc-openapi.yaml` 与用户补充的 Apifox 截图。
- 开发环境接口地址为 `https://test-genealogy-api.ddxcjp.cn`
- PC `clientid``ced7e5f0498645c6ec642dcf450b036f`,租户 ID 为 `000000`
- 登录验证码场景为 `WEB_H5_LOGIN`,注册验证码场景为 `WEB_H5_REGISTER`,忘记密码验证码场景为 `WEB_H5_FORGOT_PASSWORD`
- 不在 JS 中注入样式;页面差异样式写入对应 CSS 文件,公共样式写入 `public/css/public.css`
- 新增或修改的 HTML、CSS、JS 注释使用中文,代码保持可读,不压缩。
---
### Task 1: 记录专项计划
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Create: `docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md`
**Interfaces:**
- Consumes: 用户确认“可以”先做登录、注册、忘记密码三页。
- Produces: PC-009 专项计划记录。
- [x] **Step 1: 写入本计划**
创建本计划,明确页面、接口、验证码场景、样式和验证约束。
- [x] **Step 2: 写入追踪记录**
在追踪文档中新增 `PC-009`,状态为“进行中”,开始时间为 `2026-07-09 16:32:17 +08:00`
### Task 2: 登录页补齐短信登录
**Files:**
- Modify: `login.html`
- Modify: `public/css/login.css`
- Modify: `public/js/auth-pages.js`
- Modify: `tests/auth-pages.test.js`
- Modify: `tests/api-client.test.js`
**Interfaces:**
- Consumes: `ApiClient.login(body)``ApiClient.loginBySms(body)``ApiClient.sendSmsCode(body)``CaptchaPages.ensureToken(form, options)`
- Produces: 登录页同时支持密码登录和短信登录。
- [x] **Step 1: 页面增加登录方式切换**
`login.html` 的登录表单内增加模式切换按钮,密码登录区域保留 `phone/password/validToken`,短信登录区域增加 `phone/smsCode/validToken` 和发送验证码按钮。
- [x] **Step 2: CSS 写入登录页专属样式**
`public/css/login.css` 增加模式切换、隐藏区域、验证码行样式,不在 JS 中写样式。
- [x] **Step 3: JS 支持短信登录提交**
`public/js/auth-pages.js` 增加 `buildSmsLoginBody(values)`,提交时按当前模式调用 `/genealogy/pc/auth/login``/genealogy/pc/auth/login/sms`
- [x] **Step 4: 发送短信复用场景码**
登录页短信验证码按钮使用 `data-scene-code="WEB_H5_LOGIN"`,忘记密码页继续使用 `WEB_H5_FORGOT_PASSWORD`
- [x] **Step 5: 更新测试**
`tests/auth-pages.test.js` 增加短信登录 body 断言;`tests/api-client.test.js` 增加 `loginBySms``sendSmsCode` 路径/入参断言。
### Task 3: 注册和忘记密码复核
**Files:**
- Verify: `register.html`
- Verify: `forgot-password.html`
- Verify: `public/js/auth-pages.js`
**Interfaces:**
- Consumes: `/genealogy/pc/auth/register``/genealogy/pc/auth/sms/code``/genealogy/pc/auth/password/reset`
- Produces: 注册和忘记密码页面确认继续按 PC 字段提交。
- [x] **Step 1: 注册页字段复核**
确认注册页只提交 `phone/password/nickName/validToken``grantType/tenantId/clientId/registerSource``api-client.js` 统一补齐。
- [x] **Step 2: 忘记密码页字段复核**
确认忘记密码页提交 `tenantId/phone/smsCode/newPassword/validToken`,不再出现邮箱找回文案。
### Task 4: 专项验证和完成记录
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
**Interfaces:**
- Consumes: Task 2 和 Task 3 的实现结果。
- Produces: PC-009 完成记录和验证结果。
- [x] **Step 1: 运行测试**
运行:
```powershell
node tests\auth-pages.test.js
node tests\api-client.test.js
node tests\captcha-pages.test.js
```
期望全部输出 `passed`
- [x] **Step 2: 运行语法检查**
运行:
```powershell
node --check public\js\auth-pages.js
node --check public\js\api-client.js
node --check public\js\captcha-pages.js
```
期望无输出且退出码为 0。
- [x] **Step 3: 更新追踪文档**
`PC-009` 状态改为“已完成”,写入完成时间、修改文件、接口路径、验证码场景和验证结果。
@@ -1,95 +0,0 @@
# TAC Captcha Auth Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:**`public/tac` 滑动验证接入登录、注册、忘记密码三个认证页面,并按 `genealogy-pc-openapi.yaml` 的验证中心接口换取 `validToken` 后再提交业务接口。
**Architecture:** 新增 `public/js/captcha-pages.js` 作为 TAC 与验证中心接口之间的适配层;`auth-pages.js` 在提交登录、注册、发送短信、重置密码前调用验证码适配层。API 基础地址、clientId、tenantId 继续由 `api-client.js` 统一提供。
**Tech Stack:** 静态 HTML、原生 JS、现有 `public/tac/js/tac.min.js`、现有 `public/tac/css/tac.css`、Node 单元测试。
## Global Constraints
- 严格依据 `genealogy-pc-openapi.yaml``/captcha/require``/captcha/challenge``/captcha/verify` 字段接入。
- 业务 JS 不压缩,新增注释使用中文。
- 自有样式写入 CSS 文件,不通过自有 JS 注入样式。
- 第三方 `tac.min.js` 保持原样,不重写其内部样式和 DOM 生成逻辑。
- 登录、注册、忘记密码三页都必须有隐藏 `validToken` 字段和验证码挂载容器。
---
### Task 1: API Client Captcha Helpers
**Files:**
- Modify: `public/js/api-client.js`
- Modify: `tests/api-client.test.js`
**Interfaces:**
- Produces: `client.buildApiUrl(path, query)`
- Produces: `client.captchaRequirement({ sceneCode, subject })`
- [ ] **Step 1: Write failing tests** for captcha requirement URL, query, and public URL building.
- [ ] **Step 2: Run tests** and confirm missing methods fail.
- [ ] **Step 3: Implement minimal API client helpers**.
- [ ] **Step 4: Run tests** and confirm pass.
### Task 2: TAC Adapter Module
**Files:**
- Create: `public/js/captcha-pages.js`
- Create: `tests/captcha-pages.test.js`
**Interfaces:**
- Produces: `CaptchaPages.buildChallengeBody(options, api)`
- Produces: `CaptchaPages.adaptChallengeResponse(response)`
- Produces: `CaptchaPages.buildVerifyBody(requestData, context, api)`
- Produces: `CaptchaPages.extractValidToken(response)`
- Produces: `CaptchaPages.ensureToken(form, options)`
- [ ] **Step 1: Write failing tests** for OpenAPI-to-TAC mapping and validToken extraction.
- [ ] **Step 2: Run tests** and confirm module missing fails.
- [ ] **Step 3: Implement adapter with Chinese comments**.
- [ ] **Step 4: Run tests** and confirm pass.
### Task 3: Auth Flow Integration
**Files:**
- Modify: `public/js/auth-pages.js`
- Modify: `tests/auth-pages.test.js`
**Interfaces:**
- Consumes: `CaptchaPages.ensureToken(form, { sceneCode, subject })`
- Updates: login/register/password reset/send code flows wait for captcha before API submission.
- [ ] **Step 1: Write failing tests** for login body validToken and scene code mapping.
- [ ] **Step 2: Run tests** and confirm expected failure.
- [ ] **Step 3: Integrate captcha gate before submit/send code**.
- [ ] **Step 4: Run tests** and confirm pass.
### Task 4: HTML/CSS Wiring
**Files:**
- Modify: `login.html`
- Modify: `register.html`
- Modify: `forgot-password.html`
- Modify: `public/css/public.css`
**Interfaces:**
- Adds: `<link rel="stylesheet" href="public/tac/css/tac.css" />`
- Adds: hidden `validToken`
- Adds: `data-captcha-box`
- Adds: `public/tac/js/tac.min.js` and `public/js/captcha-pages.js`
- [ ] **Step 1: Add page hooks** with Chinese HTML comments where helpful.
- [ ] **Step 2: Add CSS-only layout for captcha container**.
- [ ] **Step 3: Run static checks** for scripts, hooks, no self JS style injection.
### Task 5: Verification And Records
**Files:**
- Modify: `docs/api-page-integration-2026-07-09.md`
- [ ] **Step 1: Run all Node tests**.
- [ ] **Step 2: Run `node --check` for all business JS**.
- [ ] **Step 3: Run static checks for HTML hooks and style-injection patterns**.
- [ ] **Step 4: Append dated progress record**.
@@ -1,26 +0,0 @@
# 2026-07-10 认证页表单校验和结构规范化计划
- 计划编号:PC-024
- 开始时间:2026-07-10 14:56:08 +08:00
- 页面范围:`login.html``register.html``forgot-password.html`
- 接口范围:登录、短信登录、注册、找回密码提交前的本地校验与验证码取票据
## 结论
- 当前三页只有很弱的前端校验,主要是必填判断和找回密码两次密码一致校验,不算完整表单验证。
- `<form>` 标签不是落后写法,它是提交类页面的语义结构;但把 `data-captcha-scene``data-success-url` 这类业务配置写在 HTML 上不够集中,后续维护容易散。
## 执行清单
1. [x] 写失败测试:认证页不能继续把业务配置写在表单 `data-*` 属性上。
2. [x] 写失败测试:认证页 JS 要提供统一表单校验函数。
3. [x] HTML 表单改为使用 `id` 作为挂载点。
4. [x] `auth-pages.js` 增加统一表单配置和校验函数。
5. [x] 保持三页提交前统一走 `CaptchaPages.ensureToken`,并继续使用 `payload.track` 换取票据。
6. [x] 运行结构测试、业务测试、验证码测试、语法检查和空白检查。
## 验证记录
- 2026-07-10 14:56:08 +08:00:开始记录 PC-024,并补充结构和校验测试。
- 2026-07-10 14:56:08 +08:00`node tests\auth-page-structure.test.js``node tests\auth-pages.test.js` 已按预期失败,失败点为 HTML 未使用表单 `id`、JS 缺少 `sms-login` 场景配置和统一校验函数。
- 2026-07-10 15:11:49 +08:00:修复后 `node tests\auth-page-structure.test.js``node tests\auth-pages.test.js``node tests\captcha-pages.test.js``node --check public\js\auth-pages.js``node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有 LF/CRLF 换行提示。
@@ -1,251 +0,0 @@
# Axios 公共请求层重构 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:** 删除现有 `fetch` 请求实现,以本地 Axios 浏览器版和 `utils/AxiosRequestUtil.js` 替换为唯一公共请求层,页面业务 JS 不再依赖或实现底层网络请求。
**Architecture:** `utils/axios.js` 是固定版本的第三方浏览器依赖;`utils/AxiosRequestUtil.js` 负责 Axios 实例、请求头、token、参数、响应解包和错误转换;`utils/ApiClient.js` 负责所有 PC 接口契约与 token 生命周期。所有页面在页面业务脚本前加载三个 utils 文件,旧 `utils/RequestUtil.js``public/js/api-client.js` 被删除且不保留兼容入口。
**Tech Stack:** Axios 1.7.9 浏览器非压缩构建、原生 JavaScript、Node `assert` 测试、PowerShell 静态扫描。
## Global Constraints
- 计划创建时间:2026-07-10 09:27:30 +08:00。
- `utils` 只存通用依赖和公共请求封装;`public/js` 只存页面业务脚本,不存 API 客户端或底层请求实现。
- 不保留 `fetch``fetchImpl``root.fetch``RequestUtil``public/js/api-client.js` 或旧脚本引用;不做 Axios/fetch 双实现兼容。
- Axios 使用本地 `utils/axios.js`,页面运行时不依赖 CDN。
- 普通 JSON 请求传对象给 Axios;文件上传传 `FormData`;不手写 JSON 字符串。
- 每个请求都带 `clientid`;有 token 且 `auth !== false` 时带 `Authorization: Bearer <token>`
- PC-014 在本重构完成后重新执行,重构前的复核结论不作为最终证据。
---
### Task 0: 收敛环境配置职责
**Files:**
- Modify: `config.js`
- Modify: `tests/config.test.js`
- Modify: `utils/ApiClient.js`
**Interfaces:**
- Produces: `GenealogyConfig.getEnvironment()`,只返回 `development``production`
- Produces: `GenealogyConfig.getApiBaseUrl()`,只返回当前环境的接口基础地址。
- Produces: `GenealogyConfig.getConfig()`,只返回 `{ environment, apiBaseUrl }`
- Consumes: `ApiClient` 自己定义 PC `clientId``tenantId`、token key 和 token header。
- [x] **Step 1: 写配置职责失败测试**
```js
assert.deepStrictEqual(config.getConfig(), {
environment: 'development',
apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn'
});
assert.strictEqual(config.getClientId, undefined);
assert.strictEqual(config.getTenantId, undefined);
assert.strictEqual(config.getTokenKey, undefined);
```
- [x] **Step 2: 运行测试确认旧配置职责过宽**
Run: `node tests\\config.test.js`
Expected: FAIL,旧配置仍暴露 clientId、tenantId、token key 或 token header。
- [x] **Step 3: 重写 config.js 为环境和域名唯一入口**
```js
var environments = {
development: { apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' },
production: { apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' }
};
function getConfig() {
return {
environment: getEnvironment(),
apiBaseUrl: getApiBaseUrl()
};
}
```
- [x] **Step 4: 将 PC 身份和 token 常量保留在 ApiClient**
```js
var DEFAULT_CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f';
var DEFAULT_TENANT_ID = '000000';
var DEFAULT_TOKEN_KEY = 'genealogy_auth_token';
var DEFAULT_TOKEN_HEADER_NAME = 'Authorization';
```
补充约束:`ApiClient` 不定义接口基础地址;必须由根目录 `config.js``apiBaseUrl` 或调用方显式 `baseUrl` 提供。
- [x] **Step 5: 运行配置与 API 客户端测试**
Run: `node tests\\config.test.js; node tests\\api-client.test.js; node --check config.js; node --check utils\\ApiClient.js`
Expected: 所有命令通过。
### Task 1: 锁定 Axios 请求契约的失败测试
**Files:**
- Modify: `tests/utils.test.js`
- Modify: `tests/api-client.test.js`
**Interfaces:**
- Produces: `AxiosRequestUtil.createRequester(options)`
- Consumes: `options.axiosInstance`,该对象实现 `request(config)`
- Produces: `request(method, path, { query, body, headers, auth })`,向 Axios 传入 `{ method, url, params, data, headers }`
- [x] **Step 1: 把工具测试改为 Axios 假实例**
```js
const axiosCalls = [];
const requester = AxiosRequestUtil.createRequester({
baseUrl: 'https://api.example.test',
clientId: 'pc-client',
getToken: () => 'token-x',
axiosInstance: {
request: async (config) => {
axiosCalls.push(config);
return { status: 200, data: { code: 200, data: { ok: true } } };
}
}
});
```
- [x] **Step 2: 运行测试确认因旧 fetch 契约失败**
Run: `node tests\\utils.test.js; node tests\\api-client.test.js`
Expected: FAIL,提示缺少 `AxiosRequestUtil` 或仍使用 `fetchImpl`
### Task 2: 创建 Axios 工具层并删除 fetch 工具层
**Files:**
- Create: `utils/axios.js`
- Create: `utils/AxiosRequestUtil.js`
- Create: `utils/ApiClient.js`
- Delete: `utils/RequestUtil.js`
- Delete: `public/js/api-client.js`
**Interfaces:**
- `AxiosRequestUtil.createRequester(options)` 只接受 Axios 实例,不接受 `fetchImpl`
- `GenealogyApi.createClient(options)``utils/ApiClient.js` 提供,只接受 `axiosInstance` 测试注入;生产环境读取 `window.axios`
- [x] **Step 1: 下载固定 Axios 非压缩浏览器构建**
Run: `curl.exe --fail --location https://cdn.jsdelivr.net/npm/axios@1.7.9/dist/axios.js --output utils\\axios.js`
Expected: `utils/axios.js` 存在且首行包含 Axios 版本信息。
- [x] **Step 2: 实现 AxiosRequestUtil 最小请求流程**
```js
var response = await axiosInstance.request({
method: method.toLowerCase(),
url: path,
params: requestOptions.query,
data: requestOptions.body,
headers: headers
});
return unwrapResponse(response.data);
```
- [x] **Step 3: 从零实现 utils/ApiClient.js 并只依赖 AxiosRequestUtil**
```js
var requestUtil = getAxiosRequestUtil();
var requester = requestUtil.createRequester({
baseUrl: baseUrl,
clientId: clientId,
getToken: getToken,
axiosInstance: settings.axiosInstance || root.axios
});
```
- [x] **Step 4: 删除旧 fetch 请求文件和旧公共 API 文件**
Run: `Remove-Item -LiteralPath utils\\RequestUtil.js; Remove-Item -LiteralPath public\\js\\api-client.js`
Expected: 两个文件不存在,后续扫描无 `RequestUtil``fetchImpl``root.fetch``fetch(``public/js/api-client.js` 匹配。
- [x] **Step 5: 运行工具和 API 客户端测试**
Run: `node tests\\utils.test.js; node tests\\api-client.test.js; node --check utils\\AxiosRequestUtil.js; node --check utils\\ApiClient.js`
Expected: 所有命令通过。
### Task 3: 更新页面公共脚本顺序
**Files:**
- Modify: 所有根目录中加载 `public/js/api-client.js``utils/ApiClient.js` 的 HTML 页面。
- Test: `tests/auth-page-structure.test.js`
- Test: `tests/api-script-order.test.js`
- [x] **Step 1: 以自动发现的脚本顺序测试锁定页面依赖**
```js
assert.strictEqual(pages.length, 45);
assert.ok(axiosIndex < axiosRequestUtilIndex);
assert.ok(axiosRequestUtilIndex < apiClientIndex);
assert.strictEqual(html.includes('utils/RequestUtil.js'), false);
```
- [x] **Step 2: 运行测试确认旧脚本顺序不满足新契约**
Run: `node tests\\auth-page-structure.test.js`
Expected: FAIL,仍有页面加载旧 API 客户端或未加载 Axios 和 AxiosRequestUtil。
- [x] **Step 3: 逐页替换公共脚本引用**
```html
<script src="utils/axios.js"></script>
<script src="utils/AxiosRequestUtil.js"></script>
<script src="utils/ApiClient.js"></script>
```
- [x] **Step 4: 运行结构测试和静态扫描**
Run: `node tests\\auth-page-structure.test.js; rg -n "utils/RequestUtil\\.js|public/js/api-client\\.js|fetchImpl|root\\.fetch|fetch\\(" --glob "!public/tac/**" --glob "!utils/axios.js" .`
Expected: 测试通过,旧请求层标识无匹配。
### Task 4: 重跑 PC 复核并更新记录
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Modify: `docs/superpowers/plans/2026-07-10-axios-request-layer-refactor.md`
- Modify: `docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md`
- [x] **Step 1: 运行所有 Node 测试与语法检查**
Run: `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; node --check utils\\AxiosRequestUtil.js; node --check utils\\ApiClient.js; git diff --check`
Expected: 所有测试通过,语法检查和差异检查无错误。
- [x] **Step 2: 执行 PC-014 复核命令并记录外部网关状态**
Run: `curl.exe -I --max-time 15 http://test-genealogy-api.ddxcjp.cn/captcha/require`
Expected: 记录当次结果;仅本地验证通过的项标为“本地复核通过”,网关不可用时标注“外部环境待验证”。
- [x] **Step 3: 写入完成时间和清理结论**
Record: Axios 文件来源、删除的 `fetch` 文件、页面脚本替换范围、所有验证命令及结果。
## 自检
- 所有底层网络调用都从 `utils/AxiosRequestUtil.js` 发起。
- `public/js` 中不含 `fetch`、Axios 实例创建或请求头拼装。
- 所有页面在页面业务脚本前加载 `utils/axios.js``utils/AxiosRequestUtil.js``utils/ApiClient.js`
- `utils/RequestUtil.js``public/js/api-client.js``fetchImpl``root.fetch` 和旧页面脚本引用均不存在。
## 完成记录
- 完成时间:2026-07-10 10:10:56 +08:00。
- Axios 来源:`axios@1.7.9/dist/axios.js` 的本地非压缩浏览器构建,存放于 `utils/axios.js`
- 请求契约:所有 PC 请求均由 `utils/AxiosRequestUtil.js` 通过 `axios.request` 发起;`utils/ApiClient.js` 仅封装 PC 接口和 token 生命周期。
- 配置边界:根目录 `config.js` 是接口基础地址唯一来源;`ApiClient` 不再保存 `DEFAULT_BASE_URL`
- 页面范围:45 个加载接口客户端的 HTML 页面已统一脚本顺序;旧 `utils/RequestUtil.js``public/js/api-client.js` 已删除。
- 验证:全量 Node 测试、语法检查、45 页脚本顺序检查、旧 fetch/旧脚本扫描和 `git diff --check` 均通过。
@@ -1,20 +0,0 @@
# 2026-07-10 验证结果参数格式修正计划
- 计划编号:PC-021
- 开始时间:2026-07-10 14:31:14 +08:00
- 页面范围:`login.html``register.html``forgot-password.html`
- 接口范围:`POST /captcha/verify`
- 新契约:验证轨迹统一提交为 `payload.track`,不再提交旧的 `payload.data`
## 执行清单
1. [x] 写失败测试:按后端最新示例断言 `payload.track``left``top``trackList`
2. [x] 修改 `captcha-pages.js`:把 TAC 轨迹转换成后端要求的 `payload.track`
3. [x] 更新追踪文档中旧的 `payload.data.trackList` 记录。
4. [x] 运行焦点测试、语法检查和空白检查。
## 验证记录
- 2026-07-10 14:31:14 +08:00:开始按新请求体契约补失败测试。
- 2026-07-10 14:31:14 +08:00`node tests\captcha-pages.test.js` 已按预期失败,失败点为实际请求体仍提交 `payload.id``payload.data`
- 2026-07-10 14:34:28 +08:00:修复后 `node tests\captcha-pages.test.js``node tests\auth-page-structure.test.js``node tests\auth-pages.test.js``node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有 LF/CRLF 换行提示。
@@ -1,26 +0,0 @@
# 2026-07-10 登录页样式与认证流程修正计划
- 计划编号:PC-022 / PC-023
- 开始时间:2026-07-10 14:42:32 +08:00
- 页面范围:`login.html``register.html``forgot-password.html`
- 接口范围:登录、注册、忘记密码提交前的 `/captcha/verify` 取票据流程
## PC-022 登录页样式优化
1. [x] 优化 `public/css/login.css` 中登录方式切换区,让选中状态、文本居中和整体边框更自然。
2. [x] 增加登录按钮与底部链接之间的间距。
3. [x] 保持样式在 CSS 文件内,不用 JS 注入样式。
## PC-023 注册跳转与验证码流程复核
1. [x] 写失败测试:注册完成后必须跳转 `login.html`
2. [x] 写结构测试:三张认证页都必须声明验证码场景并携带 `validToken`
3. [x] 修改 `register.html` 的成功跳转。
4. [x] 复核公共 `CaptchaPages` 仍统一按 `payload.track` 换取 `validToken`
5. [x] 运行相关测试、语法检查和空白检查。
## 验证记录
- 2026-07-10 14:42:32 +08:00:开始记录 PC-022 / PC-023,先补认证页结构测试。
- 2026-07-10 14:42:32 +08:00`node tests\auth-page-structure.test.js` 已按预期失败,失败点为 `register.html` 仍跳转 `create-genealogy.html`
- 2026-07-10 14:50:23 +08:00:修复后 `node tests\auth-page-structure.test.js``node tests\auth-pages.test.js``node tests\captcha-pages.test.js``node --check public\js\auth-pages.js``node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有 LF/CRLF 换行提示。
@@ -1,150 +0,0 @@
# PC 接口对接记录全量复核 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:** 重新验证 `pc-api-page-integration-tracker-2026-07-09.md` 中 PC-000 至 PC-013 的完成状态,并以当前代码、当前 PC YAML 和本次命令结果修正记录。
**Architecture:** `genealogy-pc-openapi.yaml``paths` 是接口范围唯一来源;代码和 HTML 是当前实现来源;Node 测试、语法检查和静态扫描是可重复验证来源。无法由前端离线验证的测试网关可用性明确标记为“外部环境待验证”,不将其当作页面对接完成证据。
**Tech Stack:** OpenAPI YAML、原生 JavaScript、Node `assert` 测试、PowerShell 静态扫描、Git 差异检查。
## Global Constraints
- 计划创建时间:2026-07-10 09:20:38 +08:00。
- 范围仅限追踪表中状态为“已完成”或“已修正”的 PC-000 至 PC-013;不新增业务接口或页面功能。
- 复核时不采用旧 APP 路径,不以历史测试文字代替当前命令结果。
- 每个结论必须记录命令、结果、时间和证据边界;证据不足必须改为“复核失败/待修正”或“外部环境待验证”。
- 仅在记录与事实不一致时修改文档;不改无关业务代码。
---
### Task 1: 复核契约和配置项
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Read: `genealogy-pc-openapi.yaml`
- Read: `config.js`
- Read: `public/js/api-client.js`
- Test: `tests/config.test.js`
- Test: `tests/api-client.test.js`
**Covers:** PC-000、PC-001、PC-007、PC-010。
- [x] **Step 1: 核对 YAML 路径与客户端配置**
Run: `rg -n "^ /captcha/|^ /auth/code:|^ /genealogy/pc/auth/|^ /genealogy/pc/files/|^ /genealogy/region/" genealogy-pc-openapi.yaml; rg -n "test-genealogy-api|ced7e5f0498645c6ec642dcf450b036f|web_pc" config.js public/js/api-client.js`
Expected: 当前 YAML 只包含验证码、PC 认证、PC 文件、地区路径;代码使用当前 PC clientId 和开发环境 HTTP 基础地址。
- [x] **Step 2: 运行配置与 API 客户端测试**
Run: `node tests\\config.test.js; node tests\\api-client.test.js; node --check config.js; node --check public\\js\\api-client.js`
Expected: 四条命令均通过。
### Task 2: 复核通用工具与未覆盖业务降级
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Read: `utils/StorageUtil.js`
- Read: `utils/FormUtil.js`
- Read: `utils/RequestUtil.js`
- Read: `public/js/api-client.js`
- Test: `tests/utils.test.js`
- Test: `tests/api-client.test.js`
**Covers:** PC-002、PC-008。
- [x] **Step 1: 运行工具和 API 降级测试**
Run: `node tests\\utils.test.js; node tests\\api-client.test.js; node tests\\pc-api-contract.test.js`
Expected: 三个测试通过,PC 请求契约扫描无遗留项。
- [x] **Step 2: 核对未覆盖业务的错误边界**
Run: `rg -n "PC_API_NOT_AVAILABLE" public\\js\\api-client.js`
Expected: 未被 YAML `paths` 覆盖的业务方法统一受控失败,不生成旧 APP 网络请求。
### Task 3: 复核认证、验证码与资料页面
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Read: `login.html`
- Read: `register.html`
- Read: `forgot-password.html`
- Read: `public/js/auth-pages.js`
- Read: `public/js/captcha-pages.js`
- Read: `utils/MessageUtil.js`
- Test: `tests/auth-pages.test.js`
- Test: `tests/captcha-pages.test.js`
- Test: `tests/auth-page-structure.test.js`
- Test: `tests/auth-message.test.js`
- Test: `tests/profile-pages.test.js`
- Test: `tests/security-pages.test.js`
**Covers:** PC-003、PC-009、PC-011、PC-012、PC-013。
- [x] **Step 1: 检查认证场景、接口路径、TAC 挂载和 layui 提示**
Run: `rg -n "WEB_H5_LOGIN|WEB_H5_REGISTER|WEB_H5_FORGOT_PASSWORD|captcha-modal.css|auth-captcha-modal|MessageUtil|layer.msg" login.html register.html forgot-password.html public\\js\\auth-pages.js public\\js\\captcha-pages.js utils\\MessageUtil.js`
Expected: 三张认证页分别使用正确场景;验证码使用页面级挂载点;提示由 `MessageUtil`/layui 提供。
- [x] **Step 2: 运行认证和验证码测试**
Run: `node tests\\auth-pages.test.js; node tests\\captcha-pages.test.js; node tests\\auth-page-structure.test.js; node tests\\auth-message.test.js; node tests\\profile-pages.test.js; node tests\\security-pages.test.js; node --check public\\js\\auth-pages.js; node --check public\\js\\captcha-pages.js; node --check utils\\MessageUtil.js`
Expected: 所有测试和语法检查通过。
- [x] **Step 3: 记录网关外部依赖边界**
Run: `curl.exe -I --max-time 15 http://test-genealogy-api.ddxcjp.cn/captcha/require`
Expected: 仅记录当次 HTTP 结果;无论结果如何,不将测试网关可用性视为前端实现的通过或失败。
### Task 4: 复核文件与地区公共能力
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Read: `public/js/upload-pages.js`
- Read: `public/js/region-pages.js`
- Test: `tests/upload-pages.test.js`
- Test: `tests/region-pages.test.js`
**Covers:** PC-004、PC-005、PC-006。
- [x] **Step 1: 运行文件和地区测试**
Run: `node tests\\upload-pages.test.js; node tests\\region-pages.test.js; node --check public\\js\\upload-pages.js; node --check public\\js\\region-pages.js`
Expected: 所有测试和语法检查通过。
- [x] **Step 2: 扫描旧路径与页面覆盖边界**
Run: `rg -n "PC_API_NOT_AVAILABLE" utils\\ApiClient.js`
Expected: 文件、地区旧 APP 路径无匹配;业务页面覆盖边界仍由 `PC_API_NOT_AVAILABLE` 保护。
### Task 5: 全量验证与追踪表修正
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Modify: `docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md`
- [x] **Step 1: 运行全量测试和差异检查**
Run: `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; git diff --check`
Expected: 所有测试通过,差异检查无空白错误。
- [x] **Step 2: 写入复核矩阵和最终状态**
Record: PC-000 至 PC-013 每项的本次复核时间、验证命令、结论和外部环境边界;任何不一致项从“已完成”改为“待修正”。
## 自检
- 覆盖范围:PC-000 至 PC-013 的所有“已完成/已修正”项均被归入一个复核任务。
- 来源一致性:接口范围只引用 `genealogy-pc-openapi.yaml``paths`,不引用旧 APP 文档。
- 状态规则:只有当前命令和当前代码同时支持的结论才能保留“已完成”。
@@ -1,128 +0,0 @@
# Registration Token State Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Ensure registration never creates or retains a browser authentication token, while password and SMS login continue to persist theirs.
**Architecture:** `utils/ApiClient.js` remains the single owner of browser token persistence. The `register()` method will leave its request and response contract unchanged, then clear the configured storage key after a successful response. `tests/api-client.test.js` will exercise the behavior through the public client methods and an in-memory storage adapter.
**Tech Stack:** Browser JavaScript (UMD), Node.js `assert` tests, Axios request adapter.
## Global Constraints
- Only `login()` and `loginBySms()` may persist a response token under the configured token key.
- Successful `register()` clears the configured token key even when its response contains `token`, `accessToken`, or `tokenValue`.
- Failed registration leaves existing token storage unchanged.
- Registration page behavior remains a redirect to `login.html`.
- Do not refactor unrelated request, storage, page, or authentication behavior.
---
### Task 1: Enforce registration token state in the API client
**Files:**
- Modify: `tests/api-client.test.js:83-111`
- Modify: `utils/ApiClient.js:176-182`
**Interfaces:**
- Consumes: `client.register(body)` and `client.loginBySms(body)` from `GenealogyApi.createClient()`.
- Produces: `client.register(body)` returns the API response while removing the configured token key after a successful request.
- [ ] **Step 1: Write the failing regression test**
Insert the following immediately after the existing `client.loginBySms(...)` assertions and before `client.sendSmsCode(...)` in `tests/api-client.test.js`:
```js
assert.strictEqual(store.values['token-key'], 'server-token');
store.setItem('token-key', 'existing-token');
const registerResponse = await client.register({
phone: '13900000000',
password: 'register-md5',
nickName: 'new-user',
validToken: 'register-ticket'
});
assert.deepStrictEqual(registerResponse, {
token: 'server-token',
url: '/genealogy/pc/auth/register'
});
assert.strictEqual(calls[8].url, '/genealogy/pc/auth/register');
assert.strictEqual(calls[8].headers.Authorization, undefined);
assert.strictEqual(calls[8].data.grantType, 'password');
assert.strictEqual(calls[8].data.registerSource, 'PC');
assert.strictEqual(store.getItem('token-key'), null);
const failedRegisterStore = createStore({
'token-key': 'existing-token'
});
const failedRegisterClient = GenealogyApi.createClient({
baseUrl: 'https://test-genealogy-api.ddxcjp.cn',
clientId: 'client-x',
tenantId: 'tenant-x',
tokenKey: 'token-key',
tokenStore: failedRegisterStore,
axiosInstance: {
request: async () => {
throw new Error('register failed');
}
}
});
await assert.rejects(
failedRegisterClient.register({
phone: '13900000001',
password: 'register-md5',
nickName: 'failed-user',
validToken: 'register-ticket'
}),
/register failed/
);
assert.strictEqual(failedRegisterStore.getItem('token-key'), 'existing-token');
```
Move the existing `client.sendSmsCode(...)` assertions from index `8` to index `9`, then change the final call-count assertion from `9` to `10`.
- [ ] **Step 2: Run the regression test and confirm the current failure**
Run: `node tests/api-client.test.js`
Expected: failure at `assert.strictEqual(store.getItem('token-key'), null)` because the current `register()` implementation writes `server-token` to `token-key`. The failed-registration assertion will already pass, documenting that the token must change only after a successful response.
- [ ] **Step 3: Implement the minimal token-state change**
In `utils/ApiClient.js`, replace the body of `register()` with:
```js
async function register(body) {
var data = await request('POST', '/genealogy/pc/auth/register', {
auth: false,
body: Object.assign({ grantType: 'password', registerSource: 'PC' }, withTenant(body))
});
clearToken();
return data;
}
```
- [ ] **Step 4: Run the focused test and confirm it passes**
Run: `node tests/api-client.test.js`
Expected: `api-client tests passed`.
- [ ] **Step 5: Run the complete Node test suite**
Run: `Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { node $_.FullName }`
Expected: every test script prints its success message and PowerShell exits with code `0`.
- [ ] **Step 6: Check the final diff and commit the implementation**
Run: `git diff --check`
Expected: exit code `0` with no whitespace errors.
Then commit only the implementation files:
```powershell
git add -- utils/ApiClient.js tests/api-client.test.js
git commit -m "fix: clear token after registration"
```
@@ -1,84 +0,0 @@
# TAC 默认层调用纠正实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**目标:** 在不写任何验证码 CSS、也不修改 TAC 第三方文件的前提下,将 TAC 挂载到 Layui 默认弹层中,不再渲染到认证页底部。
**架构:** `auth-pages.js` 仍在表单提交时调用 `CaptchaPages.ensureToken``captcha-pages.js` 通过已加载的 `layui.layer.open` 创建默认容器,将该容器传给 `CaptchaConfig.bindEl`,然后原样调用 `new TAC(config)`。TAC 继续根据后端下发的 `captchaType` 自己选择滑块、旋转、拼图、文字点击或禁用态。
**技术栈:** Layui 内置 `layer``public/tac/css/tac.css``public/tac/js/tac.min.js`、原生 JavaScript、Node `assert` 测试。
## 全局约束
- 计划创建时间:2026-07-10 10:38:10 +08:00。
- 不修改 `public/tac/css/tac.css``public/tac/js/tac.min.js` 或 Layui 第三方文件。
- 不创建或注入任何验证码 CSS;不传 `bgUrl``logoUrl`、尺寸、皮肤等 TAC 视觉配置。
- 只使用 Layui 默认 `layer.open` 作为弹层容器,TAC 的内部外观完全由其自带文件负责。
- 只修改认证三页、验证码适配、对应测试和记录。
---
### 任务 1:锁定正确的弹层挂载契约
**文件:**
- 修改:`tests/auth-page-structure.test.js`
- 修改:`tests/captcha-pages.test.js`
- [x] **步骤 1:写页面结构失败测试。**
断言 `login.html``register.html``forgot-password.html` 不再保留 `[data-captcha-box]` 静态挂载点,且 `public/layui/layui.js``captcha-pages.js` 前加载。
- [x] **步骤 2:写验证码适配失败测试。**
断言适配层调用 `root.layui.layer.open`、在结束时调用 `root.layui.layer.close`,仍只调用 `new root.TAC(config)`,不存在自定义验证码视觉标识。
- [x] **步骤 3:运行测试并确认当前底部挂载实现失败。**
运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js`
预期:因静态 `[data-captcha-box]` 与缺少 `layer.open` 而失败。
### 任务 2:使用 Layui 默认层承载 TAC
**文件:**
- 修改:`login.html`
- 修改:`register.html`
- 修改:`forgot-password.html`
- 修改:`public/js/captcha-pages.js`
- [x] **步骤 1:删除三页静态 `[data-captcha-box]`。**
- [x] **步骤 2:在 `captcha-pages.js` 创建无样式的 DOM 容器,并以 `layui.layer.open({ type: 1, content: mount })` 打开默认弹层。**
- [x] **步骤 3:将该 DOM 容器作为 `CaptchaConfig.bindEl`,并保持 `new TAC(config)` 原样调用。**
- [x] **步骤 4:在 TAC 成功、关闭、初始化异常和 Promise 异常时销毁 TAC 并关闭对应 Layui 层。**
- [x] **步骤 5:运行聚焦测试和语法检查。**
运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js`
预期:全部通过。
### 任务 3:全量验证和记录
**文件:**
- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md`
- 修改:`docs/superpowers/plans/2026-07-10-tac-layer-call-correction.md`
- [x] **步骤 1:运行全量 Node 测试、静态扫描和差异检查。**
运行:`Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; rg -n "data-captcha-box|auth-captcha-modal|captcha-modal.css|bgUrl|logoUrl" login.html register.html forgot-password.html public\css public\js; git diff --check`
预期:测试和差异检查通过;认证页中不再有静态验证码挂载点或自定义视觉标识。
- [x] **步骤 2:记录完成时间、TAC 功能边界和浏览器实测边界。**
## 完成记录
- 完成时间:2026-07-10 10:44:11 +08:00。
- 根因:TAC 的 `init()` 只向 `CaptchaConfig.bindEl` 追加组件;原先静态挂载点位于 `body` 末尾,移除定位样式后自然渲染在页面底部。
- 修改:删除三页静态 `[data-captcha-box]`;验证码适配层使用 Layui 默认 `layer.open` 创建临时容器并传给 `bindEl`;成功、TAC 关闭、初始化异常与 Promise 异常都会清空容器并关闭同一层。
- TAC 边界:保留原生 `new TAC(config)`、原生样式、原生刷新和后端类型分发;不传视觉配置,不写验证码 CSS,不改动任何 `public/tac` 文件。
- 验证:失败测试已复现底部挂载;聚焦测试、全量 Node 测试、`node --check public/js/captcha-pages.js``git diff --check` 均通过。
- 浏览器边界:未在真实测试网关完成一次有效轨迹验证;仍需在浏览器点击注册后确认 Layui 默认层居中显示及后端 `validToken` 返回。
@@ -1,23 +0,0 @@
# 2026-07-10 TAC 弹层居中修复计划
- 计划编号:PC-020
- 开始时间:2026-07-10 11:11:05 +08:00
- 页面范围:`login.html``register.html``forgot-password.html`
- 接口范围:`/captcha/require``/captcha/challenge``/captcha/verify`
- 约束:不修改 `public/tac`,不新增验证码自定义样式,不传 TAC 视觉配置,只调整 Layui 弹层调用参数。
## 根因
Layui 打开页面层时,`content` 里的验证码挂载点还是空节点;TAC 后续异步创建自己的 `#tianai-captcha-parent`,该原生外层尺寸是 `318px * 318px`。Layui 已经按空内容完成定位,所以最终验证码窗口会偏离预期位置。
## 执行清单
1. [x] 写失败测试:要求验证码弹层通过 Layui `area` 预留 `318px * 318px`,并保持默认居中。
2. [x] 修改 `captcha-pages.js`:只给 `layer.open` 增加 TAC 原生尺寸和默认居中参数。
3. [x] 运行焦点测试、结构测试、语法检查和空白检查。
4. [x] 将完成结果同步回 `pc-api-page-integration-tracker-2026-07-09.md`
## 验证记录
- 2026-07-10 11:11:05 +08:00`node tests\captcha-pages.test.js` 已按预期失败,失败点为 `layerOptions.area` 缺失。
- 2026-07-10 11:14:31 +08:00:修复后 `node tests\captcha-pages.test.js``node tests\auth-page-structure.test.js``node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有既有 LF/CRLF 换行提示。
@@ -1,72 +0,0 @@
# TAC Layui 实际挂载点修复实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**目标:** 修复 Layui 遮罩已显示、验证码接口已返回 200,但 TAC 验证窗口仍不显示的问题。
**架构:** `CaptchaPages.createCaptchaLayer` 使用 Layui `layer.open` 的字符串 `content` 创建弹层内部真实挂载节点;弹层创建完成后再从 `document` 查询该实际节点,并把它交给 `CaptchaConfig.bindEl`。这样 Layui 负责弹层,TAC 挂到已经进入页面的真实 DOM。
**技术栈:** Layui 2.11.5、TAC 原生 CSS/JS、原生 JavaScript、Node `assert` 测试。
## 全局约束
- 计划创建时间:2026-07-10 10:59:52 +08:00。
- 不修改 `public/tac/css/tac.css``public/tac/js/tac.min.js` 或 Layui 第三方文件。
- 不新增验证码自定义 CSS,不注入样式,不传 TAC 视觉配置。
- 保留 `auth-pages.js -> CaptchaPages.ensureToken -> TAC` 调用链,只修正弹层挂载点。
---
### 任务 1:复现“遮罩有但实际挂载点不可见”
**文件:**
- 修改:`tests/captcha-pages.test.js`
- [x] **步骤 1:写失败测试**
断言 `layer.open``content` 是包含唯一 `data-captcha-layer-box` 的字符串;`createCaptchaLayer` 必须在弹层打开后从 `document.querySelector` 取到实际挂载节点并返回。
- [x] **步骤 2:运行失败测试**
运行:`node tests\captcha-pages.test.js`
预期:失败,当前实现仍返回打开前创建的原生 DOM。
### 任务 2:修正实际挂载点
**文件:**
- 修改:`public/js/captcha-pages.js`
- [x] **步骤 1:最小实现**
增加内部自增 ID,用字符串内容创建 `<div data-captcha-layer-box="..."></div>``layer.open` 后查询该节点并作为 `box` 返回。
- [x] **步骤 2:运行聚焦测试**
运行:`node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js`
预期:全部通过。
### 任务 3:记录和回归
**文件:**
- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md`
- 修改:`docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md`
- [x] **步骤 1:补充 PC-019 记录**
记录用户浏览器复现现象、根因、修正方式、验证命令。
- [x] **步骤 2:最终验证**
运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js; git diff --check`
预期:全部通过;`git diff --check` 仅允许 Git 行尾提示,不允许空白错误。
## 完成记录
- 完成时间:2026-07-10 11:04:18 +08:00。
- 根因:PC-018 只让 `layer.open` 不再因 `.parents()` 报错,但仍把打开前创建的临时 DOM 返回给 TAC;真实浏览器里 Layui 遮罩已出现,`/captcha/challenge` 也返回 200,但 TAC 没有挂到可见弹层内容区。
- 修复:`layer.open` 使用字符串 `content` 创建弹层内部真实节点,随后通过 `document.querySelector('[data-captcha-layer-box="..."]')` 获取实际挂载点并交给 `CaptchaConfig.bindEl`
- 约束:未修改 `public/tac`,未新增验证码 CSS,未传 TAC 视觉配置。
- 验证:失败测试已先复现;修复后 `node tests\captcha-pages.test.js``node --check public\js\captcha-pages.js` 通过。最终回归记录见 `docs/pc-api-page-integration-tracker-2026-07-09.md` 的 PC-019。
@@ -1,71 +0,0 @@
# TAC Layui content 契约修复实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**目标:** 修复认证页点击后只出现 `d.parents is not a function` 提示、TAC 弹层不显示的问题。
**架构:** `CaptchaPages.createCaptchaLayer` 继续只负责创建 Layui 默认弹层容器,不写验证码样式、不改 TAC 文件。Layui `layer.open``content` 使用 Layui/jQuery 包装对象满足弹层内部 `.parents()` 契约,TAC `bindEl` 继续使用对应的真实 DOM 节点。
**技术栈:** Layui 2.11.5、TAC 原生 CSS/JS、原生 JavaScript、Node `assert` 测试。
## 全局约束
- 计划创建时间:2026-07-10 10:50:06 +08:00。
- 不修改 `public/tac/css/tac.css``public/tac/js/tac.min.js` 或 Layui 第三方文件。
- 不新增验证码自定义 CSS,不注入样式,不传 TAC 视觉配置。
- 只修正 Layui 弹层 content 契约、对应测试和记录文档。
---
### 任务 1:复现 Layui content 契约错误
**文件:**
- 修改:`tests/captcha-pages.test.js`
- [x] **步骤 1:写失败测试**
断言 `createCaptchaLayer` 传给 `layui.layer.open``content` 必须是包装对象,包装对象需要保留真实 DOM 节点,并具备 `.parents()` 方法。
- [x] **步骤 2:运行失败测试**
运行:`node tests\captcha-pages.test.js`
预期:失败,当前实现把原生 DOM 直接传给 `content`
### 任务 2:修正 createCaptchaLayer
**文件:**
- 修改:`public/js/captcha-pages.js`
- [x] **步骤 1:最小实现**
`createCaptchaLayer` 内用 `root.layui.$ || root.$ || root.jQuery` 包装真实 DOM 节点;`layer.open({ content })` 使用包装对象;返回值中的 `box` 仍为真实 DOM,供 TAC `bindEl` 使用。
- [x] **步骤 2:运行聚焦测试**
运行:`node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js`
预期:全部通过。
### 任务 3:记录和回归
**文件:**
- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md`
- 修改:`docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md`
- [x] **步骤 1:补充 PC-018 完成记录**
记录根因、修改文件、验证命令和结果。
- [x] **步骤 2:运行最终验证**
运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js; git diff --check`
预期:全部通过;`git diff --check` 仅允许 Git 行尾提示,不允许空白错误。
## 完成记录
- 完成时间:2026-07-10 10:54:03 +08:00。
- 根因:Layui 2.11.5 的 `layer.open` 在处理 DOM 内容时会调用 `.parents()`;原实现将原生 DOM 节点直接传给 `content`,导致浏览器报 `d.parents is not a function`,弹层创建中断。
- 修复:`createCaptchaLayer` 使用 `layui.$``$``jQuery` 包装临时 DOM 后传给 `layer.open({ content })`,同时继续把真实 DOM 节点传给 `CaptchaConfig.bindEl`
- 验证:失败测试已先复现;修复后 `node tests\captcha-pages.test.js``node --check public\js\captcha-pages.js` 通过。最终回归记录见 `docs/pc-api-page-integration-tracker-2026-07-09.md` 的 PC-018。
@@ -1,150 +0,0 @@
# 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 可用性问题仍需后端或网关处理;该前端修复保证取消或异常后不残留页面遮罩。
@@ -1,85 +0,0 @@
# TAC 原生样式调用清理实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**目标:** 登录、注册、找回密码页面只调用 `public/tac` 自带的验证码组件和样式,不再追加任何验证码视觉样式或视觉配置。
**架构:** 页面保留一个无 class 的 `[data-captcha-box]` 挂载点;`captcha-pages.js` 只负责验证码接口参数和成功、取消、异常后的 DOM 清理;TAC 的外观完全由 `public/tac/css/tac.css``tac.min.js` 决定。
**技术栈:** 原生 JavaScript、TAC 本地组件、Node `assert` 测试。
## 全局约束
- 计划创建时间:2026-07-10 10:19:46 +08:00。
- 不修改 `public/tac/css/tac.css``public/tac/js/tac.min.js`
- 不为验证码新增、覆盖或注入 CSS;不向 `new TAC` 传入 `bgUrl``logoUrl` 等视觉配置。
- 仅修改登录、注册、找回密码三页及其验证码适配、测试和记录。
---
### 任务 1:用测试锁定原生 TAC 调用边界
**文件:**
- 修改:`tests/auth-page-structure.test.js`
- 修改:`tests/captcha-pages.test.js`
- [x] **步骤 1:将页面结构测试改为禁止自定义验证码 CSS 与容器 class。**
断言三页加载 `public/tac/css/tac.css`,不加载 `public/css/captcha-modal.css`,且 `[data-captcha-box]` 不含 `auth-captcha-modal` class。
- [x] **步骤 2:将组件测试改为要求原生构造调用。**
断言 `captcha-pages.js` 中不存在 `bgUrl``logoUrl``.auth-captcha-modal` 视觉控制。
- [x] **步骤 3:运行测试并确认当前实现失败。**
运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js`
预期:因当前自定义验证码 CSS、容器 class 和 TAC 视觉配置而失败。
### 任务 2:删除自定义验证码视觉层
**文件:**
- 修改:`login.html`
- 修改:`register.html`
- 修改:`forgot-password.html`
- 修改:`public/css/public.css`
- 删除:`public/css/captcha-modal.css`
- 修改:`public/js/captcha-pages.js`
- [x] **步骤 1:删除三页对 `captcha-modal.css` 的引用,并将挂载点改为无 class 的 `data-captcha-box`。**
- [x] **步骤 2:删除 `public.css` 中 `.auth-captcha-modal` 及其 TAC 尺寸限制规则。**
- [x] **步骤 3:删除 `captcha-modal.css`。**
- [x] **步骤 4:将 `new TAC(config, { ... })` 改为 `new TAC(config)`,并将挂载点查询、打开和关闭逻辑改为无视觉状态的 DOM 清理。**
- [x] **步骤 5:运行两项聚焦测试并确认通过。**
运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js`
预期:全部通过。
### 任务 3:全量验证并记录
**文件:**
- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md`
- 修改:`docs/superpowers/plans/2026-07-10-tac-native-style-cleanup.md`
- [x] **步骤 1:运行全量 Node 测试、验证码样式静态扫描和差异检查。**
运行:`Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; rg -n "captcha-modal.css|auth-captcha-modal|bgUrl|logoUrl" login.html register.html forgot-password.html public\css public\js; git diff --check`
预期:测试与差异检查通过;扫描没有自定义验证码视觉标识。
- [x] **步骤 2:记录完成时间、修改范围、验证结果和外部验证码实测边界。**
## 完成记录
- 完成时间:2026-07-10 10:32:20 +08:00。
- 删除 `public/css/captcha-modal.css`,并从 `public/css/public.css` 删除 35 行 `.auth-captcha-box``.auth-captcha-modal` 和 TAC 尺寸限制规则。
- 三个认证页仅加载 `public/tac/css/tac.css`,无 class 的 `[data-captcha-box]` 只作为 TAC 挂载点。
- `captcha-pages.js` 现在只执行 `new TAC(config)`;不再传入 `bgUrl``logoUrl` 或切换任何视觉 class。成功、取消、初始化异常后仍清空 TAC 第三方 DOM。
- 验证:聚焦结构/验证码测试、全量 Node 测试、`node --check public/js/captcha-pages.js`、自定义样式标识扫描和 `git diff --check` 均通过。
- 外部边界:尚未用有效验证码轨迹完成真实服务端验证;`/captcha/verify` 的尺寸校验问题仍需以后端挑战响应原始尺寸为准单独处理。
@@ -1,474 +0,0 @@
# Auth To Profile Flow Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Complete the PC registration, login, profile, unauthorized, logout, and endpoint-address flow with one token owner and deterministic redirects.
**Architecture:** `config.js` remains the sole runtime owner of the API base address. `utils/ApiClient.js` owns token validation, persistence, and cleanup; `profile-pages.js` decides whether a profile request is allowed and redirects unauthenticated users to the homepage; `profile-common.js` delegates confirmed logout to the API client. The integration tracker records the current address and verified behavior only after verification commands complete.
**Tech Stack:** Browser JavaScript (UMD and jQuery), Axios, Node.js `assert` tests, static HTML, Markdown.
## Global Constraints
- The current API base address is `http://182.61.18.23:8080` for development and production, and `config.js` is its only runtime owner.
- Runtime-related tests use the same address; only documents explicitly marked historical may retain the former test domain.
- Successful registration clears `genealogy_auth_token` and redirects to `login.html`; it never persists a registration response token.
- Only successful password or SMS login with a nonempty response token persists `genealogy_auth_token` and redirects to `profile.html`.
- `profile.html` and `profile-data.html` redirect to `index.html` before a profile request when no token exists, and after HTTP or business `401` responses.
- Confirmed logout requests `/genealogy/pc/auth/logout`, clears the token regardless of the request outcome, and redirects to `index.html`.
- Do not add global Axios redirects, duplicate token storage in pages, alter API paths, or change password-reset, phone-change, account-deactivation, or profile pages that do not load the profile API.
---
### Task 1: Synchronize the configured API address test
**Files:**
- Modify: `tests/config.test.js:11-28`
- Modify: `tests/utils.test.js:31`
**Interfaces:**
- Consumes: `configFactory(...).getConfig()` and `getApiBaseUrl()` from `config.js`.
- Produces: a test that asserts the current `config.js` address for both environments.
- [ ] **Step 1: Confirm the existing address-contract test fails**
The current test contains these stale assertions:
```js
assert.deepStrictEqual(developmentConfig.getConfig(), {
environment: 'development',
apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn'
});
assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://test-genealogy-api.ddxcjp.cn');
```
Run: `node tests/config.test.js`
Expected: failure because `config.js` already returns `http://182.61.18.23:8080`.
- [ ] **Step 2: Update the stale test expectations**
Replace the two expected URL values with:
```js
assert.deepStrictEqual(developmentConfig.getConfig(), {
environment: 'development',
apiBaseUrl: 'http://182.61.18.23:8080'
});
assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://182.61.18.23:8080');
```
Replace the mocked requester address in `tests/utils.test.js` with:
```js
baseUrl: 'http://182.61.18.23:8080',
```
- [ ] **Step 3: Run the focused configuration test**
Run: `node tests/config.test.js`
Expected: `config tests passed`.
Run: `node tests/utils.test.js`
Expected: `utils tests passed`.
- [ ] **Step 4: Commit the address-test synchronization**
```powershell
git add -- tests/config.test.js tests/utils.test.js
git commit -m "test: sync configured api address"
```
### Task 2: Enforce login-token and logout-cleanup contracts
**Files:**
- Modify: `utils/ApiClient.js:52-60, 167-191, 270-274`
- Modify: `tests/api-client.test.js:83-177`
**Interfaces:**
- Consumes: `getTokenFromResponse(data)`, `setToken(token)`, `clearToken()`, and public `client.login`, `client.loginBySms`, and `client.logout` methods.
- Produces: `client.login(body)` and `client.loginBySms(body)` reject with `ApiError('登录响应缺少 token')` when their response has no token; `client.logout()` clears the configured token key before resolving or rejecting.
- [ ] **Step 1: Write failing API-client assertions**
Append this after the registration assertions and before `client.sendSmsCode(...)` in `tests/api-client.test.js`:
```js
const missingTokenStore = createStore();
const missingTokenClient = GenealogyApi.createClient({
baseUrl: 'http://182.61.18.23:8080',
clientId: 'client-x',
tenantId: 'tenant-x',
tokenKey: 'token-key',
tokenStore: missingTokenStore,
axiosInstance: {
request: async () => ({
status: 200,
data: { code: 200, data: {} }
})
}
});
await assert.rejects(
missingTokenClient.login({ phone: '13800000000', password: 'md5' }),
(error) => error.message === '登录响应缺少 token'
);
assert.strictEqual(missingTokenStore.getItem('token-key'), null);
store.setItem('token-key', 'logout-token');
await client.logout();
assert.strictEqual(calls[9].url, '/genealogy/pc/auth/logout');
assert.strictEqual(store.getItem('token-key'), null);
const failedLogoutStore = createStore({
'token-key': 'logout-token'
});
const failedLogoutClient = GenealogyApi.createClient({
baseUrl: 'http://182.61.18.23:8080',
clientId: 'client-x',
tenantId: 'tenant-x',
tokenKey: 'token-key',
tokenStore: failedLogoutStore,
axiosInstance: {
request: async () => {
throw new Error('logout failed');
}
}
});
await assert.rejects(failedLogoutClient.logout(), /logout failed/);
assert.strictEqual(failedLogoutStore.getItem('token-key'), null);
```
Move the existing SMS-code assertions from `calls[9]` to `calls[10]` and change the final call-count assertion from `10` to `11`.
Replace the two existing `baseUrl: 'https://test-genealogy-api.ddxcjp.cn'` fixtures in this file with:
```js
baseUrl: 'http://182.61.18.23:8080',
```
- [ ] **Step 2: Run the focused API-client test and confirm failure**
Run: `node tests/api-client.test.js`
Expected: failure at the missing-token rejection because the current login method resolves without writing a token, before reaching the logout assertions.
- [ ] **Step 3: Add the minimal token validation and logout cleanup**
Add this helper after `getTokenFromResponse` in `utils/ApiClient.js`:
```js
function requireLoginToken(data) {
var token = getTokenFromResponse(data);
if (!token) throw new ApiError('登录响应缺少 token');
return token;
}
```
Replace the token writes in `login()` and `loginBySms()` with:
```js
setToken(requireLoginToken(data));
```
Replace `logout` with:
```js
logout: async function () {
try {
return await request('DELETE', '/genealogy/pc/auth/logout');
} finally {
clearToken();
}
},
```
- [ ] **Step 4: Run the API-client test and confirm all token paths pass**
Run: `node tests/api-client.test.js`
Expected: `api-client tests passed`.
- [ ] **Step 5: Commit the API-client contract**
```powershell
git add -- utils/ApiClient.js tests/api-client.test.js
git commit -m "fix: complete auth token lifecycle"
```
### Task 3: Guard profile access and connect confirmed logout
**Files:**
- Modify: `public/js/profile-pages.js:18-22, 165-198`
- Modify: `public/js/profile-common.js:9-82`
- Modify: `profile.html:80, 355`
- Modify: `tests/profile-pages.test.js:1-65`
- Create: `tests/profile-logout-structure.test.js`
**Interfaces:**
- Consumes: public `api.getToken()`, `api.clearToken()`, `api.currentProfile()`, `api.updateProfile()`, and `api.logout()` methods.
- Produces: `ProfilePages.shouldRedirectToHome(api, error)` for token and unauthorized classification; profile reads and writes redirect to `index.html` when that helper returns true; the existing logout controls call `api.logout()` before navigating to `index.html`.
- [ ] **Step 1: Write failing profile and logout tests**
Add these assertions before the final `console.log` in `tests/profile-pages.test.js`:
```js
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => ''
}), true);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}), false);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}, { status: 401 }), true);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}, { code: 401 }), true);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}, { status: 500 }), false);
```
Create `tests/profile-logout-structure.test.js` with:
```js
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const profileHtml = fs.readFileSync(path.join(__dirname, '..', 'profile.html'), 'utf8');
const profileCommon = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'profile-common.js'), 'utf8');
assert.match(profileHtml, /data-logout-confirm/);
assert.match(profileHtml, /<a class="btn primary magnetic" href="index\.html" data-logout-confirm>/);
assert.match(profileCommon, /function performLogout\(targetUrl\)/);
assert.match(profileCommon, /api\.logout\(\)/);
assert.match(profileCommon, /targetUrl \|\| 'index\.html'/);
console.log('profile logout structure tests passed');
```
- [ ] **Step 2: Run the new focused tests and confirm failure**
Run: `node tests/profile-pages.test.js`
Expected: failure because `shouldRedirectToHome` is not exported.
Run: `node tests/profile-logout-structure.test.js`
Expected: failure because the logout confirmation markup and `performLogout` function do not exist.
- [ ] **Step 3: Add profile token guard and unauthorized redirect**
Add these helpers after `getApi()` in `public/js/profile-pages.js`:
```js
function shouldRedirectToHome(api, error) {
var status = error && (error.status || error.code);
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
}
function redirectToHome() {
if (!root.location) return;
if (typeof root.location.replace === 'function') {
root.location.replace('index.html');
return;
}
root.location.href = 'index.html';
}
function redirectUnauthorized(api, error) {
if (!shouldRedirectToHome(api, error)) return false;
if (api && api.clearToken) api.clearToken();
redirectToHome();
return true;
}
```
Replace the existing combined early-return condition in `loadProfile()` with these two branches:
```js
if (!api || !documentRef || !documentRef.querySelector('[data-profile-page]')) return;
if (redirectUnauthorized(api)) return;
```
Update its `catch` block to start with:
```js
if (redirectUnauthorized(api, error)) return;
```
Update `submitProfileForm()` to place `if (redirectUnauthorized(api)) return;` after its existing null guard, and to start its `catch` block with `if (redirectUnauthorized(api, error)) return;` before setting an error status or message.
Export the classifier with the existing public methods:
```js
shouldRedirectToHome: shouldRedirectToHome,
```
- [ ] **Step 4: Connect both logout confirmations to the API client**
Add this function before `bindLogout()` in `public/js/profile-common.js`:
```js
function performLogout(targetUrl) {
var api = window.GenealogyApi && window.GenealogyApi.defaultClient;
var redirect = function () {
window.location.href = targetUrl || 'index.html';
};
if (!api || !api.logout) {
redirect();
return;
}
api.logout().catch(function () {}).then(redirect);
}
```
In `bindLogout()`, change the default target URL to `index.html`, replace the Layui confirmation callback body with `performLogout(targetUrl);`, and add this event handler before the existing `[data-logout-close]` handler:
```js
$(document).on('click', '[data-logout-confirm]', function (event) {
event.preventDefault();
closeLegacyLogout();
performLogout($(this).attr('href') || 'index.html');
});
```
In `profile.html`, change the legacy confirmation anchor to:
```html
<a class="btn primary magnetic" href="index.html" data-logout-confirm>确认退出</a>
```
- [ ] **Step 5: Run the focused profile tests and syntax checks**
Run: `node tests/profile-pages.test.js`
Expected: `profile-pages tests passed`.
Run: `node tests/profile-logout-structure.test.js`
Expected: `profile logout structure tests passed`.
Run: `node --check public/js/profile-pages.js`
Expected: exit code `0`.
Run: `node --check public/js/profile-common.js`
Expected: exit code `0`.
- [ ] **Step 6: Commit the profile and logout flow**
```powershell
git add -- public/js/profile-pages.js public/js/profile-common.js profile.html tests/profile-pages.test.js tests/profile-logout-structure.test.js
git commit -m "fix: guard profile auth and logout"
```
### Task 4: Record the current flow and run end-to-end regression checks
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md:1-9, 32, 70, 163`
- Modify: `docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md:3-5`
**Interfaces:**
- Consumes: the verified results of Tasks 1 through 3.
- Produces: a tracker whose current address and authentication-flow status match the code and tests, and a spec marked approved for implementation.
- [ ] **Step 1: Run all focused flow tests**
Run:
```powershell
node tests/config.test.js
node tests/api-client.test.js
node tests/auth-pages.test.js
node tests/profile-pages.test.js
node tests/profile-logout-structure.test.js
```
Expected: each command prints its success message and exits with code `0`.
- [ ] **Step 2: Run syntax and complete regression checks**
Run:
```powershell
node --check utils/ApiClient.js
node --check public/js/profile-pages.js
node --check public/js/profile-common.js
Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { & node $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } }
```
Expected: all syntax checks exit `0`; every test script prints its success message; PowerShell exits `0` on the first test failure instead of masking it.
- [ ] **Step 3: Update the tracker from verified evidence**
At the top of `docs/pc-api-page-integration-tracker-2026-07-09.md`, replace the active base-address line with:
```markdown
接口基础地址:`http://182.61.18.23:8080`
```
Add this section immediately after the introductory historical-record lines:
```markdown
## Current Endpoint Contract
- Runtime owner: `config.js`.
- Current development and production address: `http://182.61.18.23:8080`.
- Address change record: on 2026-07-11 the active address changed from the historical test domain to this IP address and port. Historical references to `test-genealogy-api.ddxcjp.cn` do not describe the current runtime configuration.
```
Replace the YAML-analysis item that says the project currently uses the test domain with:
```markdown
2. `servers` 仍是本地地址示例;当前运行时基础地址以 `config.js` 为准,为 `http://182.61.18.23:8080`
```
Replace the PC-010 row with:
```markdown
| PC-010 | 已复核(当前地址已同步) | `config.js``tests/config.test.js``utils/ApiClient.js` | API 基础地址 | 当前开发和生产地址均为 `http://182.61.18.23:8080`;地址唯一运行时来源为 `config.js``tests/config.test.js` 已同步断言。 |
```
After Steps 1 and 2 pass, run `Get-Date -Format "yyyy-MM-dd HH:mm:ss K"` and insert its output as the `完成时间` line immediately after the `状态` line in this record. Append the rest of this record after the existing PC entries:
```markdown
## PC-025 注册、登录到个人资料闭环
- 状态:已完成
- 规划文件:`docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md`
- 页面范围:`register.html``login.html``profile.html``profile-data.html`
- 接口范围:`/genealogy/pc/auth/register``/genealogy/pc/auth/login``/genealogy/pc/auth/login/sms``/genealogy/pc/auth/profile``/genealogy/pc/auth/logout`
- 地址契约:运行时唯一来源为 `config.js`;当前开发和生产地址均为 `http://182.61.18.23:8080`
- 流程结果:注册成功清除 token 并跳转 `login.html`;密码和短信登录必须取得 token 后跳转 `profile.html`;个人资料无 token 或收到 HTTP/业务 `401` 时清 token 并跳转 `index.html`;确认退出无论接口结果都清 token 并跳转 `index.html`
- 修改文件:`tests/config.test.js``utils/ApiClient.js``tests/api-client.test.js``public/js/profile-pages.js``public/js/profile-common.js``profile.html``tests/profile-pages.test.js``tests/profile-logout-structure.test.js`
- 验证结果:`node tests/config.test.js``node tests/api-client.test.js``node tests/auth-pages.test.js``node tests/profile-pages.test.js``node tests/profile-logout-structure.test.js`、三个 `node --check` 命令和全量 Node 测试均通过。
```
Change the spec status body to:
```markdown
Approved for implementation.
```
- [ ] **Step 4: Check final whitespace and commit the record**
Run: `git diff --check`
Expected: exit code `0`; pre-existing CRLF conversion warnings are not whitespace errors.
```powershell
git add -- docs/pc-api-page-integration-tracker-2026-07-09.md docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md
git commit -m "docs: record auth profile flow"
```
@@ -0,0 +1,129 @@
# 内容与协作页面状态收口 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 让缺少正式接口的内容、协作、消息与提醒页面明确展示开发状态,同时保留家族圈动态的真实功能。
**Architecture:** 复用 `public/js/pending-pages.js` 的页面状态契约。每个待开发页面仅增加 `data-feature-status`、中文提示文案和状态脚本引用;脚本阻止主内容区未声明可用的业务链接,不新增接口调用、示例数据或业务脚本。
**Tech Stack:** 原生 JavaScript、静态 HTML/CSS、Node LTS `node:test`
## Global Constraints
- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。
- `PC.openapi2.json` 中缺少正式接口的功能不得伪造请求或静态数据为真实业务。
- 家族圈动态 `profile-feed.html``profile-feed-edit.html` 已接入正式接口,不能标记为待开发。
- 所有新增注释使用中文;不写入测试账号或密码。
- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。
---
### Task 1: 扩展页面状态测试
**Files:**
- Modify: `tests/pending-pages.test.js`
**Interfaces:**
- Consumes: 每页的 `data-feature-status="pending"``public/js/pending-pages.js`
- Produces: 内容与协作待开发页面的静态约束,以及家族圈动态未被误标记的约束。
- [x] **Step 1: 写入失败测试**
`contentPendingPages` 中列出以下页面:
```js
const contentPendingPages = [
'profile-content.html', 'profile-article.html', 'profile-article-edit.html',
'profile-album.html', 'profile-video.html', 'profile-gift.html',
'profile-gift-edit.html', 'profile-growth.html', 'profile-growth-edit.html',
'profile-memo.html', 'profile-memo-edit.html', 'profile-merit.html',
'profile-merit-edit.html', 'profile-messages.html', 'profile-feedback.html',
'profile-admin-permissions.html', 'profile-data-reminders.html'
];
```
同时断言 `profile-feed.html``profile-feed-edit.html` 不含待开发状态标记,并验证只有 `data-feature-link="available"` 的业务链接可跳转。
- [x] **Step 2: 运行失败测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js`
Expected: 新增页面尚未标记,测试失败。
### Task 2: 标记内容与协作页面
**Files:**
- Modify: `profile-content.html`
- Modify: `profile-article.html`
- Modify: `profile-article-edit.html`
- Modify: `profile-album.html`
- Modify: `profile-video.html`
- Modify: `profile-gift.html`
- Modify: `profile-gift-edit.html`
- Modify: `profile-growth.html`
- Modify: `profile-growth-edit.html`
- Modify: `profile-memo.html`
- Modify: `profile-memo-edit.html`
- Modify: `profile-merit.html`
- Modify: `profile-merit-edit.html`
- Modify: `profile-messages.html`
- Modify: `profile-feedback.html`
- Modify: `profile-admin-permissions.html`
- Modify: `profile-data-reminders.html`
- Test: `tests/pending-pages.test.js`
**Interfaces:**
- Consumes: `PageAvailability.init()` 读取的 `<body data-feature-status="pending" data-feature-message="..."></body>`
- Produces: 可见的“功能开发中”提示,并拦截主内容区提交与操作按钮。
- [x] **Step 1: 为每页增加状态契约**
在目标页面的 `body` 增加 `data-feature-status="pending"` 和与业务相符的中文 `data-feature-message`。例如谱文页面使用“谱文服务正在开发中,当前页面仅保留设计预览。”
- [x] **Step 2: 加载统一状态脚本**
`ApiClient.js` 的页面在其后加载:
```html
<script src="utils/ApiClient.js"></script>
<script src="public/js/pending-pages.js"></script>
```
`profile-video.html``profile-data-reminders.html` 不依赖 `ApiClient.js`,在 `page-effects.js` 前加载状态脚本。
- [x] **Step 3: 收束业务入口跳转**
待开发页面主内容区的业务链接由状态脚本拦截并显示当前页面提示;侧栏保留页面浏览导航。`profile-content.html` 的家族圈动态入口使用 `data-feature-link="available"`,继续跳转到 `profile-feed.html`
- [x] **Step 4: 运行测试确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js`
Expected: 待开发页面均加载状态脚本,家族圈动态页面未被标记。
### Task 3: 更新进度并做静态验证
**Files:**
- Modify: `docs/规划.md`
- Modify: `docs/superpowers/plans/2026-07-11-content-page-state-closure.md`
- Test: `tests/*.test.js`
**Interfaces:**
- Consumes: 全部页面的本地脚本引用。
- Produces: 已完成第二轮进度与可重复的验证结果。
- [x] **Step 1: 更新总规划的第二轮状态**
`docs/规划.md` 中“第二轮:内容与协作页面状态收口”从未完成更新为已完成,并写明家族圈动态未受影响。
- [x] **Step 2: 执行完整测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 所有 Node 测试通过。
- [x] **Step 3: 执行静态检查**
Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,随后运行 `git diff --check`
Expected: 每个本地脚本文件存在,且没有空白格式错误。
@@ -0,0 +1,192 @@
# 家族圈动态接口接入 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:**`PC.openapi2.json` 中已交付的 12 个家族圈动态接口接入既有动态页面,并保持未开发的家谱业务不调用不存在的接口。
**Architecture:** `PC.openapi2.json` 是接口唯一依据;`ApiClient` 提供一一对应的私有请求封装,`feed-pages.js` 只协调页面事件和已定义的接口方法。动态页从 URL 查询参数读取真实 `genealogyId`,不写入示例家谱 ID。
**Tech Stack:** 原生 JavaScript、Axios、Node LTS `node:test`、静态 HTML/CSS。
## Global Constraints
- 所有新增或修改的注释使用清晰中文。
- 不删除现有页面和样式;不接入尚未出现在 `PC.openapi2.json` 的业务。
- 不提交当前工作区:其中含用户已有的未提交修改。
- 动态读取响应目前只有通用 `ListResult`/`PageResult`,页面仅使用 `feedId``feedContent`;后端返回缺少这两个字段时展示明确错误,不猜测字段名。
---
### Task 1: 更新接口边界测试和动态请求测试
**Files:**
- Modify: `tests/api-client-contract.test.js`
- Modify: `tests/pc-scope.test.js`
- Create: `tests/feed-pages.test.js`
**Interfaces:**
- Consumes: `PC.openapi2.json` 的 12 个 `/genealogy/pc/genealogies/{genealogyId}/feeds` 操作。
- Produces: 对客户端路径、HTTP 方法、请求体和页面请求体转换的自动化约束。
- [ ] **Step 1: 写入失败测试**
```js
test('家族圈动态方法使用文档定义的路径和请求体', async () => {
const calls = [];
const client = createClientWithRecorder(calls);
await client.createFeed(900001001, { feedContent: '今天上传一张老照片。' });
await client.feedCommentsPage(900001001, 7001, { pageNum: 1, pageSize: 10 });
assert.deepEqual(calls.map((call) => [call.method, call.url]), [
['post', '/genealogy/pc/genealogies/900001001/feeds'],
['get', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/page']
]);
});
test('动态表单只构造 FamilyFeedBody 字段', () => {
assert.deepEqual(FeedPages.buildFeedBody({
feedContent: '家谱动态', mediaOssIds: '101,102', status: '0'
}), {
feedType: 'text', feedContent: '家谱动态', mediaOssIds: '101,102', status: '0'
});
});
```
- [ ] **Step 2: 运行失败测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 动态方法或 `feed-pages.js` 尚不存在导致失败。
- [ ] **Step 3: 调整范围检查**
```js
assert.match(contract, /\/genealogy\/pc\/genealogies\/\{genealogyId\}\/feeds/);
assert.equal(read('profile-feed.html').includes('src="public/js/feed-pages.js"'), true);
assert.equal(read('profile-feed-edit.html').includes('src="public/js/feed-pages.js"'), true);
```
- [ ] **Step 4: 重新运行测试,确认仍处于预期失败状态**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 仅动态实现相关断言失败。
### Task 2: 增加 12 个家族圈 API 方法
**Files:**
- Modify: `utils/ApiClient.js`
- Modify: `tests/api-client-contract.test.js`
**Interfaces:**
- Consumes: `genealogyId:number|string``feedId:number|string``commentId:number|string`
- Produces: `feeds``feedsPage``feedDetail``createFeed``updateFeed``deleteFeed``likeFeed``unlikeFeed``feedComments``feedCommentsPage``createFeedComment``deleteFeedComment`
- [ ] **Step 1: 保持失败测试不变**
```js
await client.createFeed(900001001, { feedContent: '家谱动态' });
assert.equal(calls[0].url, '/genealogy/pc/genealogies/900001001/feeds');
assert.deepEqual(calls[0].data, { feedContent: '家谱动态' });
```
- [ ] **Step 2: 实现最小路径构造**
```js
function feedPath(genealogyId, suffix) {
return '/genealogy/pc/genealogies/' + encodeURIComponent(genealogyId) + '/feeds' + suffix;
}
createFeed: function (genealogyId, body) {
return request('POST', feedPath(genealogyId, ''), { body: body });
}
```
- [ ] **Step 3: 运行测试,确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 动态客户端路径和请求体断言通过。
### Task 3: 恢复动态页的真实交互
**Files:**
- Create: `public/js/feed-pages.js`
- Modify: `profile-feed.html`
- Modify: `profile-feed-edit.html`
- Modify: `tests/feed-pages.test.js`
**Interfaces:**
- Consumes: `GenealogyApi.defaultClient` 的 12 个动态方法和 URL 中的 `genealogyId`、可选 `feedId`
- Produces: 动态列表、发布/编辑、点赞/取消点赞、评论查看/发布/删除、动态删除事件。
- [ ] **Step 1: 使页面测试失败**
```js
assert.equal(FeedPages.getCurrentGenealogyId('?genealogyId=900001001'), '900001001');
assert.equal(FeedPages.getCurrentGenealogyId(''), '');
assert.equal(FeedPages.getFeedId({ feedId: 7001 }), '7001');
```
- [ ] **Step 2: 实现页面数据转换和事件**
```js
function buildFeedBody(values) {
return {
feedType: 'text',
feedContent: String(values.feedContent || '').trim(),
mediaOssIds: trimOrUndefined(values.mediaOssIds),
status: trimOrUndefined(values.status)
};
}
```
列表只读取 `feedId``feedContent`,并在缺少其中任一字段时显示“动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO”。编辑页以 `feedId` 查询详情并调用 `updateFeed`;列表操作调用点赞、取消点赞、评论、删除接口后重新加载当前页。
- [ ] **Step 3: 挂载脚本和契约字段**
```html
<textarea id="feedContent" name="feedContent" required></textarea>
<script src="public/js/feed-pages.js"></script>
```
移除未在 `FamilyFeedBody` 中定义的“可见范围”提交字段;保持页面布局与既有视觉类名。
- [ ] **Step 4: 运行测试,确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: `feed-pages.test.js` 和已有测试全部通过。
### Task 4: 更新文档来源与最终验证
**Files:**
- Modify: `tests/pc-scope.test.js`
- Modify: `PC.openapi.yaml`
**Interfaces:**
- Consumes: `PC.openapi2.json` 的接口路径。
- Produces: 明确标记旧 YAML 不再为接口源,并使静态检查以 JSON 为准。
- [ ] **Step 1: 写入失败检查**
```js
const contract = JSON.parse(read('PC.openapi2.json'));
assert.ok(contract.paths['/genealogy/pc/genealogies/{genealogyId}/feeds']);
```
- [ ] **Step 2: 标记旧 YAML 失效**
`PC.openapi.yaml` 文件首行写入中文说明:该文件不再作为代码或测试接口源,正式接口源为 `PC.openapi2.json`。不复制或手改 JSON 中未定义的动态响应 DTO。
- [ ] **Step 3: 全量验证**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 全部测试通过,且所有动态页面加载的脚本文件都存在。
## Self-review
- 范围只覆盖最新文档已交付的家族圈动态接口,没有加入家谱创建、成员或世系树接口。
- 所有请求体字段来自 `FamilyFeedBody``FamilyFeedCommentBody`
- 读取响应不使用历史字段兜底,不把未知字段猜成有效数据。
- 任何 URL 缺少 `genealogyId` 的页面都会明确提示,不使用硬编码示例 ID。
@@ -0,0 +1,140 @@
# 家谱基础页面状态收口 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 让尚未获得后端家谱接口的基础页面明确展示开发状态,并阻止表单和操作按钮伪造成功结果。
**Architecture:** 仅在家谱列表、创建/加入、审核、成员管理、邀请、世系和字辈页面加载一个轻量状态脚本。页面通过 `data-feature-status``data-feature-message` 提供文案;脚本统一插入提示条,并拦截页面主内容区的提交与按钮操作。
**Tech Stack:** 原生 JavaScript、静态 HTML/CSS、Node LTS `node:test`
## Global Constraints
- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。
- 不调用 `PC.openapi2.json` 中不存在的家谱、成员、世系或字辈接口。
- 所有新增注释使用中文;不写入测试账号或密码。
- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。
---
### Task 1: 建立失败测试
**Files:**
- Create: `tests/pending-pages.test.js`
- Modify: `tests/pc-scope.test.js`
**Interfaces:**
- Consumes: `PageAvailability.buildBanner(message)``PageAvailability.isPendingPage(body)`
- Produces: 对状态脚本、页面标记和脚本加载的约束。
- [x] **Step 1: 写入失败测试**
```js
test('待开发页面生成明确且转义后的状态提示', () => {
assert.match(PageAvailability.buildBanner('家谱基础服务正在开发中'), /家谱基础服务正在开发中/);
assert.match(PageAvailability.buildBanner('<script>'), /&lt;script&gt;/);
});
test('家谱基础页面显式加载状态脚本', () => {
assert.equal(read('profile-families.html').includes('data-feature-status="pending"'), true);
assert.equal(read('profile-tree.html').includes('src="public/js/pending-pages.js"'), true);
});
```
- [x] **Step 2: 运行失败测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js tests/pc-scope.test.js`
Expected: `pending-pages.js` 不存在且页面未标记导致失败。
### Task 2: 实现统一状态脚本与样式
**Files:**
- Create: `public/js/pending-pages.js`
- Modify: `public/css/profile-module.css`
- Test: `tests/pending-pages.test.js`
**Interfaces:**
- Consumes: `<body data-feature-status="pending" data-feature-message="..."></body>`
- Produces: `PageAvailability.buildBanner(message)``PageAvailability.init()`
- [x] **Step 1: 实现最小状态脚本**
```js
function buildBanner(message) {
return '<section class="feature-status-banner" role="status"><strong>功能开发中</strong><p>' + escapeHtml(message) + '</p></section>';
}
```
脚本只在 `data-feature-status="pending"` 页面运行,将提示条放在 `.module-main` 首位;拦截该页面 `main` 区域内的表单提交和非重置按钮,显示同一提示文案。
- [x] **Step 2: 增加复用样式**
```css
.feature-status-banner {
border: 1px solid rgba(196, 146, 69, .38);
border-radius: 16px;
background: #fff8e8;
}
```
- [x] **Step 3: 运行测试确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js tests/pc-scope.test.js`
Expected: 新增状态脚本和页面标记断言通过。
### Task 3: 标记家谱基础页面
**Files:**
- Modify: `profile-families.html`
- Modify: `profile-create-family.html`
- Modify: `profile-join-family.html`
- Modify: `profile-join-review.html`
- Modify: `profile-family-admin.html`
- Modify: `profile-invite.html`
- Modify: `profile-tree.html`
- Modify: `profile-generation.html`
**Interfaces:**
- Consumes: `pending-pages.js` 和每页 `data-feature-message`
- Produces: 明确状态提示,不提交文档外请求。
- [x] **Step 1: 在 body 标记状态**
```html
<body class="page-profile-module" data-feature-status="pending" data-feature-message="家谱成员与权限服务正在开发中,当前页面仅保留设计预览。">
```
- [x] **Step 2: 在 ApiClient 后加载状态脚本**
```html
<script src="utils/ApiClient.js"></script>
<script src="public/js/pending-pages.js"></script>
<script src="public/js/page-effects.js"></script>
```
- [x] **Step 3: 运行完整测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 全部测试通过。
### Task 4: 静态验证
**Files:**
- Test: `tests/pc-scope.test.js`
**Interfaces:**
- Consumes: 8 个已标记页面。
- Produces: 所有待开发页面有状态文案,所有页面脚本路径存在。
- [x] **Step 1: 执行脚本引用检查**
Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,确认每个本地脚本文件存在。
- [x] **Step 2: 执行格式检查**
Run: `git diff --check`
Expected: 无缺失脚本和空白格式错误。
@@ -1,278 +0,0 @@
# PC Endpoint Page Coverage Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make every callable operation in `genealogy-pc-openapi.yaml` traceable to a real PC page flow, an automated regression test, and a manual acceptance step.
**Architecture:** `utils/ApiClient.js` remains the sole network contract owner. Page modules in `public/js` own input validation, loading and success/error state; `tests` prove their request and navigation behavior. The coverage matrix distinguishes a complete user flow from an endpoint that is documented but lacks the contract required to expose it safely.
**Tech Stack:** Static HTML, browser JavaScript, Axios, Layui, Node `assert` tests.
## Global Constraints
- Runtime API base URL is owned only by `config.js`: `http://182.61.18.23:8080`.
- Only PC/H5 routes and scenes defined by `genealogy-pc-openapi.yaml` may be requested.
- Registration never persists a token; password and SMS login persist the returned token and navigate to `profile.html`.
- Logout, unauthenticated profile access and account deactivation navigate to `index.html`.
- Every behavior change starts with a failing Node test and ends with focused plus full test verification.
- No page invents a business table name, business ID, CAPTCHA scene or legacy captcha submission field that is not in the PC contract.
## Endpoint Matrix
| Operation | Page owner | Automated test | Manual acceptance order | Status target |
| --- | --- | --- | --- | --- |
| `GET /captcha/require` | `CaptchaPages` on auth forms | `tests/captcha-pages.test.js` | 1-4 | Complete |
| `POST /captcha/challenge` | `CaptchaPages` on auth forms | `tests/captcha-pages.test.js` | 1-4 | Complete |
| `POST /captcha/verify` | `CaptchaPages` on auth forms | `tests/captcha-pages.test.js` | 1-4 | Complete |
| `GET /auth/code` | no safe consumer | `tests/pc-endpoint-coverage.test.js` | blocked | Backend contract required |
| Register, password login, SMS login, SMS code | `register.html`, `login.html` | `tests/auth-pages.test.js`, `tests/api-client.test.js` | 1-3 | Complete |
| Profile read/update | `profile.html`, `profile-data.html` | `tests/profile-pages.test.js` | 4-5 | Complete |
| Password reset | `forgot-password.html` | `tests/auth-pages.test.js` | 6 | Complete |
| Password change, logout, account deactivation | `profile-security.html` | `tests/security-pages.test.js` | 7-9 | Complete |
| Phone change | `profile-security.html` | `tests/security-pages.test.js` | blocked | PC/H5 phone SMS scene required |
| Single upload | profile avatar and existing upload fields | `tests/upload-pages.test.js` | 10 | Complete |
| Resumable init/chunk/complete | same file input for large files | `tests/upload-pages.test.js`, `tests/api-client.test.js` | 11 | Complete |
| File reference bind/release | no safe business entity contract | `tests/pc-endpoint-coverage.test.js` | blocked | PC business table and ID contract required |
| Region children/path/search/detail | profile address picker and region search control | `tests/region-pages.test.js`, `tests/profile-pages.test.js` | 12 | Complete |
## Task 1: Enforce Endpoint Coverage
**Files:**
- Create: `tests/pc-endpoint-coverage.test.js`
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
**Interfaces:**
- Consumes: the 25 operations declared under `paths` in `genealogy-pc-openapi.yaml`.
- Produces: a test-owned `ENDPOINTS` list that assigns every operation one of `complete` or `blocked` with a page/test owner.
- [ ] **Step 1: Write the failing coverage test**
```js
assert.deepStrictEqual(missingOperations, []);
assert.deepStrictEqual(unownedOperations, []);
assert.deepStrictEqual(unsupportedBlockedOperations, []);
```
- [ ] **Step 2: Run the test to verify it fails**
Run: `node tests\pc-endpoint-coverage.test.js`
Expected: failure listing the endpoint operations without a page/test/blocker owner.
- [ ] **Step 3: Add the explicit 25-operation matrix**
```js
const ENDPOINTS = {
'GET /captcha/require': { page: 'auth', test: 'captcha-pages', status: 'complete' },
'GET /auth/code': { status: 'blocked', reason: 'legacy captcha has no PC request field' }
};
```
- [ ] **Step 4: Run the test to verify it passes**
Run: `node tests\pc-endpoint-coverage.test.js`
Expected: `pc-endpoint-coverage tests passed`.
## Task 2: Complete and Test the Auth Journey
**Files:**
- Modify: `public/js/auth-pages.js`
- Modify: `public/js/profile-pages.js`
- Modify: `public/js/security-pages.js`
- Modify: `tests/auth-pages.test.js`
- Modify: `tests/profile-pages.test.js`
- Modify: `tests/security-pages.test.js`
**Interfaces:**
- Consumes: `GenealogyApi.login`, `loginBySms`, `register`, `sendSmsCode`, `resetPassword`, `getProfile`, `updateProfile`, `changePassword`, `deactivateAccount`, and `logout`.
- Produces: consistent form validation, token state and redirect behavior for the acceptance steps 1-9.
- [ ] **Step 1: Write failing redirect and request tests**
```js
assert.strictEqual(loginRoot.location.href, 'profile.html');
assert.strictEqual(registerRoot.location.href, 'login.html');
assert.strictEqual(logoutRoot.location.href, 'index.html');
```
- [ ] **Step 2: Run focused tests to verify failure**
Run: `node tests\auth-pages.test.js; node tests\profile-pages.test.js; node tests\security-pages.test.js`
Expected: a failure naming the missing flow or redirect.
- [ ] **Step 3: Implement only the missing flow branch in its page module**
```js
await api.loginBySms(buildSmsLoginBody(values));
root.location.href = 'profile.html';
```
Use the corresponding documented endpoint method for each form; do not create a second token store or a page-local route string owner.
- [ ] **Step 4: Run focused tests**
Run: `node tests\auth-pages.test.js; node tests\profile-pages.test.js; node tests\security-pages.test.js`
Expected: all three scripts print their pass messages.
## Task 3: Make Region Search, Path and Detail Usable
**Files:**
- Modify: `profile-data.html`
- Modify: `public/js/region-pages.js`
- Modify: `tests/region-pages.test.js`
**Interfaces:**
- Consumes: `regionChildren(parentCode)`, `regionSearch({ keyword })`, `regionPath(regionCode)`, and `regionDetail(regionCode)`.
- Produces: a profile-address picker that can search by name/code, select a result, resolve its path and write province/city/district codes before profile save.
- [ ] **Step 1: Write a failing result-normalization and selected-path test**
```js
assert.deepStrictEqual(RegionPages.buildRegionSelection(result), {
provinceCode: '510000',
cityCode: '510100',
districtCode: '510104'
});
```
- [ ] **Step 2: Run the focused test to verify failure**
Run: `node tests\region-pages.test.js`
Expected: failure because `buildRegionSelection` is not exported.
- [ ] **Step 3: Add the profile search control and selection behavior**
```js
var path = await api.regionPath(regionCode);
var selection = buildRegionSelection(path);
applyRegionSelection(picker, selection);
await api.regionDetail(regionCode);
```
- [ ] **Step 4: Run focused tests**
Run: `node tests\region-pages.test.js; node tests\profile-pages.test.js`
Expected: both scripts print their pass messages.
## Task 4: Complete Single and Resumable File Upload
**Files:**
- Modify: `public/js/upload-pages.js`
- Modify: `utils/ApiClient.js`
- Modify: `tests/upload-pages.test.js`
- Modify: `tests/api-client.test.js`
**Interfaces:**
- Consumes: `uploadFile(file)`, `initResumableUpload(body)`, `uploadChunk(body)`, `completeResumableUpload(body)`.
- Produces: `uploadFileForPage(file, options)` which uses single upload for files at or below the configured threshold and the init/chunk/complete sequence for larger files.
- [ ] **Step 1: Write failing upload-strategy tests**
```js
assert.strictEqual(UploadPages.getUploadMode({ size: 1024 }, 4194304), 'single');
assert.strictEqual(UploadPages.getUploadMode({ size: 4194305 }, 4194304), 'resumable');
```
- [ ] **Step 2: Run focused tests to verify failure**
Run: `node tests\upload-pages.test.js; node tests\api-client.test.js`
Expected: failure because `getUploadMode` does not exist.
- [ ] **Step 3: Implement sequential resumable upload**
```js
var init = await api.initResumableUpload(initBody);
for (index = 0; index < chunks.length; index += 1) {
await api.uploadChunk({ uploadId: init.uploadId, chunkIndex: index, chunkMd5: chunkMd5, file: chunks[index] });
}
return api.completeResumableUpload({ uploadId: init.uploadId, fileMd5: fileMd5, fileSize: file.size });
```
- [ ] **Step 4: Run focused tests**
Run: `node tests\upload-pages.test.js; node tests\api-client.test.js`
Expected: both scripts print their pass messages.
## Task 5: Record and Enforce Backend-Contract Blocks
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- Modify: `tests/pc-endpoint-coverage.test.js`
**Interfaces:**
- Consumes: the legacy captcha response, phone SMS scene requirements, and file-reference required business fields from the PC YAML.
- Produces: three named blocked operations with an exact missing backend input and no speculative page request.
- [ ] **Step 1: Assert exact blocker reasons**
```js
assert.strictEqual(ENDPOINTS['GET /auth/code'].reason, 'legacy captcha has no PC request field');
assert.strictEqual(ENDPOINTS['PUT /genealogy/pc/auth/phone'].reason, 'PC/H5 phone SMS scene is not documented');
assert.strictEqual(ENDPOINTS['POST /genealogy/pc/files/reference'].reason, 'PC business table and ID contract is not documented');
```
- [ ] **Step 2: Run the test to verify failure**
Run: `node tests\pc-endpoint-coverage.test.js`
Expected: failure until all three reasons are explicit.
- [ ] **Step 3: Update the tracker without adding a fake request**
```md
| Blocked operation | Missing backend contract | Page behavior |
| --- | --- | --- |
| `GET /auth/code` | legacy code submission field | no page action |
```
- [ ] **Step 4: Run the test to verify it passes**
Run: `node tests\pc-endpoint-coverage.test.js`
Expected: `pc-endpoint-coverage tests passed`.
## Task 6: Final Verification and User Test Script
**Files:**
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- [ ] **Step 1: Run syntax checks**
Run: `node --check utils\ApiClient.js`
Run: `node --check public\js\auth-pages.js`
Run: `node --check public\js\region-pages.js`
Run: `node --check public\js\upload-pages.js`
Expected: each command exits with code 0.
- [ ] **Step 2: Run the complete Node suite**
Run: `Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { & node $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } }`
Expected: every test file prints its pass message.
- [ ] **Step 3: Check the diff**
Run: `git diff --check`
Expected: exit code 0; CRLF conversion notices are informational only.
- [ ] **Step 4: Record manual acceptance steps**
Record the precise browser order: register, password login, SMS login, profile display/edit, reset password, password change, logout, account deactivation, address search, single upload, resumable upload, then each documented backend blocker.
## Self-Review
- Coverage: Tasks 1-5 assign every documented operation an implementation owner or an explicit backend block; Task 6 provides the requested sequential test script.
- Contract discipline: `ApiClient` remains the only route owner; page modules do not create undocumented paths, scene codes, business tables or identifiers.
- Scope: pages without callable PC paths remain outside implementation; their schemas are not treated as endpoints.
@@ -0,0 +1,139 @@
# 官网静态页面补齐 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 补齐官网新闻、法律文档入口、通用状态与工单页面,使无接口的线上服务不再伪装为可用功能。
**Architecture:** 新增静态 HTML 页面共用 `public/css/site-info.css` 和现有官网头尾布局。新闻页只链接现有静态文章与公告详情;法律页面明确标明待法务确认;工单页面复用 `pending-pages.js` 阻止无接口提交。
**Tech Stack:** 静态 HTML、原生 CSS、原生 JavaScript、Node LTS `node:test`
## Global Constraints
- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。
- 不新增 `PC.openapi2.json` 外的接口调用,不伪造新闻接口、法律协议、工单列表或工单详情数据。
- 法律页面只能显示“待法务确认”说明,发布前必须替换为主体与法务确认的正式文本。
- 所有新增注释使用中文;不写入测试账号或密码。
- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。
---
### Task 1: 建立静态页面约束测试
**Files:**
- Create: `tests/public-static-pages.test.js`
**Interfaces:**
- Consumes: 静态页面文件、`data-feature-status="pending"` 和本地 `href`
- Produces: 页面存在、关键说明、状态脚本与本地链接的可重复校验。
- [x] **Step 1: 写入失败测试**
测试必须断言存在以下文件:
```js
const staticPages = [
'news.html', 'privacy.html', 'children-privacy.html', 'terms.html',
'not-found.html', 'forbidden.html', 'maintenance.html',
'my-tickets.html', 'ticket-detail.html'
];
```
同时断言:`news.html` 含有新闻分类锚点并链接 `article-detail.html``notice-detail.html`;三个法律页面含“待法务确认”;`submit-ticket.html``my-tickets.html``ticket-detail.html` 有待开发状态和 `pending-pages.js`;新页面中的本地 `href` 全部存在。
- [x] **Step 2: 运行失败测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/public-static-pages.test.js`
Expected: 新页面不存在导致测试失败。
### Task 2: 建立通用静态页面外观与新闻、法律入口
**Files:**
- Create: `public/css/site-info.css`
- Create: `news.html`
- Create: `privacy.html`
- Create: `children-privacy.html`
- Create: `terms.html`
- Modify: `index.html`
**Interfaces:**
- Consumes: 现有 `public/css/public.css``site-header``footer``container``btn``card` 样式。
- Produces: 新闻分类和详情跳转,以及能从首页底部访问的法律入口。
- [x] **Step 1: 创建复用样式**
`site-info.css` 提供 `.site-info-hero``.site-info-layout``.site-info-card``.site-info-status``.site-info-links`,仅处理本轮新增页面的排版和窄屏适配。
- [x] **Step 2: 创建新闻页面**
`news.html` 必须有“平台公告”“家谱文化”分类锚点,并将两条静态内容分别链接到 `notice-detail.html``article-detail.html`
- [x] **Step 3: 创建法律页面并连接首页**
三个法律页面均写明“待法务确认”,并互相提供跳转链接。首页 `footer-legal` 增加三个页面链接。
### Task 3: 建立状态与工单边界
**Files:**
- Create: `not-found.html`
- Create: `forbidden.html`
- Create: `maintenance.html`
- Create: `my-tickets.html`
- Create: `ticket-detail.html`
- Modify: `submit-ticket.html`
**Interfaces:**
- Consumes: `PageAvailability.init()` 读取的待开发页面属性。
- Produces: 三种无接口提交的工单状态,和可返回首页、帮助中心的静态状态页。
- [x] **Step 1: 创建三个通用状态页**
三个页面分别显示“页面不存在”“无访问权限”“系统维护中”,并提供 `index.html` 返回入口。
- [x] **Step 2: 创建工单列表与详情状态页**
`my-tickets.html``ticket-detail.html` 均使用:
```html
<body data-feature-status="pending" data-feature-message="在线工单查询服务正在开发中,当前页面仅保留设计预览。">
```
并在 `public/js/pending-pages.js` 后仅放行帮助中心链接。
- [x] **Step 3: 收束提交工单表单**
`submit-ticket.html` 增加:
```html
<body class="page-submit-ticket" data-feature-status="pending" data-feature-message="在线提交工单服务正在开发中,请先通过帮助中心了解常见问题。">
```
`ApiClient.js` 后加载状态脚本,并为“返回帮助中心”链接添加 `data-feature-link="available"`
### Task 4: 回写进度与验证
**Files:**
- Modify: `docs/规划.md`
- Modify: `docs/superpowers/plans/2026-07-11-public-static-pages.md`
- Test: `tests/*.test.js`
**Interfaces:**
- Consumes: 新增静态页面和全部 HTML 本地脚本引用。
- Produces: 第四轮进度记录和验证结果。
- [x] **Step 1: 写入第四轮完成状态**
`docs/规划.md` 的“实施进度”中增加静态官网页面补齐已完成,并注明法律文本需法务替换。
- [x] **Step 2: 执行完整测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 所有 Node 测试通过。
- [x] **Step 3: 执行静态检查**
Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,随后运行 `git diff --check`
Expected: 每个本地脚本文件存在,且没有空白格式错误。
@@ -0,0 +1,112 @@
# 剩余用户 PC 业务入口收口 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 让剩余未有正式接口支持的用户 PC 业务入口显示明确状态,避免跳转到看似可用的分享、会员或家谱管理流程。
**Architecture:** 复用 `public/js/pending-pages.js` 的状态契约和链接收束规则。仅标记 `profile-family-home.html``profile-share.html``profile-services.html`;账号主页、个人资料、安全设置和家族圈动态继续使用现有真实功能。
**Tech Stack:** 原生 JavaScript、静态 HTML/CSS、Node LTS `node:test`
## Global Constraints
- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。
- `PC.openapi2.json` 中不存在家谱主页、邀请奖励和会员订单的正式接口,不得伪造请求或静态数据为真实业务。
- `profile-family-home.html` 的家族圈动态入口继续跳转到 `profile-feed.html`
- 所有新增注释使用中文;不写入测试账号或密码。
- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。
---
### Task 1: 扩展入口状态测试
**Files:**
- Modify: `tests/pending-pages.test.js`
**Interfaces:**
- Consumes: 目标页面的 `data-feature-status="pending"`、状态脚本引用与 `data-feature-link="available"`
- Produces: 剩余业务入口的静态约束,及真实账户页面不被误标记的约束。
- [x] **Step 1: 写入失败测试**
新增以下数组并加入现有待开发页面检查:
```js
const residualPendingPages = [
'profile-family-home.html',
'profile-share.html',
'profile-services.html'
];
```
新增断言:`profile-family-home.html``profile-feed.html` 链接必须带有 `data-feature-link="available"``profile.html``profile-data.html``profile-security.html` 不得含待开发状态标记。
- [x] **Step 2: 运行失败测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js`
Expected: 三个剩余入口尚未标记,测试失败。
### Task 2: 标记剩余业务入口
**Files:**
- Modify: `profile-family-home.html`
- Modify: `profile-share.html`
- Modify: `profile-services.html`
- Test: `tests/pending-pages.test.js`
**Interfaces:**
- Consumes: `PageAvailability.init()` 读取的页面状态属性,和 `PageAvailability.shouldBlockLink(link)` 的可用入口约定。
- Produces: 状态提示、业务链接拦截,以及唯一保留的动态入口。
- [x] **Step 1: 标记页面状态**
分别在三个 `body` 中增加 `data-feature-status="pending"` 和对应中文提示:家谱主页使用“家谱概览与管理服务正在开发中,当前页面仅保留设计预览。”;应用分享使用“应用分享与邀请奖励服务正在开发中,当前页面仅保留设计预览。”;帮助与服务使用“会员与在线服务正在开发中,当前页面仅保留设计预览。”
- [x] **Step 2: 加载统一状态脚本并放行动态入口**
每页都在 `utils/ApiClient.js` 后增加:
```html
<script src="utils/ApiClient.js"></script>
<script src="public/js/pending-pages.js"></script>
```
`profile-family-home.html` 中将动态链接改为:
```html
<a class="btn primary magnetic" href="profile-feed.html" data-feature-link="available">发布动态</a>
```
- [x] **Step 3: 运行测试确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js`
Expected: 全部状态页面和真实账户页面断言通过。
### Task 3: 更新进度与验证
**Files:**
- Modify: `docs/规划.md`
- Modify: `docs/superpowers/plans/2026-07-11-residual-pc-entry-closure.md`
- Test: `tests/*.test.js`
**Interfaces:**
- Consumes: 全部 HTML 的本地脚本引用。
- Produces: 已完成第三轮进度和可重复验证记录。
- [x] **Step 1: 写入第三轮完成状态**
`docs/规划.md` 的“实施进度”中增加已完成第三轮,说明三类剩余 PC 业务入口已收口,家族圈动态未受影响。
- [x] **Step 2: 执行完整测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 所有 Node 测试通过。
- [x] **Step 3: 执行静态检查**
Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,随后运行 `git diff --check`
Expected: 每个本地脚本文件存在,且没有空白格式错误。
@@ -1,29 +0,0 @@
# Registration Token State Design
## Status
Pending written-spec review.
## Goal
Registration creates an account but never creates or retains a browser login state. Only a successful password login or SMS login may persist an authentication token.
## Contract Owner
`utils/ApiClient.js` owns browser token persistence. Pages invoke API methods and redirect according to their existing form configuration; they do not read, write, or clear the token directly.
## Required Behavior
1. `login()` and `loginBySms()` persist the token returned by their successful responses under `genealogy_auth_token`.
2. `register()` posts the existing registration request unchanged and returns its response unchanged.
3. After a successful `register()` response, `register()` clears `genealogy_auth_token`. This applies even when the response includes `token`, `accessToken`, or `tokenValue`.
4. If `register()` fails, token storage is not changed.
5. `register.html` continues redirecting to `login.html` after `register()` resolves.
## Scope
This change does not add a profile-page login guard, 401 redirect handling, or logout UI. Those are separate authentication-lifecycle steps that must consume the same `ApiClient` token contract.
## Verification
The API-client tests must prove that successful registration removes a previously stored token and does not store a token returned by registration. They must also retain coverage showing password and SMS login still store their returned tokens.
@@ -1,47 +0,0 @@
# Auth To Profile Flow Design
## Status
Approved for implementation.
## Goal
Complete the PC journey from registration through login and personal-profile retrieval, with one token owner, deterministic redirects, and a single recorded API base address.
## Endpoint Configuration
`config.js` is the sole runtime owner of the API base address. Both development and production currently use `http://182.61.18.23:8080`. `tests/config.test.js` must assert that value. The PC integration tracker must record the change on 2026-07-11 and label prior test-domain references as historical rather than current configuration.
## Token Contract
`utils/ApiClient.js` owns token persistence under `genealogy_auth_token`.
1. Successful password login and SMS login must receive a nonempty response token, persist it, and then allow navigation to `profile.html`. The current backend response uses `data.access_token`; the client also accepts `token`, `accessToken`, and `tokenValue` for declared compatibility.
2. A login response without a token is an authentication failure. The login page remains in place and no profile navigation occurs.
3. Successful registration removes any existing token and navigates to `login.html`. Registration never persists a response token.
4. A failed registration leaves existing token storage unchanged.
5. `logout()` clears token storage in a `finally` path, so a failed or expired logout request cannot leave a local session behind.
## Page Flow
1. Registration validates fields, obtains `WEB_H5_REGISTER` captcha proof, posts `/genealogy/pc/auth/register`, clears token through `ApiClient.register()`, and redirects to `login.html`.
2. Password and SMS login validate fields, obtain `WEB_H5_LOGIN` captcha proof, post their respective login endpoint, persist the returned token through `ApiClient`, and redirect to `profile.html`.
3. `profile.html` and `profile-data.html` check for a token before requesting `/genealogy/pc/auth/profile`. Without one, they redirect to `index.html` without making the profile request.
4. A profile-read or profile-update error with HTTP status `401` or business code `401` clears token storage and redirects to `index.html`. Other errors remain on the current page and show the existing error message.
5. Confirmed logout calls `/genealogy/pc/auth/logout`, clears token regardless of the request outcome, and redirects to `index.html`.
## Ownership
- `config.js`: API base address.
- `utils/ApiClient.js`: login-token validation, persistence, clearing, and logout cleanup.
- `public/js/profile-pages.js`: profile-page token guard and unauthorized redirect.
- `public/js/profile-common.js`: confirmed logout action and redirect.
- `docs/pc-api-page-integration-tracker-2026-07-09.md`: current address record and end-to-end authentication status.
## Verification
Tests must prove the configured address, registration state, both login token paths, missing-token login rejection, logout cleanup after both success and failure, and profile unauthorized classification. Focused configuration, API-client, profile-page, and authentication tests must pass before the full Node test suite is run.
## Scope
This flow covers registration, password login, SMS login, profile retrieval and update on the two pages that load `/genealogy/pc/auth/profile`, and the existing profile logout action. Password reset, phone change, account deactivation, and profile pages without profile API loading remain outside this change.