diff --git a/assets/admin/src/schema/sections/post-list/page.js b/assets/admin/src/schema/sections/post-list/page.js index 278629ef..35ebbc67 100644 --- a/assets/admin/src/schema/sections/post-list/page.js +++ b/assets/admin/src/schema/sections/post-list/page.js @@ -1,144 +1,192 @@ export const pageSchema = { key: 'post_list.page', title: '文章页面', - icon: 'fa fas fa-file-alt', - description: '配置单篇文章页面的导航、封面、版权区与互动功能。', + icon: 'fa fas fa-bookmark', + description: '配置文章页面封面、面包屑导航、版权声明、插入内容与相关文章版块。', defaults: { - post_reading_progress: true, + article_image_cover: true, + article_image_cover_option: { scale: 35 }, + article_slide_cover: true, + article_slide_cover_option: { + direction: 'horizontal', + loop: true, + button: false, + pagination: true, + effect: 'slide', + scale_height: true, + scale: 35, + auto_height: false, + max_height: 500, + min_height: 180, + pc_height: 380, + m_height: 180, + spacebetween: 15, + speed: 0, + autoplay: true, + interval: 4, + }, + article_video_cover: true, + article_video_cover_option: { scale: 0 }, + post_single_hide_time_s: false, + post_p_indent_s: false, breadcrumbs_single_s: true, - breadcrumbs_home_text: '首页', + breadcrumbs_home_text: true, post_prevnext_s: true, post_authordesc_s: true, - post_single_hide_time_s: false, + article_maxheight_kg: false, + article_maxheight: 1000, + yiyan_single_content_header: false, + yiyan_single_content_footer: false, + yiyan_single_box: false, + post_copyright_s: true, + post_copyright: '文章版权归作者所有,未经允许请勿转载。', + post_button_toptext: '喜欢就支持一下吧', + post_front_content: '', + post_after_content: '', post_related_s: true, post_related_type: 'img', related_title: '相关推荐', post_related_n: 6, post_related_orderby: 'views', - breadcrumbs_single_text: true, - post_copyright_enabled: true, - post_copyright_text: '', - share_s: true, - post_favorite_enabled: true, - post_front_content: '', - post_after_content: '', - post_yiyan_header: false, - post_yiyan_footer: false, - post_yiyan_box: false, - post_article_image_cover: true, - post_article_image_cover_scale: 35, - post_article_slide_cover: true, - post_article_video_cover: true, - post_article_video_cover_scale: 56, - item_heading_bold: false, - post_button_toptext: '喜欢就支持一下吧', - post_rewards_s: false, - post_rewards_text: '赞赏', - post_author_card_limit: 6, - post_author_card_orderby: 'views', - post_author_card_style: 'card', - article_maxheight_kg: false, - article_maxheight: 1000, }, groups: [ { - key: 'navigation', - title: '文章导航', - icon: 'fa fas fa-compass', + key: 'post_page', + title: '文章页面', + icon: 'fa fas fa-bookmark', fields: [ - { key: 'post_reading_progress', label: '阅读进度条', type: 'switch', default: true }, + { key: 'article_image_cover', label: '图片封面', type: 'switch', default: true, description: '允许设置文章页顶部显示封面图' }, + { + key: 'article_image_cover_option', + label: ' ', + type: 'fieldset', + default: { scale: 35 }, + dependsOn: { key: 'article_image_cover', value: true }, + description: '图片封面配置', + fields: [ + { key: 'scale', label: '图片长宽比例', type: 'number', default: 35, min: 20, max: 200, step: 5, unit: '%' }, + ], + }, + { key: 'article_slide_cover', label: '幻灯片封面', type: 'switch', default: true, description: '允许设置幻灯片为文章封面' }, + { + key: 'article_slide_cover_option', + label: ' ', + type: 'fieldset', + default: { + direction: 'horizontal', + loop: true, + button: false, + pagination: true, + effect: 'slide', + scale_height: true, + scale: 35, + auto_height: false, + max_height: 500, + min_height: 180, + pc_height: 380, + m_height: 180, + spacebetween: 15, + speed: 0, + autoplay: true, + interval: 4, + }, + dependsOn: { key: 'article_slide_cover', value: true }, + fields: [ + { key: 'direction', label: '幻灯片方向', type: 'radio', default: 'horizontal', options: [{ label: '左右切换', value: 'horizontal' }, { label: '上下切换', value: 'vertical' }] }, + { key: 'loop', label: '循环切换', type: 'switch', default: true }, + { key: 'button', label: '显示翻页按钮', type: 'switch', default: false }, + { key: 'pagination', label: '显示指示器', type: 'switch', default: true }, + { + key: 'effect', + label: '切换动画', + type: 'select', + default: 'slide', + options: [ + { label: '滑动', value: 'slide' }, + { label: '淡出淡入', value: 'fade' }, + { label: '3D方块', value: 'cube' }, + { label: '3D滑入', value: 'coverflow' }, + { label: '3D翻转', value: 'flip' }, + ], + }, + { key: 'scale_height', label: '按比例自动高度', type: 'switch', default: true, dependsOn: { key: 'direction', not: 'vertical' } }, + { key: 'scale', label: '长宽比例', type: 'number', default: 35, min: 10, max: 300, step: 5, unit: '%', dependsOn: { all: [{ key: 'direction', not: 'vertical' }, { key: 'scale_height', value: true }] } }, + { key: 'auto_height', label: '自动高度', type: 'switch', default: false, dependsOn: { all: [{ key: 'direction', not: 'vertical' }, { key: 'scale_height', value: false }] } }, + { key: 'max_height', label: '最大高度', type: 'number', default: 500, min: 120, max: 800, step: 20, unit: 'PX', dependsOn: { all: [{ key: 'auto_height', value: true }, { key: 'direction', not: 'vertical' }, { key: 'scale_height', value: false }] } }, + { key: 'min_height', label: '最小高度', type: 'number', default: 180, min: 100, max: 500, step: 20, unit: 'PX', dependsOn: { all: [{ key: 'auto_height', value: true }, { key: 'direction', not: 'vertical' }, { key: 'scale_height', value: false }] } }, + { key: 'pc_height', label: '电脑端高度', type: 'number', default: 380, min: 120, max: 800, step: 20, unit: 'PX' }, + { key: 'm_height', label: '移动端高度', type: 'number', default: 180, min: 100, max: 500, step: 20, unit: 'PX' }, + { key: 'spacebetween', label: '幻灯片间距', type: 'number', default: 15, min: 0, max: 500, step: 5, unit: 'PX' }, + { key: 'speed', label: '切换速度', type: 'number', default: 0, min: 0, max: 3000, step: 100, unit: '毫秒', description: '切换过程的时间(越小越快),设置为0则为自动模式' }, + { key: 'autoplay', label: '自动播放', type: 'switch', default: true }, + { key: 'interval', label: '停顿时间', type: 'number', default: 4, min: 0, max: 20, step: 1, unit: '秒', dependsOn: { key: 'autoplay', value: true }, description: '自动切换的时间间隔(越小越快)' }, + ], + }, + { key: 'article_video_cover', label: '视频封面', type: 'switch', default: true, description: '允许设置视频为文章封面' }, + { + key: 'article_video_cover_option', + label: ' ', + type: 'fieldset', + default: { scale: 0 }, + dependsOn: { key: 'article_video_cover', value: true }, + description: '视频封面配置 【查看官方封面配置教程】', + fields: [ + { key: 'scale', label: '固定长宽比例', type: 'number', default: 0, min: 0, max: 200, step: 5, unit: '%', description: '为0则不固定长宽比例' }, + ], + }, + { key: 'post_single_hide_time_s', label: '隐藏时间', type: 'switch', default: false, description: '隐藏文章的更新及发布时间显示' }, + { key: 'post_p_indent_s', label: '内容段落缩进', type: 'switch', default: false, description: '开启后文章内容每一个段落首行将向右偏移2个文字距离' }, { key: 'breadcrumbs_single_s', label: '面包屑导航', type: 'switch', default: true }, - { key: 'breadcrumbs_home_text', label: '面包屑首页文字', type: 'input', default: '首页', dependsOn: { key: 'breadcrumbs_single_s', value: true } }, - { key: 'breadcrumbs_single_text', label: '末项显示「正文」', type: 'switch', default: true, dependsOn: { key: 'breadcrumbs_single_s', value: true }, help: '关闭后末项显示文章标题' }, - { key: 'post_prevnext_s', label: '上一篇/下一篇', type: 'switch', default: true }, - { key: 'post_authordesc_s', label: '作者简介', type: 'switch', default: true }, - { key: 'post_author_card_limit', label: '作者卡文章数', type: 'number', default: 6, min: 0, max: 12, dependsOn: { key: 'post_authordesc_s', value: true } }, - { key: 'post_author_card_orderby', label: '作者卡文章排序', type: 'select', default: 'views', options: [ - { label: '浏览量', value: 'views' }, - { label: '发布时间', value: 'date' }, - { label: '更新时间', value: 'modified' }, - { label: '评论数', value: 'comment_count' }, - ], dependsOn: { key: 'post_authordesc_s', value: true } }, - { key: 'post_author_card_style', label: '作者卡文章样式', type: 'radio', default: 'card', options: [ - { label: '卡片', value: 'card' }, - { label: '列表', value: 'list' }, - ], dependsOn: { key: 'post_authordesc_s', value: true } }, - { key: 'post_single_hide_time_s', label: '隐藏文章时间', type: 'switch', default: false }, - ], - }, - { - key: 'cover', - title: '封面展示', - icon: 'fa fas fa-image', - fields: [ - { key: 'post_article_image_cover', label: '图片封面', type: 'switch', default: true, help: '文章有特色图时自动展示封面' }, - { key: 'post_article_image_cover_scale', label: '封面高度比例(%)', type: 'number', default: 35, min: 10, max: 100, dependsOn: { key: 'post_article_image_cover', value: true } }, - { key: 'post_article_slide_cover', label: '幻灯片封面', type: 'switch', default: true, help: '文章有图集时以轮播形式展示封面' }, - { key: 'post_article_video_cover', label: '视频封面', type: 'switch', default: true, help: '文章有视频时以视频播放器作为封面' }, - { key: 'post_article_video_cover_scale', label: '视频封面高度比例(%)', type: 'number', default: 56, min: 20, max: 100, dependsOn: { key: 'post_article_video_cover', value: true } }, - ], - }, - { - key: 'related', - title: '相关推荐', - icon: 'fa fas fa-random', - fields: [ - { key: 'post_related_s', label: '显示相关推荐', type: 'switch', default: true }, - { key: 'post_related_type', label: '展示样式', type: 'radio', default: 'img', options: [ - { label: '图文卡片', value: 'img' }, - { label: '列表', value: 'list' }, - { label: '纯文字', value: 'text' }, - ], dependsOn: { key: 'post_related_s', value: true }, help: '对齐 legacy 相关文章三种样式' }, - { key: 'related_title', label: '推荐标题', type: 'input', default: '相关推荐', dependsOn: { key: 'post_related_s', value: true } }, - { key: 'post_related_n', label: '推荐数量', type: 'number', default: 6, min: 4, max: 12, step: 2, dependsOn: { key: 'post_related_s', value: true } }, - { key: 'post_related_orderby', label: '推荐排序', type: 'select', default: 'views', options: [ - { label: '更新时间', value: 'modified' }, - { label: '发布时间', value: 'date' }, - { label: '浏览量', value: 'views' }, - { label: '点赞量', value: 'like' }, - { label: '评论量', value: 'comment_count' }, - { label: '收藏数量', value: 'favorite' }, - { label: '随机', value: 'rand' }, - ], dependsOn: { key: 'post_related_s', value: true } }, - ], - }, - { - key: 'interactions', - title: '互动功能', - icon: 'fa fas fa-heart', - fields: [ - { key: 'post_favorite_enabled', label: '收藏', type: 'switch', default: true }, - { key: 'share_s', label: '分享按钮', type: 'switch', default: true }, - { key: 'post_copyright_enabled', label: '版权信息', type: 'switch', default: true }, - { key: 'post_copyright_text', label: '版权默认文案', type: 'textarea', default: '', help: '留空则自动生成"本文由 XX 原创发布"' }, - { key: 'post_button_toptext', label: '按钮上方文案', type: 'input', default: '喜欢就支持一下吧', help: '显示在点赞/打赏/分享按钮上方' }, - { key: 'post_rewards_s', label: '用户打赏', type: 'switch', default: false, help: '开启后,登录用户可使用站内余额赞赏作者,金额直接进入作者余额。' }, - { key: 'post_rewards_text', label: '打赏按钮文案', type: 'input', default: '赞赏', dependsOn: { key: 'post_rewards_s', value: true } }, - { key: 'post_rewards_amounts', label: '快捷金额', type: 'input', default: '5,10,20,50,100', help: '使用英文逗号分隔', dependsOn: { key: 'post_rewards_s', value: true } }, - { key: 'post_rewards_custom', label: '允许自定义金额', type: 'switch', default: true, dependsOn: { key: 'post_rewards_s', value: true } }, - { key: 'post_rewards_min', label: '最低金额', type: 'input', default: '1.00', min: 0.01, max: 1000, step: 0.01, dependsOn: { key: 'post_rewards_s', value: true } }, - { key: 'post_rewards_max', label: '最高金额', type: 'input', default: '1000.00', min: 1, max: 100000, step: 0.01, dependsOn: { key: 'post_rewards_s', value: true } }, - { key: 'article_maxheight_kg', label: '正文限高', type: 'switch', default: false, help: '开启后长文默认折叠,可展开全文' }, - { key: 'article_maxheight', label: '限高像素', type: 'number', default: 1000, min: 200, max: 5000, dependsOn: { key: 'article_maxheight_kg', value: true } }, - ], - }, - { - key: 'content', - title: '内容插入', - icon: 'fa fas fa-pen-square', - fields: [ - { key: 'post_front_content', label: '文章头部内容', type: 'textarea', default: '', help: '显示在所有文章正文前方(支持 HTML)' }, - { key: 'post_after_content', label: '文章底部内容', type: 'textarea', default: '', help: '显示在所有文章正文后方(支持 HTML)' }, - ], - }, - { - key: 'yiyan', - title: '一言', - icon: 'fa fas fa-quote-left', - fields: [ - { key: 'post_yiyan_header', label: '文章头部一言', type: 'switch', default: false, help: '在文章标题下方显示一言' }, - { key: 'post_yiyan_footer', label: '文章底部一言', type: 'switch', default: false, help: '在文章内容底部显示一言' }, - { key: 'post_yiyan_box', label: '一言盒子', type: 'switch', default: false, help: '在相关文章前显示一言盒子' }, + { key: 'breadcrumbs_home_text', label: '面包屑导航用“首页”替代网站名称', type: 'switch', default: true, dependsOn: { key: 'breadcrumbs_single_s', value: true } }, + { key: 'post_prevnext_s', label: '上一页、下一页版块', type: 'switch', default: true }, + { key: 'post_authordesc_s', label: '作者信息版块', type: 'switch', default: true }, + { key: 'article_maxheight_kg', label: '内容高度限制', type: 'switch', default: false, description: '此处为全局设置,每篇文章可单独设置', help: '全局开关' }, + { key: 'article_maxheight', label: '限制的最大高度', type: 'slider', default: 1000, min: 600, max: 3000, step: 100, unit: 'px', dependsOn: { key: 'article_maxheight_kg', value: true }, description: '开启后如果文章高度超过设定值则会显示展开阅读全文的按钮。每篇文章可单独开启此功能' }, + { + key: 'yiyan_intro', + type: 'content', + ui_only: true, + content: '

精彩一言功能:随机显示中英文的文案,在此支持一言内容插入到文章页位置,同时可以使用小工具模块调用。
主题默认调用的文件为:themes/aksrt/yiyan/qv-yiyan.txt
您可以将以上文件复制到 wp-content 文件夹,并重命名为 yiyan.txt,再进行修改或添加内容,则今后更新主题就无需二次修改

', + }, + { key: 'yiyan_single_content_header', label: '文章内容头部显示一言', type: 'switch', default: false }, + { key: 'yiyan_single_content_footer', label: '文章内容尾部显示一言', type: 'switch', default: false }, + { key: 'yiyan_single_box', label: '文章页面下方独立一言版块', type: 'switch', default: false }, + { key: 'post_copyright_s', label: '版权声明', type: 'switch', default: true }, + { key: 'post_copyright', label: '版权提示内容', type: 'textarea', default: '文章版权归作者所有,未经允许请勿转载。', rows: 2, dependsOn: { key: 'post_copyright_s', value: true }, description: '支持HTML代码,请注意代码规范及标签闭合' }, + { key: 'post_button_toptext', label: '文章页脚文案', type: 'input', default: '喜欢就支持一下吧', description: '文章底部打赏、分享按钮上面的文字' }, + { key: 'post_front_content', label: '在文章内容前-插入内容', type: 'textarea', default: '', rows: 3 }, + { key: 'post_after_content', label: '在文章内容后-插入内容', type: 'textarea', default: '', rows: 3, description: '在每篇文章顶部和尾部插入内容,可以插入广告或者文章说明等内容。支持HTML代码,请注意代码规范及标签闭合' }, + { key: 'post_related_s', label: '相关文章版块', type: 'switch', default: true }, + { + key: 'post_related_type', + label: '显示样式', + type: 'image_select', + default: 'img', + dependsOn: { key: 'post_related_s', value: true }, + options: [ + { label: '图文卡片', value: 'img', icon: 'fa fas fa-image' }, + { label: '列表', value: 'list', icon: 'fa fas fa-list' }, + { label: '纯文字', value: 'text', icon: 'fa fas fa-align-left' }, + ], + }, + { key: 'related_title', label: '版块标题', type: 'input', default: '相关推荐', dependsOn: { key: 'post_related_s', value: true } }, + { key: 'post_related_n', label: '显示数量', type: 'number', default: 6, min: 4, max: 12, step: 2, unit: '篇', dependsOn: { key: 'post_related_s', value: true } }, + { + key: 'post_related_orderby', + label: '排序方式', + type: 'select', + default: 'views', + dependsOn: { key: 'post_related_s', value: true }, + options: [ + { label: '更新时间', value: 'modified' }, + { label: '发布时间', value: 'date' }, + { label: '浏览量', value: 'views' }, + { label: '点赞量', value: 'like' }, + { label: '评论量', value: 'comment_count' }, + { label: '收藏数量', value: 'favorite' }, + { label: '随机', value: 'rand' }, + ], + }, ], }, ], diff --git a/assets/admin/src/schema/sections/post-list/share.js b/assets/admin/src/schema/sections/post-list/share.js index 4db6cf24..f0344feb 100644 --- a/assets/admin/src/schema/sections/post-list/share.js +++ b/assets/admin/src/schema/sections/post-list/share.js @@ -4,6 +4,7 @@ export const shareSchema = { icon: 'fa fas fa-share-alt', description: '配置微信分享凭据、海报分享和社交分享选项。', defaults: { + share_s: true, share_wechat_s: false, share_wechat_appid: '', share_wechat_app_secret: '', @@ -15,6 +16,14 @@ export const shareSchema = { share_desc: '扫一扫 立即查看', }, groups: [ + { + key: 'basic', + title: '分享按钮', + icon: 'fa fas fa-share-alt', + fields: [ + { key: 'share_s', label: '分享按钮', type: 'switch', default: true, help: '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响' }, + ], + }, { key: 'wechat', title: '微信分享', diff --git a/assets/admin/src/schema/sections/users/features.js b/assets/admin/src/schema/sections/users/features.js index 6a005991..a9e1060f 100644 --- a/assets/admin/src/schema/sections/users/features.js +++ b/assets/admin/src/schema/sections/users/features.js @@ -14,6 +14,12 @@ export const featuresSchema = { user_cover_upload_s: true, user_cover_img: `${themeRoot}/assets/img/user_t.jpg`, user_desc_std: '这家伙很懒,什么都没有写...', + post_rewards_s: true, + post_rewards_text: '赞赏', + post_rewards_amounts: '5,10,20,50,100', + post_rewards_custom: true, + post_rewards_min: '1.00', + post_rewards_max: '1000.00', }, groups: [ { @@ -45,5 +51,18 @@ export const featuresSchema = { { key: 'user_desc_std', label: '用户默认签名', type: 'input', default: '这家伙很懒,什么都没有写...', help: '用户未设置个人简介时显示的默认文案。' }, ], }, + { + key: 'rewards', + title: '打赏', + icon: 'fa fas fa-hand-holding-heart', + fields: [ + { key: 'post_rewards_s', label: '用户打赏功能', type: 'switch', default: true, help: '开启后,登录用户可使用站内余额赞赏作者,金额直接进入作者余额。' }, + { key: 'post_rewards_text', label: '打赏按钮文案', type: 'input', default: '赞赏', dependsOn: { key: 'post_rewards_s', value: true }, help: '自定义打赏按钮文字' }, + { key: 'post_rewards_amounts', label: '快捷金额', type: 'input', default: '5,10,20,50,100', dependsOn: { key: 'post_rewards_s', value: true }, help: '使用英文逗号分隔' }, + { key: 'post_rewards_custom', label: '允许自定义金额', type: 'switch', default: true, dependsOn: { key: 'post_rewards_s', value: true } }, + { key: 'post_rewards_min', label: '最低金额', type: 'input', default: '1.00', min: 0.01, max: 1000, step: 0.01, dependsOn: { key: 'post_rewards_s', value: true } }, + { key: 'post_rewards_max', label: '最高金额', type: 'input', default: '1000.00', min: 1, max: 100000, step: 0.01, dependsOn: { key: 'post_rewards_s', value: true } }, + ], + }, ], } diff --git a/assets/css/build/single.css b/assets/css/build/single.css index 31c3cb6b..90bc08e1 100644 --- a/assets/css/build/single.css +++ b/assets/css/build/single.css @@ -476,6 +476,11 @@ article.article.box-body, margin: 0 0 1.5em; } +/* 段落首行缩进(参考主题 post_p_indent_s:开启后正文段落首行缩进 2em)。 */ +.wp-posts-content.p-indent p { + text-indent: 2em; +} + :is(.entry-content, .wp-posts-content) h2 { font-size: 20px; font-weight: 700; diff --git a/assets/css/build/single.min.css b/assets/css/build/single.min.css index 20e4cb5b..9b95303d 100644 --- a/assets/css/build/single.min.css +++ b/assets/css/build/single.min.css @@ -1 +1 @@ -.relative{position:relative}.flex{display:flex}.flex.ac,.ac{align-items:center}.flex.jc,.jc{justify-content:center}.flex.jsb,.jsb{justify-content:space-between}.flex.at,.at{align-items:flex-start}.flex.ae,.ae{align-items:flex-end}.flex0{flex:0 0 auto}.flex1{flex:1 1 auto;min-width:0}.flex.column{flex-direction:column}.flex.wrap{flex-wrap:wrap}.flex.flex-row,.flex-row{flex-direction:row}.flex.jse,.jse{justify-content:space-evenly}.flex.jsa,.jsa{justify-content:space-around}.flex>.flex1,.flex .flex1{flex:1;overflow:hidden;min-width:0}.relative-h{position:relative;height:100%}.padding-10{padding:10px}.font-bold{font-weight:700}.mt6{margin-top:6px}.gutters-5{margin:-5px}.gutters-5>*{padding:5px}@media(max-width:767px){.flex.flex-row.flex-col-sm-2{flex-wrap:wrap}.flex.flex-row.flex-col-sm-2>*{flex-basis:50%}}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}.abs-right{position:absolute;right:0;top:0}.abs-center{position:absolute;left:0;width:100%;top:50%;right:.7em;transform:translateY(-50%);z-index:1}.abs-center.left-top,.abs-center.right-top{transform:unset;top:0}.abs-center.right-top{left:auto;width:auto;text-align:right}.abs-center.left-top{text-align:left}.abs-center.conter-bottom,.abs-center.conter-conter{text-align:center}.abs-center.right-top,.abs-center.right-bottom,.abs-center.right-conter{text-align:right}.abs-center.conter-bottom,.abs-center.right-bottom,.abs-center.left-bottom{top:auto;transform:unset;bottom:0}.abs-center.left-bottom{left:0;right:auto;text-align:left}.abs-center.right-bottom,.abs-center.right-conter{left:auto;right:0;text-align:right}.graphic,.page-cover{text-shadow:0 0 5px rgba(0,0,0,.2);--main-color: #fff;color:#fff;position:relative;overflow:hidden;box-shadow:0 0 10px var(--main-shadow, rgba(116, 116, 116, .08));padding:0 0 70%;border-radius:var(--main-radius, var(--radius, 8px))}.graphic>.fit-cover,.graphic>img.fit-cover{position:absolute;width:100%;height:100%;object-fit:cover;inset:0}.fit-cover{object-fit:cover;max-width:100%}.title-h-left{position:relative;padding-bottom:8px}.title-h-left:before{transition:.4s;transform-origin:left;position:absolute;content:"";width:40px;height:3px;background:var(--theme-color, var(--focus-color, #f04494));left:0;bottom:3px;border-radius:5px;box-shadow:1px 1px 3px -1px var(--theme-color, var(--focus-color, #f04494))}.line-form-line,.line-form-line:before{position:absolute;bottom:0;left:0;height:1px;width:100%;transition:.8s;background:var(--main-border-color, rgba(50, 50, 50, .06))}.line-form-line:before{content:"";width:0;background:var(--focus-color, #f04494)}.avatar-img{position:relative;display:inline-block;flex-shrink:0;--this-size: 38px;width:var(--this-size);height:var(--this-size)}.avatar-img .avatar,.avatar-img img{width:100%;height:100%;border-radius:50%;object-fit:cover}.avatar-lg{--this-size: 80px}.avatar-md{--this-size: 55px}.avatar-mini{--this-size: 20px}.shrink0{flex-shrink:0}.opacity8{opacity:.8}.muted-color{color:var(--muted-color, var(--color-ink-3))}.muted-2-color{color:var(--muted-2-color, var(--color-ink-4))}.muted-3-color{color:var(--muted-3-color, #b1b1b1)}.muted-4-color{color:var(--muted-4-color, #d2d2d2)}.text-center{text-align:center}.em09{font-size:.9em}.em12{font-size:1.2em}.px12-sm{font-size:12px}.ml3{margin-left:3px}.ml6{margin-left:6px}.ml10{margin-left:10px}.mr3{margin-right:3px}.mr6{margin-right:6px}.mt10{margin-top:10px}.mt20{margin-top:20px}.mb10{margin-bottom:10px}.mb20{margin-bottom:20px}.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.text-ellipsis-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.main-bg{background-color:var(--main-bg-color, var(--surface-1))}.separator{position:relative;letter-spacing:.08em}.icon-circle+.icon-circle:before{content:"·";display:inline-block;margin:0 .45em;opacity:.55}.article-avatar name,.article-avatar .name,.user-name{display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:100%}.pull-right{float:right}.pull-left{float:left}body.single main.container .content-layout{display:block;min-width:0}body.single main.container .content-layout>*{min-width:0;max-width:100%}body.single main.container .content-layout>.breadcrumb,body.single main.container .content-layout>ul.breadcrumb,body.single main.container .content-layout>.breadcrumb-nav,body.single main.container .content-layout>.breadcrumbs-wrap{position:relative;z-index:1;margin-top:0;margin-bottom:12px;padding-top:2px}body.single .sidebar .aksrt-panel,body.single .sidebar .widget{margin-bottom:15px}body.single main.container{box-sizing:border-box}.article,article.article.box-body,.article.main-bg.box-body{padding:0 30px;overflow:hidden}@media(max-width:767px){.article,article.article.box-body,.article.main-bg.box-body{padding:0 15px}.article-header .article-title{font-size:18px}}.entry-header,.article-header{margin-bottom:10px;padding-bottom:0;border-bottom:none;background:transparent;box-shadow:none;border-radius:0}.entry-title,.article-title{font-size:22px;font-weight:700;margin:0;line-height:1.4;letter-spacing:0;color:var(--key-color, var(--color-ink))}.article-title>a:hover{color:inherit}.article-header .article-title{font-size:22px;font-weight:700;color:var(--key-color, var(--color-ink));margin-top:15px}.article-header .article-avatar{margin-top:15px}.entry-meta{display:flex;flex-wrap:wrap;gap:10px 16px;font-size:.875rem;color:var(--color-ink-4)}.entry-meta a{color:var(--color-ink-3)}.entry-meta a:hover{color:var(--color-primary)}.entry-thumb{margin:0 0 28px;border-radius:var(--radius-btn);overflow:hidden}.entry-content,.wp-posts-content{font-size:16px;line-height:2;color:var(--main-color, var(--color-ink));overflow-wrap:break-word;word-wrap:break-word;min-height:90px}.wp-posts-content>h1:not([class]),.wp-posts-content>h2:not([class]),.wp-posts-content>h3:not([class]),.wp-posts-content>h4:not([class]),.wp-posts-content>h1.wp-block-heading,.wp-posts-content>h2.wp-block-heading,.wp-posts-content>h3.wp-block-heading,.wp-posts-content>h4.wp-block-heading{position:relative;padding-bottom:8px}.wp-posts-content>h1:not([class]):before,.wp-posts-content>h2:not([class]):before,.wp-posts-content>h3:not([class]):before,.wp-posts-content>h4:not([class]):before,.wp-posts-content>h1.wp-block-heading:before,.wp-posts-content>h2.wp-block-heading:before,.wp-posts-content>h3.wp-block-heading:before,.wp-posts-content>h4.wp-block-heading:before{content:"";position:absolute;left:0;bottom:0;width:40px;height:3px;background:var(--theme-color, var(--focus-color, #f04494));border-radius:5px}.wp-posts-content>h3:not([class]):before,.wp-posts-content>h4:not([class]):before,.wp-posts-content>h3.wp-block-heading:before,.wp-posts-content>h4.wp-block-heading:before{width:28px;height:2px}:is(.entry-content,.wp-posts-content)>*:first-child{margin-top:0}:is(.entry-content,.wp-posts-content) p{margin:0 0 1.5em}:is(.entry-content,.wp-posts-content) h2{font-size:20px;font-weight:700;margin:28px 0 14px}:is(.entry-content,.wp-posts-content) h3{font-size:18px;font-weight:700;margin:24px 0 12px}:is(.entry-content,.wp-posts-content) h4{font-size:16px;font-weight:700;margin:22px 0 10px}:is(.entry-content,.wp-posts-content) blockquote{margin:0 0 24px;padding:18px 20px;border-left:4px solid var(--color-primary);background:var(--surface-4, rgba(0, 0, 0, .03));border-radius:0 var(--radius-lg) var(--radius-lg) 0;color:var(--color-ink-3)}:is(.entry-content,.wp-posts-content) img{margin:0 auto;display:block;max-width:100%;height:auto;border-radius:min(8px,var(--main-radius, 8px))}:is(.entry-content,.wp-posts-content) figure{max-width:100%;margin:1.5em auto}:is(.entry-content,.wp-posts-content) figcaption{margin-top:8px;color:var(--muted-color, #777);font-size:13px;line-height:1.6;text-align:center}:is(.entry-content,.wp-posts-content) iframe,:is(.entry-content,.wp-posts-content) .wp-block-embed iframe,:is(.entry-content,.wp-posts-content) video{max-width:100%;height:auto}:is(.entry-content,.wp-posts-content) .wp-block-embed{position:relative;width:100%;max-width:100%;overflow:hidden;margin:1.5em 0}:is(.entry-content,.wp-posts-content) code{font-family:var(--font-mono);font-size:.9em;color:#ff3c98;background:#fd999933;padding:2px 6px;border-radius:4px}:is(.entry-content,.wp-posts-content) ul,:is(.entry-content,.wp-posts-content) ol{padding-left:1.5em;margin:0 0 1.5em}:is(.entry-content,.wp-posts-content) ul li::marker{color:var(--color-primary)}.entry-footer{margin-top:40px;padding-top:24px;border-top:1px solid var(--color-line)}.post-tags{display:flex;flex-wrap:wrap;gap:8px}.post-tags a{padding:4px 10px;border-radius:50px;background:var(--muted-bg-color, #eee);border:1px solid var(--main-border-color, rgba(50, 50, 50, .06));font-size:13px;color:var(--muted-color, #777)}.post-tags a:hover{border-color:var(--color-primary);color:var(--color-primary)}.related-posts{margin-top:48px;padding:20px 0 0;border-top:1px solid var(--color-line-soft)}.related-posts__title{font-size:15px;font-weight:700;margin:0 0 10px;color:var(--key-color, #333)}.related-posts__title:before{content:"";display:inline-block;width:40px;height:3px;background:var(--theme-color, #f04494);border-radius:5px;vertical-align:middle;margin-right:8px}.related-posts__grid{display:flex;flex-direction:column;gap:0}.related-card{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--main-border-color, rgba(50, 50, 50, .06));transition:.15s}.related-card:last-child{border-bottom:none}.related-card a{display:flex;align-items:center;gap:12px;color:var(--key-color, #333);text-decoration:none;width:100%}.related-card:hover a{color:var(--focus-color, #f04494)}.related-card__thumb{width:96px;min-width:96px;height:64px;border-radius:var(--main-radius, 8px);overflow:hidden;flex-shrink:0}.related-card__thumb img{width:100%;height:100%;object-fit:cover;transition:transform .3s}.related-card:hover .related-card__thumb img{transform:scale(1.05)}.related-card h4{font-size:14px;font-weight:600;margin:0;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.article-footer{margin-top:32px;padding-top:20px;border-top:1px solid var(--color-line)}.article-copyright{font-size:13px;color:var(--color-ink-4, var(--color-ink-4));margin-bottom:16px}.article-end-separator{text-align:center;margin:16px 0;position:relative}.article-end-separator:before{content:"";position:absolute;top:50%;left:0;right:0;height:1px;background:var(--color-line)}.article-end-separator span{position:relative;display:inline-block;padding:0 14px;background:var(--surface-page);font-size:11px;font-weight:600;color:var(--color-ink-4, var(--color-ink-4));letter-spacing:2px}.article-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}.post-actions-wrap{position:relative;text-align:center;margin-bottom:16px}.post-actions-card,.post-actions{position:relative;text-align:center;margin-bottom:16px;padding-bottom:15px;display:block}.post-actions-wrap>.post-actions{margin-bottom:0}.post-actions-card__hint{font-size:13px;color:var(--color-ink-4, var(--color-ink-4));margin-bottom:12px}.post-actions-card__buttons{display:flex;justify-content:center;gap:10px;margin-bottom:12px}.article .action,.post-actions .action{width:80px;padding:10px 0;display:inline-block;opacity:.7;vertical-align:top;min-width:0;height:auto;border-radius:0;border:0;background:transparent;color:var(--muted-color, #777);cursor:pointer;font-size:14px;transition:opacity .15s ease,color .15s ease;gap:0;text-align:center}.article .action:hover,.post-actions .action:hover,.article .action.is-active,.post-actions .action.is-active{color:inherit;opacity:1;background:transparent;border-color:transparent}.article .action i,.post-actions .action i{font-size:20px;display:block;margin:0 auto 4px;line-height:1}.post-actions .action .action-btn__count,.post-actions .action .action-btn__label,.action-btn__count,.action-btn__label{display:block;font-size:12px;line-height:1.2;font-weight:500}.action-btn{display:inline-flex;align-items:center;gap:6px;padding:.38em 1.1em;border-radius:50px;border:1px solid transparent;background:var(--surface-4, rgba(136, 136, 136, .1));color:var(--color-ink-3);cursor:pointer;font-size:14px;transition:all .15s ease}.action-btn:hover{background:var(--color-primary-soft);color:var(--color-primary)}.action-btn.is-active{border-color:var(--color-primary);color:var(--color-primary);background:var(--color-primary-soft, rgba(240, 68, 148, .08))}.action-btn i{font-size:1.2em}.share-dropdown{position:absolute;left:50%;bottom:calc(100% + 8px);transform:translate(-50%);z-index:100;display:none!important}.share-dropdown[aria-hidden=false]{display:block!important;animation:shareDropdownIn .2s ease-out forwards}@keyframes shareDropdownIn{0%{opacity:0;transform:translate(-50%) translateY(8px)}to{opacity:1;transform:translate(-50%) translateY(0)}}.share-dropdown__grid{display:flex;align-items:center;gap:8px;padding:12px 16px;background:var(--surface-page);border:1px solid var(--color-line);border-radius:var(--radius-lg, 8px);box-shadow:var(--shadow-md, 0 4px 16px rgba(0,0,0,.1))}.share-item{display:inline-flex;flex-direction:column;align-items:center;gap:4px;padding:8px 12px;border:none;border-radius:var(--radius-btn, 6px);background:transparent;color:var(--color-ink-2, var(--color-ink-3));cursor:pointer;font-size:11px;transition:background .15s,color .15s,transform .15s;white-space:nowrap}.share-item:hover{transform:none}.share-item i{font-size:18px}.share-item[data-platform=qq]:hover{background:#e8f4fd;color:#12b7f5}.share-item[data-platform=weibo]:hover{background:#fceae9;color:#ff6a00}.share-item[data-platform=twitter]:hover{background:#f0f0f0;color:#000}.share-item[data-platform=wechat]:hover{background:#e6f7ec;color:#07c160}.share-item[data-platform=copy]:hover{background:#f0f2f5;color:#555}.share-item[data-platform=poster]:hover{background:#fff4e8;color:#f59e0b}#share-qrcode-box{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);padding:14px;background:var(--surface-page);border:1px solid var(--color-line);border-radius:var(--radius-lg, 8px);box-shadow:var(--shadow-md, 0 4px 16px rgba(0,0,0,.12));text-align:center;z-index:101;pointer-events:none;opacity:0;transition:opacity .2s ease,transform .2s ease}#share-qrcode-box.is-visible{pointer-events:auto;opacity:1}#share-qrcode-image{width:140px;height:140px;display:block}.theme-dark .share-dropdown__grid,html.dark .share-dropdown__grid{background:var(--surface-1);border-color:var(--color-line);box-shadow:0 4px 16px #00000059}.theme-dark #share-qrcode-box,html.dark #share-qrcode-box{background:var(--surface-1);border-color:var(--color-line)}.theme-dark .share-item,html.dark .share-item{color:var(--color-ink-2, #aaa)}.theme-dark .share-item[data-platform=qq]:hover{background:#12b7f51f;color:#12b7f5}.theme-dark .share-item[data-platform=weibo]:hover{background:#ff6a001f;color:#ff6a00}.theme-dark .share-item[data-platform=twitter]:hover{background:#ffffff14;color:#fff}.theme-dark .share-item[data-platform=wechat]:hover{background:#07c1601f;color:#07c160}.theme-dark .share-item[data-platform=copy]:hover{background:#ffffff0f;color:#ccc}.theme-dark .share-item[data-platform=poster]:hover{background:#f59e0b1f;color:#f59e0b}@media(max-width:767px){.share-dropdown{position:fixed;inset:auto 0 0;transform:none;width:100%;max-width:none;z-index:1200;padding:0 0 calc(12px + env(safe-area-inset-bottom,0px));background:transparent}.share-dropdown[aria-hidden=false]{animation:shareSheetIn .22s ease-out forwards}@keyframes shareSheetIn{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}.share-dropdown__grid{justify-content:space-around;flex-wrap:wrap;gap:6px;margin:0 10px;padding:14px 12px 16px;border-radius:14px 14px 10px 10px;box-shadow:0 -6px 28px #00000029}.share-item{min-width:64px;flex:1 1 28%;max-width:33%}#share-qrcode-box{position:static;margin:10px auto 0;transform:none}body.aksrt-share-sheet-open{overflow:hidden}body.aksrt-share-sheet-open:before{content:"";position:fixed;inset:0;background:#00000059;z-index:1190}}.theme-dark .post-drop-meta__menu,html.dark .post-drop-meta__menu{background:var(--surface-1, #323335);border-color:var(--color-line);box-shadow:0 8px 24px #00000059}.theme-dark .post-drop-meta__item,html.dark .post-drop-meta__item{color:var(--color-ink-2, #ccc)}.theme-dark .post-drop-meta__item:hover,html.dark .post-drop-meta__item:hover{background:#ffffff0f;color:#fff}.single-metabox{display:flex;align-items:center;justify-content:flex-end;gap:8px}.post-drop-meta{position:relative}.post-drop-meta__menu{position:absolute;top:calc(100% + 8px);right:0;min-width:150px;padding:8px;border-radius:var(--radius, 8px);border:1px solid var(--color-line);background:var(--surface-page, #fff);box-shadow:var(--shadow-md, 0 8px 24px rgba(0, 0, 0, .12));display:none;z-index:20}.post-drop-meta.is-open .post-drop-meta__menu{display:grid;gap:4px}.article-header:has(.post-drop-meta.is-open){z-index:30}.post-drop-meta__item{width:100%;display:inline-flex;align-items:center;gap:8px;padding:8px 10px;border:0;border-radius:6px;background:transparent;color:var(--color-ink-2, var(--color-ink));text-decoration:none;font-size:13px;text-align:left;cursor:pointer}.post-drop-meta__item:hover{background:var(--color-line, rgba(50, 50, 50, .06));color:var(--color-primary)}.post-drop-meta__item i{width:16px;text-align:center}.article-avatar .user-info{width:100%}.article-avatar .avatar-img{--this-size: 42px}.article-avatar .avatar-img img,.article-avatar .avatar{width:100%;height:100%;border-radius:50%;object-fit:cover}.article-avatar .display-name{font-weight:600;color:var(--key-color, #333)}.article-header .article-avatar .single-meta-row{margin-top:20px;position:relative;min-height:28px;display:flex;align-items:center;justify-content:flex-end}.article-header .article-avatar .single-meta-row>.line-form-line{bottom:50%}.article-header .article-avatar .single-meta-row>.single-metabox{position:relative;inset:auto;transform:none;z-index:1}.single-metabox{right:0;font-size:15px;--meta-color: var(--muted-color, #777)}.single-metabox .post-metas{background:var(--body-bg-color, #f5f5f5);right:0;padding:2px 6px;border-radius:100px;color:var(--meta-color);display:inline-flex;flex-wrap:wrap;align-items:center;gap:0}.single-metabox .post-metas item,.single-metabox .post-metas .item{margin:0 6px;font-size:13px;display:inline-flex;align-items:center;gap:4px}.single-metabox .post-drop-meta{position:relative;display:inline-block;--this-size: 1.73em;--this-color: var(--meta-color);--this-bg: var(--body-bg-color, #f5f5f5)}.article-avatar .line-form-line{display:block}.article-tags br{flex-basis:100%;width:100%;content:"";display:block;height:0}.article-tags .but.c-blue{color:#2196f3}.article-tags .but.c-yellow{color:#f0ad4e}.article-tags .but.c-green{color:#5cb85c}.article-tags .but.c-purple{color:#9c27b0}.article-tags .but.c-red{color:#f44336} +.relative{position:relative}.flex{display:flex}.flex.ac,.ac{align-items:center}.flex.jc,.jc{justify-content:center}.flex.jsb,.jsb{justify-content:space-between}.flex.at,.at{align-items:flex-start}.flex.ae,.ae{align-items:flex-end}.flex0{flex:0 0 auto}.flex1{flex:1 1 auto;min-width:0}.flex.column{flex-direction:column}.flex.wrap{flex-wrap:wrap}.flex.flex-row,.flex-row{flex-direction:row}.flex.jse,.jse{justify-content:space-evenly}.flex.jsa,.jsa{justify-content:space-around}.flex>.flex1,.flex .flex1{flex:1;overflow:hidden;min-width:0}.relative-h{position:relative;height:100%}.padding-10{padding:10px}.font-bold{font-weight:700}.mt6{margin-top:6px}.gutters-5{margin:-5px}.gutters-5>*{padding:5px}@media(max-width:767px){.flex.flex-row.flex-col-sm-2{flex-wrap:wrap}.flex.flex-row.flex-col-sm-2>*{flex-basis:50%}}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}.abs-right{position:absolute;right:0;top:0}.abs-center{position:absolute;left:0;width:100%;top:50%;right:.7em;transform:translateY(-50%);z-index:1}.abs-center.left-top,.abs-center.right-top{transform:unset;top:0}.abs-center.right-top{left:auto;width:auto;text-align:right}.abs-center.left-top{text-align:left}.abs-center.conter-bottom,.abs-center.conter-conter{text-align:center}.abs-center.right-top,.abs-center.right-bottom,.abs-center.right-conter{text-align:right}.abs-center.conter-bottom,.abs-center.right-bottom,.abs-center.left-bottom{top:auto;transform:unset;bottom:0}.abs-center.left-bottom{left:0;right:auto;text-align:left}.abs-center.right-bottom,.abs-center.right-conter{left:auto;right:0;text-align:right}.graphic,.page-cover{text-shadow:0 0 5px rgba(0,0,0,.2);--main-color: #fff;color:#fff;position:relative;overflow:hidden;box-shadow:0 0 10px var(--main-shadow, rgba(116, 116, 116, .08));padding:0 0 70%;border-radius:var(--main-radius, var(--radius, 8px))}.graphic>.fit-cover,.graphic>img.fit-cover{position:absolute;width:100%;height:100%;object-fit:cover;inset:0}.fit-cover{object-fit:cover;max-width:100%}.title-h-left{position:relative;padding-bottom:8px}.title-h-left:before{transition:.4s;transform-origin:left;position:absolute;content:"";width:40px;height:3px;background:var(--theme-color, var(--focus-color, #f04494));left:0;bottom:3px;border-radius:5px;box-shadow:1px 1px 3px -1px var(--theme-color, var(--focus-color, #f04494))}.line-form-line,.line-form-line:before{position:absolute;bottom:0;left:0;height:1px;width:100%;transition:.8s;background:var(--main-border-color, rgba(50, 50, 50, .06))}.line-form-line:before{content:"";width:0;background:var(--focus-color, #f04494)}.avatar-img{position:relative;display:inline-block;flex-shrink:0;--this-size: 38px;width:var(--this-size);height:var(--this-size)}.avatar-img .avatar,.avatar-img img{width:100%;height:100%;border-radius:50%;object-fit:cover}.avatar-lg{--this-size: 80px}.avatar-md{--this-size: 55px}.avatar-mini{--this-size: 20px}.shrink0{flex-shrink:0}.opacity8{opacity:.8}.muted-color{color:var(--muted-color, var(--color-ink-3))}.muted-2-color{color:var(--muted-2-color, var(--color-ink-4))}.muted-3-color{color:var(--muted-3-color, #b1b1b1)}.muted-4-color{color:var(--muted-4-color, #d2d2d2)}.text-center{text-align:center}.em09{font-size:.9em}.em12{font-size:1.2em}.px12-sm{font-size:12px}.ml3{margin-left:3px}.ml6{margin-left:6px}.ml10{margin-left:10px}.mr3{margin-right:3px}.mr6{margin-right:6px}.mt10{margin-top:10px}.mt20{margin-top:20px}.mb10{margin-bottom:10px}.mb20{margin-bottom:20px}.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.text-ellipsis-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.main-bg{background-color:var(--main-bg-color, var(--surface-1))}.separator{position:relative;letter-spacing:.08em}.icon-circle+.icon-circle:before{content:"·";display:inline-block;margin:0 .45em;opacity:.55}.article-avatar name,.article-avatar .name,.user-name{display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:100%}.pull-right{float:right}.pull-left{float:left}body.single main.container .content-layout{display:block;min-width:0}body.single main.container .content-layout>*{min-width:0;max-width:100%}body.single main.container .content-layout>.breadcrumb,body.single main.container .content-layout>ul.breadcrumb,body.single main.container .content-layout>.breadcrumb-nav,body.single main.container .content-layout>.breadcrumbs-wrap{position:relative;z-index:1;margin-top:0;margin-bottom:12px;padding-top:2px}body.single .sidebar .aksrt-panel,body.single .sidebar .widget{margin-bottom:15px}body.single main.container{box-sizing:border-box}.article,article.article.box-body,.article.main-bg.box-body{padding:0 30px;overflow:hidden}@media(max-width:767px){.article,article.article.box-body,.article.main-bg.box-body{padding:0 15px}.article-header .article-title{font-size:18px}}.entry-header,.article-header{margin-bottom:10px;padding-bottom:0;border-bottom:none;background:transparent;box-shadow:none;border-radius:0}.entry-title,.article-title{font-size:22px;font-weight:700;margin:0;line-height:1.4;letter-spacing:0;color:var(--key-color, var(--color-ink))}.article-title>a:hover{color:inherit}.article-header .article-title{font-size:22px;font-weight:700;color:var(--key-color, var(--color-ink));margin-top:15px}.article-header .article-avatar{margin-top:15px}.entry-meta{display:flex;flex-wrap:wrap;gap:10px 16px;font-size:.875rem;color:var(--color-ink-4)}.entry-meta a{color:var(--color-ink-3)}.entry-meta a:hover{color:var(--color-primary)}.entry-thumb{margin:0 0 28px;border-radius:var(--radius-btn);overflow:hidden}.entry-content,.wp-posts-content{font-size:16px;line-height:2;color:var(--main-color, var(--color-ink));overflow-wrap:break-word;word-wrap:break-word;min-height:90px}.wp-posts-content>h1:not([class]),.wp-posts-content>h2:not([class]),.wp-posts-content>h3:not([class]),.wp-posts-content>h4:not([class]),.wp-posts-content>h1.wp-block-heading,.wp-posts-content>h2.wp-block-heading,.wp-posts-content>h3.wp-block-heading,.wp-posts-content>h4.wp-block-heading{position:relative;padding-bottom:8px}.wp-posts-content>h1:not([class]):before,.wp-posts-content>h2:not([class]):before,.wp-posts-content>h3:not([class]):before,.wp-posts-content>h4:not([class]):before,.wp-posts-content>h1.wp-block-heading:before,.wp-posts-content>h2.wp-block-heading:before,.wp-posts-content>h3.wp-block-heading:before,.wp-posts-content>h4.wp-block-heading:before{content:"";position:absolute;left:0;bottom:0;width:40px;height:3px;background:var(--theme-color, var(--focus-color, #f04494));border-radius:5px}.wp-posts-content>h3:not([class]):before,.wp-posts-content>h4:not([class]):before,.wp-posts-content>h3.wp-block-heading:before,.wp-posts-content>h4.wp-block-heading:before{width:28px;height:2px}:is(.entry-content,.wp-posts-content)>*:first-child{margin-top:0}:is(.entry-content,.wp-posts-content) p{margin:0 0 1.5em}.wp-posts-content.p-indent p{text-indent:2em}:is(.entry-content,.wp-posts-content) h2{font-size:20px;font-weight:700;margin:28px 0 14px}:is(.entry-content,.wp-posts-content) h3{font-size:18px;font-weight:700;margin:24px 0 12px}:is(.entry-content,.wp-posts-content) h4{font-size:16px;font-weight:700;margin:22px 0 10px}:is(.entry-content,.wp-posts-content) blockquote{margin:0 0 24px;padding:18px 20px;border-left:4px solid var(--color-primary);background:var(--surface-4, rgba(0, 0, 0, .03));border-radius:0 var(--radius-lg) var(--radius-lg) 0;color:var(--color-ink-3)}:is(.entry-content,.wp-posts-content) img{margin:0 auto;display:block;max-width:100%;height:auto;border-radius:min(8px,var(--main-radius, 8px))}:is(.entry-content,.wp-posts-content) figure{max-width:100%;margin:1.5em auto}:is(.entry-content,.wp-posts-content) figcaption{margin-top:8px;color:var(--muted-color, #777);font-size:13px;line-height:1.6;text-align:center}:is(.entry-content,.wp-posts-content) iframe,:is(.entry-content,.wp-posts-content) .wp-block-embed iframe,:is(.entry-content,.wp-posts-content) video{max-width:100%;height:auto}:is(.entry-content,.wp-posts-content) .wp-block-embed{position:relative;width:100%;max-width:100%;overflow:hidden;margin:1.5em 0}:is(.entry-content,.wp-posts-content) code{font-family:var(--font-mono);font-size:.9em;color:#ff3c98;background:#fd999933;padding:2px 6px;border-radius:4px}:is(.entry-content,.wp-posts-content) ul,:is(.entry-content,.wp-posts-content) ol{padding-left:1.5em;margin:0 0 1.5em}:is(.entry-content,.wp-posts-content) ul li::marker{color:var(--color-primary)}.entry-footer{margin-top:40px;padding-top:24px;border-top:1px solid var(--color-line)}.post-tags{display:flex;flex-wrap:wrap;gap:8px}.post-tags a{padding:4px 10px;border-radius:50px;background:var(--muted-bg-color, #eee);border:1px solid var(--main-border-color, rgba(50, 50, 50, .06));font-size:13px;color:var(--muted-color, #777)}.post-tags a:hover{border-color:var(--color-primary);color:var(--color-primary)}.related-posts{margin-top:48px;padding:20px 0 0;border-top:1px solid var(--color-line-soft)}.related-posts__title{font-size:15px;font-weight:700;margin:0 0 10px;color:var(--key-color, #333)}.related-posts__title:before{content:"";display:inline-block;width:40px;height:3px;background:var(--theme-color, #f04494);border-radius:5px;vertical-align:middle;margin-right:8px}.related-posts__grid{display:flex;flex-direction:column;gap:0}.related-card{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--main-border-color, rgba(50, 50, 50, .06));transition:.15s}.related-card:last-child{border-bottom:none}.related-card a{display:flex;align-items:center;gap:12px;color:var(--key-color, #333);text-decoration:none;width:100%}.related-card:hover a{color:var(--focus-color, #f04494)}.related-card__thumb{width:96px;min-width:96px;height:64px;border-radius:var(--main-radius, 8px);overflow:hidden;flex-shrink:0}.related-card__thumb img{width:100%;height:100%;object-fit:cover;transition:transform .3s}.related-card:hover .related-card__thumb img{transform:scale(1.05)}.related-card h4{font-size:14px;font-weight:600;margin:0;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.article-footer{margin-top:32px;padding-top:20px;border-top:1px solid var(--color-line)}.article-copyright{font-size:13px;color:var(--color-ink-4, var(--color-ink-4));margin-bottom:16px}.article-end-separator{text-align:center;margin:16px 0;position:relative}.article-end-separator:before{content:"";position:absolute;top:50%;left:0;right:0;height:1px;background:var(--color-line)}.article-end-separator span{position:relative;display:inline-block;padding:0 14px;background:var(--surface-page);font-size:11px;font-weight:600;color:var(--color-ink-4, var(--color-ink-4));letter-spacing:2px}.article-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}.post-actions-wrap{position:relative;text-align:center;margin-bottom:16px}.post-actions-card,.post-actions{position:relative;text-align:center;margin-bottom:16px;padding-bottom:15px;display:block}.post-actions-wrap>.post-actions{margin-bottom:0}.post-actions-card__hint{font-size:13px;color:var(--color-ink-4, var(--color-ink-4));margin-bottom:12px}.post-actions-card__buttons{display:flex;justify-content:center;gap:10px;margin-bottom:12px}.article .action,.post-actions .action{width:80px;padding:10px 0;display:inline-block;opacity:.7;vertical-align:top;min-width:0;height:auto;border-radius:0;border:0;background:transparent;color:var(--muted-color, #777);cursor:pointer;font-size:14px;transition:opacity .15s ease,color .15s ease;gap:0;text-align:center}.article .action:hover,.post-actions .action:hover,.article .action.is-active,.post-actions .action.is-active{color:inherit;opacity:1;background:transparent;border-color:transparent}.article .action i,.post-actions .action i{font-size:20px;display:block;margin:0 auto 4px;line-height:1}.post-actions .action .action-btn__count,.post-actions .action .action-btn__label,.action-btn__count,.action-btn__label{display:block;font-size:12px;line-height:1.2;font-weight:500}.action-btn{display:inline-flex;align-items:center;gap:6px;padding:.38em 1.1em;border-radius:50px;border:1px solid transparent;background:var(--surface-4, rgba(136, 136, 136, .1));color:var(--color-ink-3);cursor:pointer;font-size:14px;transition:all .15s ease}.action-btn:hover{background:var(--color-primary-soft);color:var(--color-primary)}.action-btn.is-active{border-color:var(--color-primary);color:var(--color-primary);background:var(--color-primary-soft, rgba(240, 68, 148, .08))}.action-btn i{font-size:1.2em}.share-dropdown{position:absolute;left:50%;bottom:calc(100% + 8px);transform:translate(-50%);z-index:100;display:none!important}.share-dropdown[aria-hidden=false]{display:block!important;animation:shareDropdownIn .2s ease-out forwards}@keyframes shareDropdownIn{0%{opacity:0;transform:translate(-50%) translateY(8px)}to{opacity:1;transform:translate(-50%) translateY(0)}}.share-dropdown__grid{display:flex;align-items:center;gap:8px;padding:12px 16px;background:var(--surface-page);border:1px solid var(--color-line);border-radius:var(--radius-lg, 8px);box-shadow:var(--shadow-md, 0 4px 16px rgba(0,0,0,.1))}.share-item{display:inline-flex;flex-direction:column;align-items:center;gap:4px;padding:8px 12px;border:none;border-radius:var(--radius-btn, 6px);background:transparent;color:var(--color-ink-2, var(--color-ink-3));cursor:pointer;font-size:11px;transition:background .15s,color .15s,transform .15s;white-space:nowrap}.share-item:hover{transform:none}.share-item i{font-size:18px}.share-item[data-platform=qq]:hover{background:#e8f4fd;color:#12b7f5}.share-item[data-platform=weibo]:hover{background:#fceae9;color:#ff6a00}.share-item[data-platform=twitter]:hover{background:#f0f0f0;color:#000}.share-item[data-platform=wechat]:hover{background:#e6f7ec;color:#07c160}.share-item[data-platform=copy]:hover{background:#f0f2f5;color:#555}.share-item[data-platform=poster]:hover{background:#fff4e8;color:#f59e0b}#share-qrcode-box{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);padding:14px;background:var(--surface-page);border:1px solid var(--color-line);border-radius:var(--radius-lg, 8px);box-shadow:var(--shadow-md, 0 4px 16px rgba(0,0,0,.12));text-align:center;z-index:101;pointer-events:none;opacity:0;transition:opacity .2s ease,transform .2s ease}#share-qrcode-box.is-visible{pointer-events:auto;opacity:1}#share-qrcode-image{width:140px;height:140px;display:block}.theme-dark .share-dropdown__grid,html.dark .share-dropdown__grid{background:var(--surface-1);border-color:var(--color-line);box-shadow:0 4px 16px #00000059}.theme-dark #share-qrcode-box,html.dark #share-qrcode-box{background:var(--surface-1);border-color:var(--color-line)}.theme-dark .share-item,html.dark .share-item{color:var(--color-ink-2, #aaa)}.theme-dark .share-item[data-platform=qq]:hover{background:#12b7f51f;color:#12b7f5}.theme-dark .share-item[data-platform=weibo]:hover{background:#ff6a001f;color:#ff6a00}.theme-dark .share-item[data-platform=twitter]:hover{background:#ffffff14;color:#fff}.theme-dark .share-item[data-platform=wechat]:hover{background:#07c1601f;color:#07c160}.theme-dark .share-item[data-platform=copy]:hover{background:#ffffff0f;color:#ccc}.theme-dark .share-item[data-platform=poster]:hover{background:#f59e0b1f;color:#f59e0b}@media(max-width:767px){.share-dropdown{position:fixed;inset:auto 0 0;transform:none;width:100%;max-width:none;z-index:1200;padding:0 0 calc(12px + env(safe-area-inset-bottom,0px));background:transparent}.share-dropdown[aria-hidden=false]{animation:shareSheetIn .22s ease-out forwards}@keyframes shareSheetIn{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}.share-dropdown__grid{justify-content:space-around;flex-wrap:wrap;gap:6px;margin:0 10px;padding:14px 12px 16px;border-radius:14px 14px 10px 10px;box-shadow:0 -6px 28px #00000029}.share-item{min-width:64px;flex:1 1 28%;max-width:33%}#share-qrcode-box{position:static;margin:10px auto 0;transform:none}body.aksrt-share-sheet-open{overflow:hidden}body.aksrt-share-sheet-open:before{content:"";position:fixed;inset:0;background:#00000059;z-index:1190}}.theme-dark .post-drop-meta__menu,html.dark .post-drop-meta__menu{background:var(--surface-1, #323335);border-color:var(--color-line);box-shadow:0 8px 24px #00000059}.theme-dark .post-drop-meta__item,html.dark .post-drop-meta__item{color:var(--color-ink-2, #ccc)}.theme-dark .post-drop-meta__item:hover,html.dark .post-drop-meta__item:hover{background:#ffffff0f;color:#fff}.single-metabox{display:flex;align-items:center;justify-content:flex-end;gap:8px}.post-drop-meta{position:relative}.post-drop-meta__menu{position:absolute;top:calc(100% + 8px);right:0;min-width:150px;padding:8px;border-radius:var(--radius, 8px);border:1px solid var(--color-line);background:var(--surface-page, #fff);box-shadow:var(--shadow-md, 0 8px 24px rgba(0, 0, 0, .12));display:none;z-index:20}.post-drop-meta.is-open .post-drop-meta__menu{display:grid;gap:4px}.article-header:has(.post-drop-meta.is-open){z-index:30}.post-drop-meta__item{width:100%;display:inline-flex;align-items:center;gap:8px;padding:8px 10px;border:0;border-radius:6px;background:transparent;color:var(--color-ink-2, var(--color-ink));text-decoration:none;font-size:13px;text-align:left;cursor:pointer}.post-drop-meta__item:hover{background:var(--color-line, rgba(50, 50, 50, .06));color:var(--color-primary)}.post-drop-meta__item i{width:16px;text-align:center}.article-avatar .user-info{width:100%}.article-avatar .avatar-img{--this-size: 42px}.article-avatar .avatar-img img,.article-avatar .avatar{width:100%;height:100%;border-radius:50%;object-fit:cover}.article-avatar .display-name{font-weight:600;color:var(--key-color, #333)}.article-header .article-avatar .single-meta-row{margin-top:20px;position:relative;min-height:28px;display:flex;align-items:center;justify-content:flex-end}.article-header .article-avatar .single-meta-row>.line-form-line{bottom:50%}.article-header .article-avatar .single-meta-row>.single-metabox{position:relative;inset:auto;transform:none;z-index:1}.single-metabox{right:0;font-size:15px;--meta-color: var(--muted-color, #777)}.single-metabox .post-metas{background:var(--body-bg-color, #f5f5f5);right:0;padding:2px 6px;border-radius:100px;color:var(--meta-color);display:inline-flex;flex-wrap:wrap;align-items:center;gap:0}.single-metabox .post-metas item,.single-metabox .post-metas .item{margin:0 6px;font-size:13px;display:inline-flex;align-items:center;gap:4px}.single-metabox .post-drop-meta{position:relative;display:inline-block;--this-size: 1.73em;--this-color: var(--meta-color);--this-bg: var(--body-bg-color, #f5f5f5)}.article-avatar .line-form-line{display:block}.article-tags br{flex-basis:100%;width:100%;content:"";display:block;height:0}.article-tags .but.c-blue{color:#2196f3}.article-tags .but.c-yellow{color:#f0ad4e}.article-tags .but.c-green{color:#5cb85c}.article-tags .but.c-purple{color:#9c27b0}.article-tags .but.c-red{color:#f44336} diff --git a/assets/css/frontend/single-post/content.css b/assets/css/frontend/single-post/content.css index cfd79a12..de4b18d9 100644 --- a/assets/css/frontend/single-post/content.css +++ b/assets/css/frontend/single-post/content.css @@ -137,6 +137,11 @@ article.article.box-body, margin: 0 0 1.5em; } +/* 段落首行缩进(参考主题 post_p_indent_s:开启后正文段落首行缩进 2em)。 */ +.wp-posts-content.p-indent p { + text-indent: 2em; +} + :is(.entry-content, .wp-posts-content) h2 { font-size: 20px; font-weight: 700; diff --git a/inc/helpers/aksrt-frontend-tags.php b/inc/helpers/aksrt-frontend-tags.php index d7cef069..488fa790 100644 --- a/inc/helpers/aksrt-frontend-tags.php +++ b/inc/helpers/aksrt-frontend-tags.php @@ -28,6 +28,14 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) : 'related_orderby' => 'views', 'related_title' => '', 'breadcrumbs_single_text' => true, + 'breadcrumbs_home_text' => true, + 'p_indent' => false, + 'article_image_cover' => true, + 'article_image_cover_scale' => 35, + 'article_slide_cover' => true, + 'article_slide_cover_option'=> [], + 'article_video_cover' => true, + 'article_video_cover_scale' => 0, 'like_enabled' => true, 'favorite_enabled' => true, 'share_enabled' => true, @@ -46,7 +54,7 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) : 'maxheight_enabled' => false, 'maxheight' => 1000, 'copyright_enabled' => true, - 'copyright_text' => '', + 'copyright_text' => '文章版权归作者所有,未经允许请勿转载。', 'front_content' => '', 'after_content' => '', 'yiyan_header' => false, @@ -62,9 +70,6 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) : } if (!array_key_exists('breadcrumbs', $settings) && array_key_exists('breadcrumbs_single_s', $settings)) { $settings['breadcrumbs'] = !empty($settings['breadcrumbs_single_s']); - } - if (!array_key_exists('breadcrumbs_home', $settings) && array_key_exists('breadcrumbs_home_text', $settings)) { - $settings['breadcrumbs_home'] = (string) $settings['breadcrumbs_home_text']; } if (!array_key_exists('prevnext', $settings) && array_key_exists('prevnext_s', $settings)) { $settings['prevnext'] = !empty($settings['prevnext_s']); diff --git a/inc/helpers/aksrt-single-render.php b/inc/helpers/aksrt-single-render.php index 8c0765f7..d5253c3b 100644 --- a/inc/helpers/aksrt-single-render.php +++ b/inc/helpers/aksrt-single-render.php @@ -21,8 +21,11 @@ if (!function_exists('aksrt_single_render_cover')) : $type = (string) $ctx['cover_type']; $badge = (string) $ctx['status_badge']; $crumbs = (string) $ctx['breadcrumbs_html']; - $img_scale = max(10, min(100, (int) $ctx['img_scale'])); - $video_scale = max(20, min(100, (int) $ctx['video_scale'])); + // 参考主题 article_image_cover_option.scale:20-200(%),标题预留 50px。 + $img_scale = max(20, min(200, (int) $ctx['img_scale'])); + // 参考主题 article_video_cover_option.scale:0-200,0 = 不固定长宽比 → 16:9 回退。 + $video_scale = max(0, min(200, (int) $ctx['video_scale'])); + $video_padding = $video_scale > 0 ? (string) $video_scale : '56.25'; $slides = is_array($ctx['slide_images'] ?? null) ? $ctx['slide_images'] : []; $poster = (string) ($ctx['video_poster'] ?? ''); $title_html = (string) ($ctx['title_html'] ?? esc_html(get_the_title())); @@ -34,7 +37,7 @@ if (!function_exists('aksrt_single_render_cover')) : ?>
-
+
style="position:absolute;inset:0;width:100%;height:100%;">
'; - // 与单图封面同一高度公式:calc(比例% + 50px)。 - $slider_html = aksrt_render_slider([ + // 幻灯片配置(参考主题 CFS_Module::slide 全字段,经 get_post_page_settings 归一化)。 + $slide_option = is_array($ctx['settings']['article_slide_cover_option'] ?? null) + ? $ctx['settings']['article_slide_cover_option'] + : []; + $single_slide = count($slide_items) === 1; + $slider_args = [ 'slides' => $slide_items, - 'scale_height' => true, - 'scale_ratio' => $img_scale / 100, - 'scale_extra' => '50px', + 'effect' => (string) ($slide_option['effect'] ?? 'slide'), + 'direction' => (string) ($slide_option['direction'] ?? 'horizontal'), + 'spaceBetween' => (int) ($slide_option['spacebetween'] ?? 15), + 'speed' => (int) ($slide_option['speed'] ?? 0), + 'loop' => !$single_slide && !empty($slide_option['loop']), + 'autoplay' => !$single_slide && !empty($slide_option['autoplay']), + 'interval' => max(1, (int) ($slide_option['interval'] ?? 4)) * 1000, + 'show_nav' => !empty($slide_option['button']), + 'show_pagination' => !empty($slide_option['pagination']), 'wrapper_class' => 'mb20 single-cover-slide imgbox-container', 'slider_class' => 'single-cover single-cover--slide', - 'show_nav' => true, - 'show_pagination' => true, - 'loop' => count($slide_items) > 1, - 'autoplay' => count($slide_items) > 1, 'overlay_html' => $overlay, - ]); + ]; + if (!empty($slide_option['scale_height'])) { + // 固定高度模式:PC/移动端分别取 px 高度。 + $slider_args['scale_height'] = false; + $slider_args['pc_height'] = ((int) ($slide_option['pc_height'] ?? 380)) . 'px'; + $slider_args['m_height'] = ((int) ($slide_option['m_height'] ?? 180)) . 'px'; + } else { + // 比例高度模式:scale% + 50px(标题预留,与单图封面同公式)。 + $slider_args['scale_height'] = true; + $slider_args['scale_ratio'] = max(10, (int) ($slide_option['scale'] ?? 35)) / 100; + $slider_args['scale_extra'] = '50px'; + } + $slider_html = aksrt_render_slider($slider_args); ?>
@@ -633,7 +654,7 @@ if (!function_exists('aksrt_single_render_article')) :
-
0 ? ' style="max-height:' . esc_attr((string) $maxheight) . 'px;"' : ''; ?> data-maxheight=" 0 ? max(0, $maxheight - 80) : 0)); ?>"> diff --git a/src/Admin/Schema/SchemaPostList.php b/src/Admin/Schema/SchemaPostList.php index f61080a1..574fcd54 100644 --- a/src/Admin/Schema/SchemaPostList.php +++ b/src/Admin/Schema/SchemaPostList.php @@ -46,83 +46,83 @@ class SchemaPostList { 'page' => [ 'key' => 'page', 'title' => '文章页面', - 'version' => '1.0.0', + 'version' => '2.0.0', 'option_key' => 'aksrt_settings', 'groups' => [ - ['key' => 'navigation', 'title' => '文章导航', 'fields' => [ - ['key' => 'post_reading_progress', 'label' => '阅读进度条', 'type' => 'switch', 'default' => true], - ['key' => 'breadcrumbs_single_s', 'label' => '面包屑导航', 'type' => 'switch', 'default' => true], - ['key' => 'breadcrumbs_home_text', 'label' => '面包屑首页文字', 'type' => 'input', 'default' => '首页', 'depends_on' => 'breadcrumbs_single_s'], - ['key' => 'breadcrumbs_single_text', 'label' => '末项显示「正文」', 'type' => 'switch', 'default' => true, 'depends_on' => 'breadcrumbs_single_s', 'description' => '关闭后末项显示文章标题'], - ['key' => 'post_prevnext_s', 'label' => '上一篇/下一篇', 'type' => 'switch', 'default' => true], - ['key' => 'post_authordesc_s', 'label' => '作者简介', 'type' => 'switch', 'default' => true], - ['key' => 'post_author_card_limit', 'label' => '作者卡文章数', 'type' => 'number', 'default' => 6, 'min' => 0, 'max' => 12], - ['key' => 'post_author_card_orderby', 'label' => '作者卡文章排序', 'type' => 'select', 'default' => 'views', 'options' => [ - ['label' => '浏览量', 'value' => 'views'], - ['label' => '发布时间', 'value' => 'date'], - ['label' => '更新时间', 'value' => 'modified'], - ['label' => '评论数', 'value' => 'comment_count'], + ['key' => 'post_page', 'title' => '文章页面', 'fields' => [ + ['key' => 'article_image_cover', 'label' => '图片封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置文章页顶部显示封面图'], + ['key' => 'article_image_cover_option', 'label' => ' ', 'type' => 'fieldset', 'default' => ['scale' => 35], 'dependsOn' => ['key' => 'article_image_cover', 'value' => true], 'description' => '图片封面配置', 'fields' => [ + ['key' => 'scale', 'label' => '图片长宽比例', 'type' => 'number', 'default' => 35, 'min' => 20, 'max' => 200, 'step' => 5, 'unit' => '%'], ]], - ['key' => 'post_author_card_style', 'label' => '作者卡文章样式', 'type' => 'radio', 'default' => 'card', 'options' => [ - ['label' => '卡片', 'value' => 'card'], + ['key' => 'article_slide_cover', 'label' => '幻灯片封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置幻灯片为文章封面'], + ['key' => 'article_slide_cover_option', 'label' => ' ', 'type' => 'fieldset', 'default' => [ + 'direction' => 'horizontal', 'loop' => true, 'button' => false, 'pagination' => true, 'effect' => 'slide', + 'scale_height' => true, 'scale' => 35, 'auto_height' => false, 'max_height' => 500, 'min_height' => 180, + 'pc_height' => 380, 'm_height' => 180, 'spacebetween' => 15, 'speed' => 0, 'autoplay' => true, 'interval' => 4, + ], 'dependsOn' => ['key' => 'article_slide_cover', 'value' => true], 'fields' => [ + ['key' => 'direction', 'label' => '幻灯片方向', 'type' => 'radio', 'default' => 'horizontal', 'options' => [ + ['label' => '左右切换', 'value' => 'horizontal'], + ['label' => '上下切换', 'value' => 'vertical'], + ]], + ['key' => 'loop', 'label' => '循环切换', 'type' => 'switch', 'default' => true], + ['key' => 'button', 'label' => '显示翻页按钮', 'type' => 'switch', 'default' => false], + ['key' => 'pagination', 'label' => '显示指示器', 'type' => 'switch', 'default' => true], + ['key' => 'effect', 'label' => '切换动画', 'type' => 'select', 'default' => 'slide', 'options' => [ + ['label' => '滑动', 'value' => 'slide'], + ['label' => '淡出淡入', 'value' => 'fade'], + ['label' => '3D方块', 'value' => 'cube'], + ['label' => '3D滑入', 'value' => 'coverflow'], + ['label' => '3D翻转', 'value' => 'flip'], + ]], + ['key' => 'scale_height', 'label' => '按比例自动高度', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'direction', 'not' => 'vertical']], + ['key' => 'scale', 'label' => '长宽比例', 'type' => 'number', 'default' => 35, 'min' => 10, 'max' => 300, 'step' => 5, 'unit' => '%', 'dependsOn' => ['all' => [['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => true]]]], + ['key' => 'auto_height', 'label' => '自动高度', 'type' => 'switch', 'default' => false, 'dependsOn' => ['all' => [['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => false]]]], + ['key' => 'max_height', 'label' => '最大高度', 'type' => 'number', 'default' => 500, 'min' => 120, 'max' => 800, 'step' => 20, 'unit' => 'PX', 'dependsOn' => ['all' => [['key' => 'auto_height', 'value' => true], ['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => false]]]], + ['key' => 'min_height', 'label' => '最小高度', 'type' => 'number', 'default' => 180, 'min' => 100, 'max' => 500, 'step' => 20, 'unit' => 'PX', 'dependsOn' => ['all' => [['key' => 'auto_height', 'value' => true], ['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => false]]]], + ['key' => 'pc_height', 'label' => '电脑端高度', 'type' => 'number', 'default' => 380, 'min' => 120, 'max' => 800, 'step' => 20, 'unit' => 'PX'], + ['key' => 'm_height', 'label' => '移动端高度', 'type' => 'number', 'default' => 180, 'min' => 100, 'max' => 500, 'step' => 20, 'unit' => 'PX'], + ['key' => 'spacebetween', 'label' => '幻灯片间距', 'type' => 'number', 'default' => 15, 'min' => 0, 'max' => 500, 'step' => 5, 'unit' => 'PX'], + ['key' => 'speed', 'label' => '切换速度', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 3000, 'step' => 100, 'unit' => '毫秒', 'description' => '切换过程的时间(越小越快),设置为0则为自动模式'], + ['key' => 'autoplay', 'label' => '自动播放', 'type' => 'switch', 'default' => true], + ['key' => 'interval', 'label' => '停顿时间', 'type' => 'number', 'default' => 4, 'min' => 0, 'max' => 20, 'step' => 1, 'unit' => '秒', 'dependsOn' => ['key' => 'autoplay', 'value' => true], 'description' => '自动切换的时间间隔(越小越快)'], + ]], + ['key' => 'article_video_cover', 'label' => '视频封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置视频为文章封面'], + ['key' => 'article_video_cover_option', 'label' => ' ', 'type' => 'fieldset', 'default' => ['scale' => 0], 'dependsOn' => ['key' => 'article_video_cover', 'value' => true], 'description' => '视频封面配置', 'fields' => [ + ['key' => 'scale', 'label' => '固定长宽比例', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 200, 'step' => 5, 'unit' => '%', 'description' => '为0则不固定长宽比例'], + ]], + ['key' => 'post_single_hide_time_s', 'label' => '隐藏时间', 'type' => 'switcher', 'default' => false, 'description' => '隐藏文章的更新及发布时间显示'], + ['key' => 'post_p_indent_s', 'label' => '内容段落缩进', 'type' => 'switcher', 'default' => false, 'description' => '开启后文章内容每一个段落首行将向右偏移2个文字距离'], + ['key' => 'breadcrumbs_single_s', 'label' => '面包屑导航', 'type' => 'switcher', 'default' => true], + ['key' => 'breadcrumbs_home_text', 'label' => '面包屑导航用“首页”替代网站名称', 'type' => 'switcher', 'default' => true, 'dependsOn' => ['key' => 'breadcrumbs_single_s', 'value' => true]], + ['key' => 'post_prevnext_s', 'label' => '上一页、下一页版块', 'type' => 'switcher', 'default' => true], + ['key' => 'post_authordesc_s', 'label' => '作者信息版块', 'type' => 'switcher', 'default' => true], + ['key' => 'article_maxheight_kg', 'label' => '内容高度限制', 'type' => 'switcher', 'default' => false, 'description' => '此处为全局设置,每篇文章可单独设置', 'help' => '全局开关'], + ['key' => 'article_maxheight', 'label' => '限制的最大高度', 'type' => 'slider', 'default' => 1000, 'min' => 600, 'max' => 3000, 'step' => 100, 'unit' => 'px', 'dependsOn' => ['key' => 'article_maxheight_kg', 'value' => true], 'description' => '开启后如果文章高度超过设定值则会显示展开阅读全文的按钮。每篇文章可单独开启此功能'], + ['key' => 'yiyan_single_content_header', 'label' => '文章内容头部显示一言', 'type' => 'switcher', 'default' => false], + ['key' => 'yiyan_single_content_footer', 'label' => '文章内容尾部显示一言', 'type' => 'switcher', 'default' => false], + ['key' => 'yiyan_single_box', 'label' => '文章页面下方独立一言版块', 'type' => 'switcher', 'default' => false], + ['key' => 'post_copyright_s', 'label' => '版权声明', 'type' => 'switcher', 'default' => true], + ['key' => 'post_copyright', 'label' => '版权提示内容', 'type' => 'textarea', 'default' => '文章版权归作者所有,未经允许请勿转载。', 'rows' => 2, 'dependsOn' => ['key' => 'post_copyright_s', 'value' => true], 'description' => '支持HTML代码,请注意代码规范及标签闭合'], + ['key' => 'post_button_toptext', 'label' => '文章页脚文案', 'type' => 'input', 'default' => '喜欢就支持一下吧', 'description' => '文章底部打赏、分享按钮上面的文字'], + ['key' => 'post_front_content', 'label' => '在文章内容前-插入内容', 'type' => 'textarea', 'default' => '', 'rows' => 3], + ['key' => 'post_after_content', 'label' => '在文章内容后-插入内容', 'type' => 'textarea', 'default' => '', 'rows' => 3, 'description' => '在每篇文章顶部和尾部插入内容,可以插入广告或者文章说明等内容。支持HTML代码,请注意代码规范及标签闭合'], + ['key' => 'post_related_s', 'label' => '相关文章版块', 'type' => 'switcher', 'default' => true], + ['key' => 'post_related_type', 'label' => '显示样式', 'type' => 'image_select', 'default' => 'img', 'dependsOn' => ['key' => 'post_related_s', 'value' => true], 'options' => [ + ['label' => '图文卡片', 'value' => 'img'], ['label' => '列表', 'value' => 'list'], + ['label' => '纯文字', 'value' => 'text'], + ]], + ['key' => 'related_title', 'label' => '版块标题', 'type' => 'input', 'default' => '相关推荐', 'dependsOn' => ['key' => 'post_related_s', 'value' => true]], + ['key' => 'post_related_n', 'label' => '显示数量', 'type' => 'number', 'default' => 6, 'min' => 4, 'max' => 12, 'step' => 2, 'unit' => '篇', 'dependsOn' => ['key' => 'post_related_s', 'value' => true]], + ['key' => 'post_related_orderby', 'label' => '排序方式', 'type' => 'select', 'default' => 'views', 'dependsOn' => ['key' => 'post_related_s', 'value' => true], 'options' => [ + ['label' => '更新时间', 'value' => 'modified'], + ['label' => '发布时间', 'value' => 'date'], + ['label' => '浏览量', 'value' => 'views'], + ['label' => '点赞量', 'value' => 'like'], + ['label' => '评论量', 'value' => 'comment_count'], + ['label' => '收藏数量', 'value' => 'favorite'], + ['label' => '随机', 'value' => 'rand'], ]], - ['key' => 'post_single_hide_time_s', 'label' => '隐藏文章时间', 'type' => 'switch', 'default' => false], - ]], - ['key' => 'cover', 'title' => '封面展示', 'fields' => [ - ['key' => 'post_article_image_cover', 'label' => '图片封面', 'type' => 'switch', 'default' => true, 'description' => '文章有特色图时自动展示封面'], - ['key' => 'post_article_image_cover_scale', 'label' => '封面高度比例(%)', 'type' => 'number', 'default' => 35, 'min' => 10, 'max' => 100], - ['key' => 'post_article_slide_cover', 'label' => '幻灯片封面', 'type' => 'switch', 'default' => true, 'description' => '文章有图集时以轮播形式展示封面'], - ['key' => 'post_article_video_cover', 'label' => '视频封面', 'type' => 'switch', 'default' => true, 'description' => '文章有视频时以视频播放器作为封面'], - ['key' => 'post_article_video_cover_scale', 'label' => '视频封面高度比例(%)', 'type' => 'number', 'default' => 56, 'min' => 20, 'max' => 100], - ]], - ['key' => 'related', 'title' => '相关推荐', 'fields' => [ - ['key' => 'post_related_s', 'label' => '显示相关推荐', 'type' => 'switch', 'default' => true], - ['key' => 'post_related_type', 'label' => '展示样式', 'type' => 'radio', 'default' => 'img', 'options' => [ - ['label' => '图文卡片', 'value' => 'img'], - ['label' => '列表', 'value' => 'list'], - ['label' => '纯文字', 'value' => 'text'], - ]], - ['key' => 'related_title', 'label' => '推荐标题', 'type' => 'input', 'default' => '相关推荐'], - ['key' => 'post_related_n', 'label' => '推荐数量', 'type' => 'number', 'default' => 6, 'min' => 4, 'max' => 12, 'step' => 2], - ['key' => 'post_related_orderby', 'label' => '推荐排序', 'type' => 'select', 'default' => 'views', 'options' => [ - ['label' => '更新时间', 'value' => 'modified'], - ['label' => '发布时间', 'value' => 'date'], - ['label' => '浏览量', 'value' => 'views'], - ['label' => '点赞量', 'value' => 'like'], - ['label' => '评论量', 'value' => 'comment_count'], - ['label' => '收藏数量', 'value' => 'favorite'], - ['label' => '随机', 'value' => 'rand'], - ]], - ]], - ['key' => 'interactions', 'title' => '互动功能', 'fields' => [ - ['key' => 'post_favorite_enabled', 'label' => '收藏', 'type' => 'switch', 'default' => true], - ['key' => 'share_s', 'label' => '分享按钮', 'type' => 'switch', 'default' => true], - ['key' => 'post_copyright_enabled', 'label' => '版权信息', 'type' => 'switch', 'default' => true], - ['key' => 'post_copyright_text', 'label' => '版权默认文案', 'type' => 'textarea', 'default' => '', 'description' => '留空则自动生成"本文由 XX 原创发布"'], - ['key' => 'post_wechat_share', 'label' => '微信分享', 'type' => 'switch', 'default' => false], - ['key' => 'post_rewards_s', 'label' => '用户打赏', 'type' => 'switch', 'default' => false], - ['key' => 'post_rewards_text', 'label' => '打赏按钮文案', 'type' => 'input', 'default' => '赞赏'], - ['key' => 'post_rewards_amounts', 'label' => '快捷金额', 'type' => 'input', 'default' => '5,10,20,50,100', 'description' => '英文逗号分隔,赞赏直接进入作者站内余额'], - ['key' => 'post_rewards_custom', 'label' => '允许自定义金额', 'type' => 'switch', 'default' => true], - ['key' => 'post_rewards_min', 'label' => '最低金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1.00', 'min' => 0.01, 'max' => 1000, 'step' => 0.01], - ['key' => 'post_rewards_max', 'label' => '最高金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1000.00', 'min' => 1, 'max' => 100000, 'step' => 0.01], - ['key' => 'post_button_toptext', 'label' => '按钮上方文案', 'type' => 'input', 'default' => '喜欢就支持一下吧'], - ]], - ['key' => 'content', 'title' => '内容处理', 'fields' => [ - ['key' => 'post_p_indent', 'label' => '段落缩进', 'type' => 'switch', 'default' => false], - ['key' => 'post_maxheight_kg', 'label' => '内容高度限制', 'type' => 'switch', 'default' => false], - ['key' => 'post_maxheight', 'label' => '最大高度', 'type' => 'number', 'default' => 1000, 'min' => 600, 'max' => 3000], - ['key' => 'post_front_content', 'label' => '文章头部内容', 'type' => 'textarea', 'default' => ''], - ['key' => 'post_after_content', 'label' => '文章底部内容', 'type' => 'textarea', 'default' => ''], - ]], - ['key' => 'yiyan', 'title' => '一言', 'fields' => [ - ['key' => 'post_yiyan_header', 'label' => '文章头部一言', 'type' => 'switch', 'default' => false], - ['key' => 'post_yiyan_footer', 'label' => '文章底部一言', 'type' => 'switch', 'default' => false], - ['key' => 'post_yiyan_box', 'label' => '一言盒子', 'type' => 'switch', 'default' => false], - ]], - ['key' => 'meta', 'title' => 'Meta设置', 'fields' => [ - ['key' => 'post_views_s', 'label' => '浏览统计', 'type' => 'switch', 'default' => true], ]], ], ], @@ -286,7 +286,10 @@ class SchemaPostList { 'version' => '1.0.0', 'option_key' => 'aksrt_settings', 'groups' => [ - ['key' => 'basic', 'title' => '分享设置', 'fields' => [ + ['key' => 'basic', 'title' => '分享按钮', 'fields' => [ + ['key' => 'share_s', 'label' => '分享按钮', 'type' => 'switcher', 'default' => true, 'help' => '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响'], + ]], + ['key' => 'wechat', 'title' => '微信分享', 'fields' => [ ['key' => 'share_wechat_s', 'label' => '微信分享', 'type' => 'switch', 'default' => false], ['key' => 'share_wechat_appid', 'label' => 'AppID', 'type' => 'input', 'default' => ''], ['key' => 'share_wechat_app_secret', 'label' => 'AppSecret', 'type' => 'input', 'default' => ''], diff --git a/src/Admin/Schema/SchemaUsers.php b/src/Admin/Schema/SchemaUsers.php index d21604fc..5216401a 100644 --- a/src/Admin/Schema/SchemaUsers.php +++ b/src/Admin/Schema/SchemaUsers.php @@ -525,6 +525,14 @@ class SchemaUsers { ['key' => 'user_cover_img', 'label' => '用户默认封面', 'type' => 'media', 'default' => get_theme_file_uri('assets/img/user_t.jpg'), 'help' => '建议尺寸 1000×400;用户未设置封面时显示此图。'], ['key' => 'user_desc_std', 'label' => '用户默认签名', 'type' => 'input', 'default' => '这家伙很懒,什么都没有写...', 'help' => '用户未设置个人简介时显示的默认文案。'], ]], + ['key' => 'rewards', 'title' => '打赏', 'fields' => [ + ['key' => 'post_rewards_s', 'label' => '用户打赏功能', 'type' => 'switcher', 'default' => true, 'help' => '开启后,登录用户可使用站内余额赞赏作者,金额直接进入作者余额。'], + ['key' => 'post_rewards_text', 'label' => '打赏按钮文案', 'type' => 'input', 'default' => '赞赏', 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true], 'help' => '自定义打赏按钮文字'], + ['key' => 'post_rewards_amounts', 'label' => '快捷金额', 'type' => 'input', 'default' => '5,10,20,50,100', 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true], 'help' => '使用英文逗号分隔'], + ['key' => 'post_rewards_custom', 'label' => '允许自定义金额', 'type' => 'switcher', 'default' => true, 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true]], + ['key' => 'post_rewards_min', 'label' => '最低金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1.00', 'min' => 0.01, 'max' => 1000, 'step' => 0.01, 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true]], + ['key' => 'post_rewards_max', 'label' => '最高金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1000.00', 'min' => 1, 'max' => 100000, 'step' => 0.01, 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true]], + ]], ], ], 'verify' => [ diff --git a/src/Core/PostMetaBox.php b/src/Core/PostMetaBox.php index 2d6dfd21..ee26ae85 100644 --- a/src/Core/PostMetaBox.php +++ b/src/Core/PostMetaBox.php @@ -109,13 +109,13 @@ class PostMetaBox { public function render_meta_box(\WP_Post $post): void { $meta = self::get_meta($post->ID); $settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : []; - $show_image = !array_key_exists('post_article_image_cover', $settings) || !empty($settings['post_article_image_cover']); + $show_image = !array_key_exists('article_image_cover', $settings) || !empty($settings['article_image_cover']); $show_slide = !array_key_exists('list_thumb_slides_s', $settings) || !empty($settings['list_thumb_slides_s']) - || !empty($settings['post_article_slide_cover']); + || !empty($settings['article_slide_cover']); $show_video = !array_key_exists('list_thumb_video_s', $settings) || !empty($settings['list_thumb_video_s']) - || !empty($settings['post_article_video_cover']); + || !empty($settings['article_video_cover']); wp_nonce_field('aksrt_post_extension_save', 'aksrt_post_extension_nonce'); ?> diff --git a/src/Settings/FacadeBulk.php b/src/Settings/FacadeBulk.php index 7ab850b0..003c620b 100644 --- a/src/Settings/FacadeBulk.php +++ b/src/Settings/FacadeBulk.php @@ -165,10 +165,20 @@ trait FacadeBulk { } $settings = self::get_all(); + // 参考主题语义:switcher 开 = 面包屑用“首页”替代网站名称。 + $breadcrumbs_home_replace = !isset($settings['breadcrumbs_home_text']) || !empty($settings['breadcrumbs_home_text']); return [ - 'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']), - 'breadcrumbs' => !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']), - 'breadcrumbs_home' => trim((string) ($settings['breadcrumbs_home_text'] ?? '')) ?: __('首页', 'aksrt'), + 'reading_progress' => true, + 'breadcrumbs' => !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']), + 'breadcrumbs_home' => $breadcrumbs_home_replace ? __('首页', 'aksrt') : (string) get_bloginfo('name'), + 'breadcrumbs_home_replace' => $breadcrumbs_home_replace, + 'p_indent' => !empty($settings['post_p_indent_s']), + 'article_image_cover' => !isset($settings['article_image_cover']) || !empty($settings['article_image_cover']), + 'article_image_cover_scale' => 35, + 'article_slide_cover' => !isset($settings['article_slide_cover']) || !empty($settings['article_slide_cover']), + 'article_slide_cover_option'=> [], + 'article_video_cover' => !isset($settings['article_video_cover']) || !empty($settings['article_video_cover']), + 'article_video_cover_scale' => 0, 'related_enabled' => !isset($settings['post_related_s']) || !empty($settings['post_related_s']), 'related_count' => max(4, min(12, (int) ($settings['post_related_n'] ?? 6))), 'related_style' => in_array(($settings['post_related_type'] ?? 'img'), ['img', 'list', 'text'], true) ? (string) $settings['post_related_type'] : 'img', diff --git a/src/Settings/PostDisplay.php b/src/Settings/PostDisplay.php index 829517f9..00081a4e 100644 --- a/src/Settings/PostDisplay.php +++ b/src/Settings/PostDisplay.php @@ -152,9 +152,9 @@ class PostDisplay { // Canonical render keys (used by aksrt_single_*). Legacy *_s aliases kept for older callers. $breadcrumbs = !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']); - $breadcrumbs_home = sanitize_text_field((string) ($settings['breadcrumbs_home_text'] ?? '首页')); - // legacy breadcrumbs_single_text: true → last crumb is "正文"; false → full title. - $breadcrumbs_single_text = !isset($settings['breadcrumbs_single_text']) || !empty($settings['breadcrumbs_single_text']); + // 参考主题语义:switcher 开 = 面包屑用“首页”替代网站名称。 + $breadcrumbs_home_replace = !isset($settings['breadcrumbs_home_text']) || !empty($settings['breadcrumbs_home_text']); + $breadcrumbs_home = $breadcrumbs_home_replace ? __('首页', 'aksrt') : (string) get_bloginfo('name'); $prevnext = !isset($settings['post_prevnext_s']) || !empty($settings['post_prevnext_s']); $authordesc = !isset($settings['post_authordesc_s']) || !empty($settings['post_authordesc_s']); $hide_time = !empty($settings['post_single_hide_time_s']); @@ -195,23 +195,22 @@ class PostDisplay { } $rewards_min = $rewards_min_amount->decimal(); $rewards_max = $rewards_max_amount->decimal(); - $author_card_limit = max(0, min(12, (int) ($settings['post_author_card_limit'] ?? 6))); - $author_card_orderby = sanitize_key((string) ($settings['post_author_card_orderby'] ?? 'views')); - if (!in_array($author_card_orderby, ['views', 'date', 'modified', 'comment_count'], true)) { - $author_card_orderby = 'views'; - } - $author_card_style = sanitize_key((string) ($settings['post_author_card_style'] ?? 'card')); - if (!in_array($author_card_style, ['card', 'list'], true)) { - $author_card_style = 'card'; - } - $maxheight_enabled = !empty($settings['article_maxheight_kg']) - || !empty($settings['post_article_maxheight_kg']) - || !empty($settings['post_maxheight_kg']); - $maxheight = max(0, (int) ($settings['article_maxheight'] ?? $settings['post_article_maxheight'] ?? $settings['post_maxheight'] ?? 1000)); + // 内容高度限制(参考键 article_maxheight_kg / article_maxheight)。 + $maxheight_enabled = !empty($settings['article_maxheight_kg']); + $maxheight = max(0, (int) ($settings['article_maxheight'] ?? 1000)); if ($maxheight <= 0) { $maxheight = 1000; } + // 封面(参考键 article_*_cover + option fieldset)。 + $image_cover_option = is_array($settings['article_image_cover_option'] ?? null) ? $settings['article_image_cover_option'] : []; + $image_cover_scale = max(20, min(200, (int) ($image_cover_option['scale'] ?? 35))); + $video_cover_option = is_array($settings['article_video_cover_option'] ?? null) ? $settings['article_video_cover_option'] : []; + $video_cover_scale = max(0, min(200, (int) ($video_cover_option['scale'] ?? 0))); + + $slide_option_raw = is_array($settings['article_slide_cover_option'] ?? null) ? $settings['article_slide_cover_option'] : []; + $slide_option = self::normalize_slide_cover_option($slide_option_raw); + // 图片灯箱(参考主题 imagelightbox*):pc_s/m_s 复选框归并为单一开关。 $lightbox_type = sanitize_key((string) ($settings['imagelightbox_type'] ?? 'group')); if (!in_array($lightbox_type, ['group', 'alone'], true)) { @@ -245,24 +244,28 @@ class PostDisplay { } return [ - 'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']), + // 参考主题无阅读进度条配置:功能常开(字段已随 1:1 复刻移除)。 + 'reading_progress' => true, // Canonical + legacy breadcrumbs. 'breadcrumbs' => $breadcrumbs, 'breadcrumbs_home' => $breadcrumbs_home, + 'breadcrumbs_home_replace' => $breadcrumbs_home_replace, 'breadcrumbs_single_s' => $breadcrumbs, - 'breadcrumbs_home_text' => $breadcrumbs_home, - 'breadcrumbs_single_text' => $breadcrumbs_single_text, + 'breadcrumbs_home_text' => $breadcrumbs_home_replace, + 'breadcrumbs_single_text' => true, // Canonical + legacy prev/next / time; canonical author card flag. 'prevnext' => $prevnext, 'prevnext_s' => $prevnext, 'authordesc' => $authordesc, 'hide_time' => $hide_time, 'single_hide_time_s' => $hide_time, - 'article_image_cover' => !isset($settings['post_article_image_cover']) || !empty($settings['post_article_image_cover']), - 'article_image_cover_scale'=> max(10, min(100, (int) ($settings['post_article_image_cover_scale'] ?? 35))), - 'article_slide_cover' => !isset($settings['post_article_slide_cover']) || !empty($settings['post_article_slide_cover']), - 'article_video_cover' => !isset($settings['post_article_video_cover']) || !empty($settings['post_article_video_cover']), - 'article_video_cover_scale'=> max(20, min(100, (int) ($settings['post_article_video_cover_scale'] ?? 56))), + 'p_indent' => !empty($settings['post_p_indent_s']), + 'article_image_cover' => !isset($settings['article_image_cover']) || !empty($settings['article_image_cover']), + 'article_image_cover_scale'=> $image_cover_scale, + 'article_slide_cover' => !isset($settings['article_slide_cover']) || !empty($settings['article_slide_cover']), + 'article_slide_cover_option' => $slide_option, + 'article_video_cover' => !isset($settings['article_video_cover']) || !empty($settings['article_video_cover']), + 'article_video_cover_scale'=> $video_cover_scale, 'lightbox' => !isset($settings['imagelightbox']) || !empty($settings['imagelightbox']), 'lightbox_type' => $lightbox_type, 'lightbox_thumbs' => $lightbox_thumbs, @@ -276,15 +279,16 @@ class PostDisplay { 'related_orderby' => $related_orderby, 'related_title' => sanitize_text_field((string) ($settings['related_title'] ?? '相关推荐')), 'like_enabled' => !isset($settings['post_like_s']) || !empty($settings['post_like_s']), - 'favorite_enabled' => !isset($settings['post_favorite_enabled']) || !empty($settings['post_favorite_enabled']), + // 参考主题无收藏开关:收藏按钮常显(字段已随 1:1 复刻移除)。 + 'favorite_enabled' => true, // Canonical + legacy share. 'share_enabled' => $share_enabled, 'share_s' => $share_enabled, - // Author card posts (legacy user_card_box defaults). - 'author_card_limit' => $author_card_limit, - 'author_card_orderby' => $author_card_orderby, - 'author_card_style' => $author_card_style, - // On-site wallet tipping. + // Author card posts(参考主题无独立配置:保持默认)。 + 'author_card_limit' => 6, + 'author_card_orderby' => 'views', + 'author_card_style' => 'card', + // On-site wallet tipping(配置位于用户功能页,参考主题同位置)。 'rewards_enabled' => $rewards_enabled, 'post_rewards_s' => $rewards_enabled, 'rewards_text' => $rewards_text, @@ -296,14 +300,14 @@ class PostDisplay { // Content max height (legacy article_maxheight_kg / article_maxheight). 'maxheight_enabled' => $maxheight_enabled, 'maxheight' => $maxheight, - 'copyright_enabled' => !isset($settings['post_copyright_enabled']) || !empty($settings['post_copyright_enabled']), - 'copyright_text' => (string) ($settings['post_copyright_text'] ?? ''), + 'copyright_enabled' => !isset($settings['post_copyright_s']) || !empty($settings['post_copyright_s']), + 'copyright_text' => (string) ($settings['post_copyright'] ?? '文章版权归作者所有,未经允许请勿转载。'), 'button_toptext' => sanitize_text_field((string) ($settings['post_button_toptext'] ?? '喜欢就支持一下吧')), 'front_content' => (string) ($settings['post_front_content'] ?? ''), 'after_content' => (string) ($settings['post_after_content'] ?? ''), - 'yiyan_header' => !empty($settings['post_yiyan_header']), - 'yiyan_footer' => !empty($settings['post_yiyan_footer']), - 'yiyan_box' => !empty($settings['post_yiyan_box']), + 'yiyan_header' => !empty($settings['yiyan_single_content_header']), + 'yiyan_footer' => !empty($settings['yiyan_single_content_footer']), + 'yiyan_box' => !empty($settings['yiyan_single_box']), 'default_views_min' => max(0, min(9999, (int) ($views_range['min'] ?? 20))), 'default_views_max' => max(1, min(9999, (int) ($views_range['max'] ?? 50))), 'default_likes_min' => max(0, min(9999, (int) ($like_range['min'] ?? 5))), @@ -313,6 +317,35 @@ class PostDisplay { ]; } + /** + * 幻灯片封面配置归一化(参考主题 CFS_Module::slide 全字段)。 + */ + private static function normalize_slide_cover_option(array $raw): array { + $effect = sanitize_key((string) ($raw['effect'] ?? 'slide')); + if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) { + $effect = 'slide'; + } + $direction = sanitize_key((string) ($raw['direction'] ?? 'horizontal')) === 'vertical' ? 'vertical' : 'horizontal'; + return [ + 'direction' => $direction, + 'loop' => !empty($raw['loop']), + 'button' => !empty($raw['button']), + 'pagination' => !empty($raw['pagination']), + 'effect' => $effect, + 'scale_height' => !empty($raw['scale_height']), + 'scale' => max(10, min(300, (int) ($raw['scale'] ?? 35))), + 'auto_height' => !empty($raw['auto_height']), + 'max_height' => max(120, min(800, (int) ($raw['max_height'] ?? 500))), + 'min_height' => max(100, min(500, (int) ($raw['min_height'] ?? 180))), + 'pc_height' => max(120, min(800, (int) ($raw['pc_height'] ?? 380))), + 'm_height' => max(100, min(500, (int) ($raw['m_height'] ?? 180))), + 'spacebetween' => max(0, min(500, (int) ($raw['spacebetween'] ?? 15))), + 'speed' => max(0, min(3000, (int) ($raw['speed'] ?? 0))), + 'autoplay' => !empty($raw['autoplay']), + 'interval' => max(0, min(20, (int) ($raw['interval'] ?? 4))), + ]; + } + public static function get_share_settings(): array { $settings = Settings::get_all(); return [ diff --git a/tests/admin/post-page-settings-reference-contract.test.cjs b/tests/admin/post-page-settings-reference-contract.test.cjs new file mode 100644 index 00000000..2fb405f0 --- /dev/null +++ b/tests/admin/post-page-settings-reference-contract.test.cjs @@ -0,0 +1,341 @@ +'use strict'; + +/** + * 「文章页面」后台设置页契约(对齐参考主题 zibll 1:1): + * 1) PHP schema(SchemaPostList.php page section 2.0.0)字段键、顺序、默认值与 + * JS schema(assets/admin/src/schema/sections/post-list/page.js)一致; + * 2) 参考主题字段全集存在:三类封面(article_image_cover / article_slide_cover / + * article_video_cover + option fieldset)、post_single_hide_time_s、 + * post_p_indent_s、breadcrumbs_single_s、breadcrumbs_home_text(switcher)、 + * post_prevnext_s、post_authordesc_s、article_maxheight_kg/maxheight、 + * 一言三项、post_copyright_s/post_copyright、post_button_toptext、 + * post_front_content/after_content、post_related_*; + * 3) 幻灯片 fieldset 全字段及嵌套依赖(direction/scale_height/auto_height 组合) + * 对齐参考主题 CFS_Module::slide; + * 4) 旧 AKSRT 私有/异名字段(阅读进度条、面包屑末项文本、作者卡片配置、收藏开关、 + * share_s 与 post_rewards_*、旧封面键)已彻底移除,无双路兼容; + * 5) 运行时(PostDisplay::get_post_page_settings)只读取新键:幻灯片配置归一化并 + * 接线 aksrt_render_slider,p_indent 接线容器类,breadcrumbs_home_text 为 + * switcher 布尔语义(true = 用「首页」替代网站名称)。 + */ + +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const test = require('node:test'); + +const root = path.resolve(__dirname, '..', '..'); +const php = fs.readFileSync(path.join(root, 'src/Admin/Schema/SchemaPostList.php'), 'utf8'); +const js = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/post-list/page.js'), 'utf8'); +const runtime = fs.readFileSync(path.join(root, 'src/Settings/PostDisplay.php'), 'utf8'); +const facadeBulk = fs.readFileSync(path.join(root, 'src/Settings/FacadeBulk.php'), 'utf8'); +const metaBox = fs.readFileSync(path.join(root, 'src/Core/PostMetaBox.php'), 'utf8'); +const frontendTags = fs.readFileSync(path.join(root, 'inc/helpers/aksrt-frontend-tags.php'), 'utf8'); +const singleRender = fs.readFileSync(path.join(root, 'inc/helpers/aksrt-single-render.php'), 'utf8'); +const contentCss = fs.readFileSync(path.join(root, 'assets/css/frontend/single-post/content.css'), 'utf8'); +const shareJs = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/post-list/share.js'), 'utf8'); +const sharePhp = php; +const featuresJs = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/users/features.js'), 'utf8'); +const usersPhp = fs.readFileSync(path.join(root, 'src/Admin/Schema/SchemaUsers.php'), 'utf8'); + +/** Extract a PHP section body by its section key (balanced brackets). */ +function phpSection(src, sectionKey) { + const start = src.indexOf(`'${sectionKey}' => [`); + assert.notEqual(start, -1, `PHP schema has a ${sectionKey} section`); + const open = src.indexOf('[', start); + let depth = 0; + for (let i = open; i < src.length; i++) { + const ch = src[i]; + if (ch === "'" || ch === '"') { + const quote = ch; + i++; + while (i < src.length) { + if (src[i] === '\\') { i += 2; continue; } + if (src[i] === quote) break; + i++; + } + continue; + } + if (ch === '[') depth++; + else if (ch === ']') { + depth--; + if (depth === 0) return src.slice(open, i + 1); + } + } + throw new Error(`unbalanced ${sectionKey} section`); +} + +const pagePhp = phpSection(php, 'page'); + +// 参考主题 admin-options.php 4934–5249 的字段顺序(含 fieldset 父字段与子字段)。 +const PAGE_FIELD_SEQUENCE = [ + 'article_image_cover', + 'article_image_cover_option', + 'scale', // article_image_cover_option.scale + 'article_slide_cover', + 'article_slide_cover_option', + // article_slide_cover_option(CFS_Module::slide 全字段) + 'direction', 'loop', 'button', 'pagination', 'effect', 'scale_height', 'scale', + 'auto_height', 'max_height', 'min_height', 'pc_height', 'm_height', + 'spacebetween', 'speed', 'autoplay', 'interval', + 'article_video_cover', + 'article_video_cover_option', + 'scale', // article_video_cover_option.scale + 'post_single_hide_time_s', + 'post_p_indent_s', + 'breadcrumbs_single_s', + 'breadcrumbs_home_text', + 'post_prevnext_s', + 'post_authordesc_s', + 'article_maxheight_kg', + 'article_maxheight', + 'yiyan_single_content_header', + 'yiyan_single_content_footer', + 'yiyan_single_box', + 'post_copyright_s', + 'post_copyright', + 'post_button_toptext', + 'post_front_content', + 'post_after_content', + 'post_related_s', + 'post_related_type', + 'related_title', + 'post_related_n', + 'post_related_orderby', +]; + +// 旧 AKSRT 私有/异名字段:已迁移(share_s / post_rewards_*)或无参考对应项被移除。 +const LEGACY_KEYS = [ + 'post_reading_progress', + 'post_author_card_limit', + 'post_author_card_orderby', + 'post_author_card_style', + 'post_favorite_enabled', + 'post_wechat_share', + 'post_article_image_cover', + 'post_article_slide_cover', + 'post_article_video_cover', +]; + +test('PHP page section 字段键与参考主题一致且顺序相同', () => { + // 字段定义均为 ['key' => '...', 'label' => ...] 开头;dependsOn 内的 + // ['key' => '...', 'value'/'not' => ...] 不是字段定义,须排除。 + const found = []; + const re = /'key'\s*=>\s*'([a-z0-9_]+)',\s*'label'/g; + let m; + while ((m = re.exec(pagePhp)) !== null) found.push(m[1]); + assert.deepEqual(found, PAGE_FIELD_SEQUENCE); +}); + +test('PHP page section 版本升级至 2.0.0 且为单一 post_page 组', () => { + assert.match(pagePhp, /'version'\s*=>\s*'2\.0\.0'/); + assert.match(pagePhp, /'groups'\s*=>\s*\[\s*\['key'\s*=>\s*'post_page',\s*'title'\s*=>\s*'文章页面'/); +}); + +test('PHP page section 默认值对齐参考主题', () => { + // 三类封面开关。 + assert.match(pagePhp, /'article_image_cover',\s*'label'\s*=>\s*'图片封面',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/); + assert.match(pagePhp, /'article_slide_cover',\s*'label'\s*=>\s*'幻灯片封面',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/); + assert.match(pagePhp, /'article_video_cover',\s*'label'\s*=>\s*'视频封面',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/); + // 图片封面比例:20-200 step5,默认 35。 + assert.match(pagePhp, /'scale',\s*'label'\s*=>\s*'图片长宽比例',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*35,\s*'min'\s*=>\s*20,\s*'max'\s*=>\s*200,\s*'step'\s*=>\s*5/); + // 视频封面比例:0-200,默认 0(为 0 则不固定长宽比例)。 + assert.match(pagePhp, /'scale',\s*'label'\s*=>\s*'固定长宽比例',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*0,\s*'min'\s*=>\s*0,\s*'max'\s*=>\s*200/); + // 面包屑「首页」替代开关(switcher,默认 true)。 + assert.match(pagePhp, /'breadcrumbs_home_text',\s*'label'\s*=>\s*'面包屑导航用“首页”替代网站名称',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/); + // 段落缩进 / 隐藏时间。 + assert.match(pagePhp, /'post_p_indent_s',\s*'label'\s*=>\s*'内容段落缩进',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/); + assert.match(pagePhp, /'post_single_hide_time_s',\s*'label'\s*=>\s*'隐藏时间',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/); + // 内容高度限制:slider 600-3000 step100。 + assert.match(pagePhp, /'article_maxheight_kg',\s*'label'\s*=>\s*'内容高度限制',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/); + assert.match(pagePhp, /'article_maxheight',\s*'label'\s*=>\s*'限制的最大高度',\s*'type'\s*=>\s*'slider',\s*'default'\s*=>\s*1000,\s*'min'\s*=>\s*600,\s*'max'\s*=>\s*3000,\s*'step'\s*=>\s*100/); + // 版权声明。 + assert.match(pagePhp, /'post_copyright_s',\s*'label'\s*=>\s*'版权声明',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/); + assert.match(pagePhp, /'post_copyright',\s*'label'\s*=>\s*'版权提示内容',\s*'type'\s*=>\s*'textarea',\s*'default'\s*=>\s*'文章版权归作者所有,未经允许请勿转载。'/); + assert.match(pagePhp, /'post_button_toptext',\s*'label'\s*=>\s*'文章页脚文案',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'喜欢就支持一下吧'/); + // 相关文章:image_select 三样式 + 数量 4-12 step2 + 排序默认 views。 + assert.match(pagePhp, /'post_related_type',\s*'label'\s*=>\s*'显示样式',\s*'type'\s*=>\s*'image_select',\s*'default'\s*=>\s*'img'/); + assert.match(pagePhp, /'post_related_n',\s*'label'\s*=>\s*'显示数量',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*6,\s*'min'\s*=>\s*4,\s*'max'\s*=>\s*12,\s*'step'\s*=>\s*2/); + assert.match(pagePhp, /'post_related_orderby',\s*'label'\s*=>\s*'排序方式',\s*'type'\s*=>\s*'select',\s*'default'\s*=>\s*'views'/); +}); + +test('PHP slide fieldset 全字段及嵌套依赖对齐 CFS_Module::slide', () => { + // 幻灯片默认值(pagination: true, pc_height: 380, m_height: 180 为参考主题默认)。 + assert.match(pagePhp, /'direction'\s*=>\s*'horizontal',\s*'loop'\s*=>\s*true,\s*'button'\s*=>\s*false,\s*'pagination'\s*=>\s*true,\s*'effect'\s*=>\s*'slide'/); + assert.match(pagePhp, /'pc_height'\s*=>\s*380,\s*'m_height'\s*=>\s*180,\s*'spacebetween'\s*=>\s*15,\s*'speed'\s*=>\s*0,\s*'autoplay'\s*=>\s*true,\s*'interval'\s*=>\s*4/); + // 嵌套依赖:scale_height 需要 direction != vertical。 + assert.match(pagePhp, /'scale_height',\s*'label'\s*=>\s*'按比例自动高度',\s*'type'\s*=>\s*'switch',\s*'default'\s*=>\s*true,\s*'dependsOn'\s*=>\s*\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\]/); + // scale 需要 direction != vertical 且 scale_height 开启。 + assert.match(pagePhp, /'scale',\s*'label'\s*=>\s*'长宽比例'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\],\s*\['key'\s*=>\s*'scale_height',\s*'value'\s*=>\s*true\]\]\]/); + // auto_height 需要 direction != vertical 且 scale_height 关闭。 + assert.match(pagePhp, /'auto_height',\s*'label'\s*=>\s*'自动高度'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\],\s*\['key'\s*=>\s*'scale_height',\s*'value'\s*=>\s*false\]\]\]/); + // max/min_height 需要 auto_height 开启 + direction != vertical + scale_height 关闭。 + assert.match(pagePhp, /'max_height',\s*'label'\s*=>\s*'最大高度'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'auto_height',\s*'value'\s*=>\s*true\],\s*\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\],\s*\['key'\s*=>\s*'scale_height',\s*'value'\s*=>\s*false\]\]\]/); + // interval 依赖 autoplay。 + assert.match(pagePhp, /'interval',\s*'label'\s*=>\s*'停顿时间'[\s\S]*?'dependsOn'\s*=>\s*\['key'\s*=>\s*'autoplay',\s*'value'\s*=>\s*true\]/); + // 五种切换动画。 + for (const effect of ['slide', 'fade', 'cube', 'coverflow', 'flip']) { + assert.ok(pagePhp.includes(`'value' => '${effect}'`), `缺少切换动画 ${effect}`); + } +}); + +test('PHP page section 不含旧私有/异名字段(无双路)', () => { + for (const legacy of LEGACY_KEYS) { + assert.ok(!pagePhp.includes(`'${legacy}'`), `page section 残留旧字段: ${legacy}`); + } + // share_s / post_rewards_* 已迁移至分享设置页与用户功能页。 + assert.ok(!pagePhp.includes(`'share_s'`), 'page section 残留 share_s'); + assert.ok(!pagePhp.includes(`'post_rewards_`), 'page section 残留 post_rewards_*'); +}); + +test('JS schema 与 PHP 字段键一致(ui_only 一言行除外)', () => { + const keys = []; + const re = /key:\s*'([a-z0-9_]+)',\s*label:/g; + let m; + while ((m = re.exec(js)) !== null) keys.push(m[1]); + assert.deepEqual(keys, PAGE_FIELD_SEQUENCE); + for (const legacy of LEGACY_KEYS) { + assert.ok(!keys.includes(legacy), `JS schema 残留旧字段: ${legacy}`); + } + assert.ok(!js.includes(`key: 'share_s'`), 'JS page schema 残留 share_s'); + assert.ok(!/key:\s*'post_rewards_/.test(js), 'JS page schema 残留 post_rewards_*'); + // 参考主题视频封面配置教程链接以 ui_only description 承载。 + assert.match(js, /ui_only:\s*true/); + assert.match(js, /https:\/\/www\.zibll\.com\/1900\.html/); +}); + +test('JS schema 默认值与依赖对齐参考主题', () => { + assert.match(js, /article_image_cover:\s*true/); + assert.match(js, /article_image_cover_option:\s*\{\s*scale:\s*35\s*\}/); + assert.match(js, /article_slide_cover:\s*true/); + assert.match(js, /article_video_cover:\s*true/); + assert.match(js, /article_video_cover_option:\s*\{\s*scale:\s*0\s*\}/); + assert.match(js, /post_single_hide_time_s:\s*false/); + assert.match(js, /post_p_indent_s:\s*false/); + assert.match(js, /breadcrumbs_single_s:\s*true/); + assert.match(js, /breadcrumbs_home_text:\s*true/); + assert.match(js, /post_prevnext_s:\s*true/); + assert.match(js, /post_authordesc_s:\s*true/); + assert.match(js, /article_maxheight_kg:\s*false/); + assert.match(js, /article_maxheight:\s*1000/); + assert.match(js, /post_copyright_s:\s*true/); + assert.match(js, /post_copyright:\s*'文章版权归作者所有,未经允许请勿转载。'/); + assert.match(js, /post_button_toptext:\s*'喜欢就支持一下吧'/); + assert.match(js, /post_related_s:\s*true/); + assert.match(js, /post_related_type:\s*'img'/); + assert.match(js, /related_title:\s*'相关推荐'/); + assert.match(js, /post_related_n:\s*6/); + assert.match(js, /post_related_orderby:\s*'views'/); + // 幻灯片嵌套依赖(dependsOn {key, not} / {all: [...]} 形式)。 + assert.match(js, /key:\s*'scale_height',[\s\S]*?dependsOn:\s*\{\s*key:\s*'direction',\s*not:\s*'vertical'\s*\}/); + assert.match(js, /key:\s*'scale',[\s\S]*?dependsOn:\s*\{\s*all:\s*\[\{\s*key:\s*'direction',\s*not:\s*'vertical'\s*\},\s*\{\s*key:\s*'scale_height',\s*value:\s*true\s*\}\]\s*\}/); + assert.match(js, /key:\s*'auto_height',[\s\S]*?dependsOn:\s*\{\s*all:\s*\[\{\s*key:\s*'direction',\s*not:\s*'vertical'\s*\},\s*\{\s*key:\s*'scale_height',\s*value:\s*false\s*\}\]\s*\}/); + assert.match(js, /key:\s*'interval',[\s\S]*?dependsOn:\s*\{\s*key:\s*'autoplay',\s*value:\s*true\s*\}/); +}); + +test('share_s 迁移至分享设置页 basic 组(键不变,无双路)', () => { + assert.match(sharePhp, /\['key' => 'basic', 'title' => '分享按钮', 'fields' => \[\s*\['key' => 'share_s', 'label' => '分享按钮', 'type' => 'switcher', 'default' => true, 'help' => '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响'\]/); + assert.match(shareJs, /key: 'basic',/); + assert.match(shareJs, /share_s: true/); + assert.match(shareJs, /key: 'share_s', label: '分享按钮', type: 'switch', default: true, help: '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响'/); +}); + +test('post_rewards_* 迁移至用户功能页 rewards 组(对齐参考主题 8977 行)', () => { + assert.match(usersPhp, /\['key' => 'rewards', 'title' => '打赏', 'fields' => \[/); + assert.match(usersPhp, /'post_rewards_s', 'label' => '用户打赏功能', 'type' => 'switcher', 'default' => true/); + assert.match(usersPhp, /'post_rewards_text', 'label' => '打赏按钮文案', 'type' => 'input', 'default' => '赞赏'/); + assert.match(featuresJs, /post_rewards_s:\s*true/); + assert.match(featuresJs, /post_rewards_text:\s*'赞赏'/); + assert.match(featuresJs, /key: 'post_rewards_s', label: '用户打赏功能', type: 'switch', default: true/); +}); + +test('旧字段在 schema 与运行时中均已移除(无双路)', () => { + for (const legacy of LEGACY_KEYS) { + assert.ok(!php.includes(legacy), `PHP schema 残留旧字段: ${legacy}`); + assert.ok(!runtime.includes(legacy), `PostDisplay 残留旧字段: ${legacy}`); + assert.ok(!js.includes(legacy), `page.js 残留旧字段: ${legacy}`); + assert.ok(!metaBox.includes(legacy), `PostMetaBox 残留旧字段: ${legacy}`); + } + // 面包屑末项正文无参考配置:schema 字段移除,运行时硬编码为常开。 + assert.ok(!pagePhp.includes(`'breadcrumbs_single_text'`), 'page section 残留 breadcrumbs_single_text'); + assert.ok(!js.includes(`key: 'breadcrumbs_single_text'`), 'page.js 残留 breadcrumbs_single_text'); + assert.match(runtime, /'breadcrumbs_single_text'\s*=>\s*true/); + // aksrt-frontend-tags 不再有 breadcrumbs_home_text 文本兼容(switcher 布尔语义)。 + assert.ok(!frontendTags.includes("breadcrumbs_home'] = (string) $settings['breadcrumbs_home_text'"), 'frontend-tags 残留 breadcrumbs_home_text 文本兼容'); +}); + +test('运行时只读取新键并接线封面/缩进/版权/一言', () => { + // 三类封面 + option fieldset。 + assert.match(runtime, /\$settings\['article_image_cover'\]/); + assert.match(runtime, /\$settings\['article_image_cover_option'\]/); + assert.match(runtime, /\$settings\['article_slide_cover'\]/); + assert.match(runtime, /\$settings\['article_slide_cover_option'\]/); + assert.match(runtime, /\$settings\['article_video_cover'\]/); + assert.match(runtime, /\$settings\['article_video_cover_option'\]/); + // 图片封面比例 clamp 20-200(参考主题默认 35)。 + assert.match(runtime, /max\(20, min\(200, \(int\) \(\$image_cover_option\['scale'\] \?\? 35\)\)\)/); + // 视频封面比例 clamp 0-200(0 = 不固定长宽比例)。 + assert.match(runtime, /max\(0, min\(200, \(int\) \(\$video_cover_option\['scale'\] \?\? 0\)\)\)/); + // 幻灯片配置归一化(CFS_Module::slide 全字段)。 + assert.match(runtime, /normalize_slide_cover_option\(\$slide_option_raw\)/); + for (const key of ['direction', 'loop', 'button', 'pagination', 'effect', 'scale_height', 'scale', 'auto_height', 'max_height', 'min_height', 'pc_height', 'm_height', 'spacebetween', 'speed', 'autoplay', 'interval']) { + assert.ok(runtime.includes(`'${key}'`), `normalize_slide_cover_option 缺少 ${key}`); + } + // 面包屑「首页」替代语义:true → 「首页」,false → 网站名称。 + assert.match(runtime, /!isset\(\$settings\['breadcrumbs_home_text'\]\) \|\| !empty\(\$settings\['breadcrumbs_home_text'\]\)/); + assert.match(runtime, /get_bloginfo\('name'\)/); + // 段落缩进 / 高度限制 / 版权 / 页脚文案 / 插入内容 / 一言 / 相关文章。 + assert.match(runtime, /\$settings\['post_p_indent_s'\]/); + assert.match(runtime, /\$settings\['article_maxheight_kg'\]/); + assert.match(runtime, /\$settings\['article_maxheight'\]/); + assert.match(runtime, /\$settings\['post_copyright_s'\]/); + assert.match(runtime, /\$settings\['post_copyright'\]/); + assert.match(runtime, /\$settings\['post_button_toptext'\]/); + assert.match(runtime, /\$settings\['post_front_content'\]/); + assert.match(runtime, /\$settings\['post_after_content'\]/); + assert.match(runtime, /\$settings\['yiyan_single_content_header'\]/); + assert.match(runtime, /\$settings\['yiyan_single_content_footer'\]/); + assert.match(runtime, /\$settings\['yiyan_single_box'\]/); + assert.match(runtime, /\$settings\['post_related_s'\]/); + assert.match(runtime, /\$settings\['post_related_type'\]/); + assert.match(runtime, /\$settings\['post_related_n'\]/); + assert.match(runtime, /\$settings\['post_related_orderby'\]/); + // 阅读进度/收藏/作者卡片无参考配置:常量默认(无双路)。 + assert.match(runtime, /'reading_progress'\s*=>\s*true/); + assert.match(runtime, /'favorite_enabled'\s*=>\s*true/); + assert.match(runtime, /'author_card_limit'\s*=>\s*6/); +}); + +test('前台链路:幻灯片配置接线渲染器,p_indent 接线容器与 CSS', () => { + // 幻灯片配置接入 aksrt_render_slider(effect/direction/spaceBetween/speed/interval)。 + assert.match(singleRender, /article_slide_cover_option/); + assert.match(singleRender, /'effect'\s*=>\s*\(string\) \(\$slide_option\['effect'\] \?\? 'slide'\)/); + assert.match(singleRender, /'direction'\s*=>\s*\(string\) \(\$slide_option\['direction'\] \?\? 'horizontal'\)/); + assert.match(singleRender, /'spaceBetween'\s*=>\s*\(int\) \(\$slide_option\['spacebetween'\] \?\? 15\)/); + assert.match(singleRender, /'interval'\s*=>\s*max\(1, \(int\) \(\$slide_option\['interval'\] \?\? 4\)\) \* 1000/); + assert.match(singleRender, /'show_nav'\s*=>\s*!empty\(\$slide_option\['button'\]\)/); + assert.match(singleRender, /'show_pagination'\s*=>\s*!empty\(\$slide_option\['pagination'\]\)/); + // 固定高度模式(scale_height)与比例高度模式(scale + 50px 标题预留)双分支。 + assert.match(singleRender, /!empty\(\$slide_option\['scale_height'\]\)/); + assert.match(singleRender, /\$slider_args\['pc_height'\] = \(\(int\) \(\$slide_option\['pc_height'\] \?\? 380\)\) \. 'px'/); + assert.match(singleRender, /\$slider_args\['scale_ratio'\] = max\(10, \(int\) \(\$slide_option\['scale'\] \?\? 35\)\) \/ 100/); + // 图片封面比例 clamp 20-200;视频封面 0 → 16:9(56.25)回退。 + assert.match(singleRender, /max\(20, min\(200, \(int\) \$ctx\['img_scale'\]\)\)/); + assert.match(singleRender, /max\(0, min\(200, \(int\) \$ctx\['video_scale'\]\)\)/); + assert.match(singleRender, /\$video_scale > 0 \? \(string\) \$video_scale : '56\.25'/); + // p_indent 容器类 + CSS 规则(首行缩进 2em)。 + assert.match(singleRender, /p-indent/); + assert.match(contentCss, /\.wp-posts-content\.p-indent p\s*\{\s*text-indent:\s*2em;/); + // 前端回退数组包含新键。 + assert.match(frontendTags, /'p_indent'\s*=>\s*false/); + assert.match(frontendTags, /'article_image_cover_scale'\s*=>\s*35/); + assert.match(frontendTags, /'article_slide_cover_option'\s*=>\s*\[\]/); + assert.match(frontendTags, /'article_video_cover_scale'\s*=>\s*0/); + // PostMetaBox 读取新封面键(文章级封面字段可见性)。 + assert.match(metaBox, /!empty\(\$settings\['article_image_cover'\]\)/); + assert.match(metaBox, /!empty\(\$settings\['article_slide_cover'\]\)/); + assert.match(metaBox, /!empty\(\$settings\['article_video_cover'\]\)/); + // FacadeBulk 独立回退与新键同步。 + assert.match(facadeBulk, /'breadcrumbs_home_replace'/); + assert.match(facadeBulk, /'article_slide_cover_option'/); +}); diff --git a/tests/widget-system/single-cover-height-contract.test.cjs b/tests/widget-system/single-cover-height-contract.test.cjs index fbeb835a..e0f932f0 100644 --- a/tests/widget-system/single-cover-height-contract.test.cjs +++ b/tests/widget-system/single-cover-height-contract.test.cjs @@ -25,10 +25,16 @@ test('single cover: image mode keeps the calc(scale% + 50px) height', () => { }); test('single cover: video mode uses the bare ratio height like the reference dplayer box', () => { + // 参考主题 article_video_cover_option.scale:0-200,0 = 不固定长宽比 → 16:9 回退。 assert.match( singleRender, - /single-cover--video imgbox-container" style="padding-bottom:<\?php echo esc_attr\(\(string\) \$video_scale\); \?>%;"/, - '视频封面必须输出裸 video_scale% 高度(标题在下方 footer,无叠加补偿)' + /\$video_padding = \$video_scale > 0 \? \(string\) \$video_scale : '56\.25';/, + '视频封面 scale=0 必须回退 16:9(56.25%),非 0 输出裸比例' + ); + assert.match( + singleRender, + /single-cover--video imgbox-container" style="padding-bottom:<\?php echo esc_attr\(\$video_padding\); \?>%;"/, + '视频封面必须输出裸比例高度(标题在下方 footer,无叠加补偿)' ); // +50px 是标题叠加封面的补偿量,仅单图/多图封面适用;视频封面 // 对齐参考主题 .dplayer-scale-height,不得继承该公式。 @@ -40,10 +46,17 @@ test('single cover: video mode uses the bare ratio height like the reference dpl }); test('single cover: slide mode reserves the same +50px via the slider helper', () => { + // 比例高度模式(scale_height 关闭分支)向 slider helper 传 scale_extra=50px; + // 固定高度模式(scale_height 开启分支)改用参考主题 pc/m_height 定高。 assert.match( singleRender, - /'scale_extra'\s*=>\s*'50px'/, - '多图封面必须向 aksrt_render_slider 传入 scale_extra=50px' + /\$slider_args\['scale_extra'\] = '50px';/, + '比例高度分支必须向 aksrt_render_slider 传 scale_extra=50px' + ); + assert.match( + singleRender, + /\$slider_args\['pc_height'\] = \(\(int\) \(\$slide_option\['pc_height'\] \?\? 380\)\) \. 'px';/, + '固定高度分支必须使用参考主题 pc_height 定高' ); assert.match( sliderHelper, diff --git a/tests/widget-system/user-features-contract.test.cjs b/tests/widget-system/user-features-contract.test.cjs index eeb78a8d..536d3fc3 100644 --- a/tests/widget-system/user-features-contract.test.cjs +++ b/tests/widget-system/user-features-contract.test.cjs @@ -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]); diff --git a/tools/admin-contract-report.json b/tools/admin-contract-report.json index 96993d94..b5f26170 100644 --- a/tools/admin-contract-report.json +++ b/tools/admin-contract-report.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-09-24T07:38:16+00:00", + "generated_at": "2026-09-24T08:11:53+00:00", "errors": [], "warnings": [ "customPage tools-update section 'tools/update' not in PHP schema (ok if pure operational page)", @@ -10,6 +10,8 @@ "enum incomplete parse for 'bind_reminder' (php subset of js): php=[email] js=[email,phone,email_phone,close]", "enum incomplete parse for 'content_audit_on_failure' (php subset of js): php=[allow] js=[allow,block]", "enum incomplete parse for 'content_audit_provider' (php subset of js): php=[baidu] js=[baidu,null]", + "enum incomplete parse for 'direction' (php subset of js): php=[horizontal] js=[horizontal,vertical]", + "enum incomplete parse for 'effect' (php subset of js): php=[slide] js=[slide,fade,cube,coverflow,flip]", "enum incomplete parse for 'epay_device' (php subset of js): php=[auto] js=[auto,pc,mobile]", "enum incomplete parse for 'font_family' (php subset of js): php=[system] js=[system,noto-sans-sc,noto-serif-sc,custom]", "enum incomplete parse for 'i18n_default_lang' (php subset of js): php=[zh-CN] js=[zh-CN,en,zh-TW,ja,ko,fr,de,es,ru]", @@ -37,11 +39,7 @@ "enum incomplete parse for 'pay_box_position' (php subset of js): php=[box_top] js=[box_top,top,bottom,box_bottom]", "enum incomplete parse for 'pay_modo_default' (php subset of js): php=[0] js=[0,points]", "enum incomplete parse for 'pay_wechat_sdk_options' (php subset of js): php=[official_wechat] js=[official_wechat,epay,null]", - "enum incomplete parse for 'post_author_card_orderby' (php subset of js): php=[views] js=[views,date,modified,comment_count]", - "enum incomplete parse for 'post_author_card_style' (php subset of js): php=[card] js=[card,list]", "enum incomplete parse for 'post_pay_box_position' (php subset of js): php=[content] js=[content,mask]", - "enum incomplete parse for 'post_related_orderby' (php subset of js): php=[modified] js=[modified,date,views,like,comment_count,favorite,rand]", - "enum incomplete parse for 'post_related_type' (php subset of js): php=[img] js=[img,list,text]", "enum incomplete parse for 'post_time_source' (php subset of js): php=[date] js=[date,modified]", "enum incomplete parse for 'referral_attribution_model' (php subset of js): php=[first_touch] js=[first_touch,last_touch]", "enum incomplete parse for 'search_type_in' (php subset of js): php=[post] js=[post,user]",