feat(admin): replicate reference post-page settings page 1:1

- rewrite post_list.page schema (JS+PHP 2.0.0) with the reference field set:
  three cover types with option fieldsets (image 20-200% / slide CFS_Module::slide
  full config with nested dependsOn / video 0-200%), hide-time, p_indent,
  breadcrumbs (home-text as switcher), prevnext, authordesc, maxheight slider,
  yiyan block, copyright, button toptext, front/after content, related posts
- migrate share_s to share page basic group and post_rewards_* to users
  features rewards group (runtime keys unchanged)
- remap get_post_page_settings() to reference keys, normalize slide cover
  option, hardcode non-reference runtime flags (reading progress, favorites,
  author card), drop dual-path legacy aliases
- wire slide option into aksrt_render_slider (effect/direction/spaceBetween/
  speed/interval/loop/autoplay/nav/pagination, scale vs fixed-height modes),
  clamp image scale 20-200 and video scale 0-200 with 16:9 fallback,
  wire p_indent container class + CSS, resync frontend-tags fallback and
  FacadeBulk fallback, remap PostMetaBox cover keys
- add post-page reference contract test; update stale cover-height and
  user-features contract tests

Validation: node --test 464/464 pass, audit-admin-contracts errors=0,
vite build ok, build-public-assets ok, php -l clean.
This commit is contained in:
2026-09-24 16:13:00 +08:00
parent a8060fa71d
commit 66f8bb933d
17 changed files with 797 additions and 267 deletions
@@ -14,11 +14,18 @@ const expectedFields = [
'user_cover_upload_s',
'user_cover_img',
'user_desc_std',
// 打赏组(参考主题 8977 行:post_rewards_* 迁移至用户功能页)。
'post_rewards_s',
'post_rewards_text',
'post_rewards_amounts',
'post_rewards_custom',
'post_rewards_min',
'post_rewards_max',
];
const externalSources = ['', 'gravatar', 'weavatar', 'cravatar', 'geekzu', 'loli', 'sep', 'v2ex'];
test('user features schemas expose exactly the six canonical fields', () => {
test('user features schemas expose the canonical fields plus the migrated rewards group', () => {
const js = read('assets/admin/src/schema/sections/users/features.js');
const phpAll = read('src/Admin/Schema/SchemaUsers.php');
const php = phpAll.slice(phpAll.indexOf("'features' => ["), phpAll.indexOf("'verify' => ["));
@@ -36,6 +43,11 @@ test('user features schemas expose exactly the six canonical fields', () => {
assert.match(js, /user_desc_std:\s*'这家伙很懒,什么都没有写\.\.\.'/);
assert.match(php, /avatar_default_img[^\n]+get_theme_file_uri\('assets\/img\/avatar-default\.png'\)/);
assert.match(php, /user_cover_img[^\n]+get_theme_file_uri\('assets\/img\/user_t\.jpg'\)/);
// 打赏组:开关默认开(对齐参考主题),依赖收敛于 post_rewards_s。
assert.match(js, /post_rewards_s:\s*true/);
assert.match(js, /post_rewards_text:\s*'赞赏'/);
assert.match(php, /\['key' => 'rewards', 'title' => '打赏', 'fields' => \[/);
assert.match(php, /'post_rewards_s', 'label' => '用户打赏功能', 'type' => 'switcher', 'default' => true/);
const jsSources = [...js.matchAll(/\{ label: '[^']+', value: '([^']*)' \}/g)].map((match) => match[1]);
const phpSources = [...php.matchAll(/\['label' => '[^']+', 'value' => '([^']*)'\]/g)].map((match) => match[1]);