标签: HTML

  • 【网站】为SSL证书自动续期设定正确的Nginx配置

    使用certbot进行renew证书操作,基于Nginx+Apache。

    在webroot模式下的Nginx配置,需要对80端口和443端口进行配置,以保证验证程序可以读取/.well-known/acme-challenge/路径

    
    # 80端口
     # ACME challenge
        location ^~ /.well-known/acme-challenge/ {
            root  #webroot路径;
            allow all;
        }
       # 其他请求跳转 HTTPS
        location / {
            return 301 https://example.cn$request_uri;
        }
    
    
    # 443端口
    # ACME challenge
        location ^~ /.well-known/acme-challenge/ {
            root #webroot路径;
            allow all;
        }
    
  • 【网站】为WordPress配置正确的文件夹权限

    参考链接

    Hardening WordPress – Advanced Administration Handbook | Developer.WordPress.org

    # 切换到WP目录
    cd /var/www/wordpress
    
    # 将所有权设置为 www-data(用户和组)
    sudo chown -R www-data:www-data .
    
    # 所有目录设为 755
    sudo find . -type d -exec chmod 755 {} \;
    
    # 所有文件设为 644
    sudo find . -type f -exec chmod 644 {} \;
    
    # 强化 wp-config.php 权限
    sudo chmod 640 wp-config.php
    
  • 【网站】如何用Docker容器运行供应商的定制版WordPress

    一、一个烂摊子

    部门此前寻找网站供应商制作了产品网站,其交付网站架构较为老旧,网站基于WordPress 5.0版本构建,PHP版本要求为不高于7.3,MySQL版本5.7。

    就是这种要求在回迁公司服务器时也出现了各种问题。由于公司服务器环境过于杂乱,连mysql5.7的数据库都无法顺利导入。

    这里面也有供应商的一些问题,例如交付的WordPress配置文件与数据库表头不匹配,数据库时间配置错误等。

    在我的帮助下,数据库和配置文件都得以修改正常,网站终于部署上线,但是系统环境导致包含中文文件名的文件无法被解析,在调整Nginx配置无效后,IT让我修改中文名文件,我。。。

    二、服务器和Docker

    借由新ECS服务器和公司官网的搭建,考虑把产品网站也纳入部门服务器的管理。

    但供应商的环境版本老久,为了避免产品网站潜藏的漏洞影响整体服务器的稳定,我使用了Docker来为产品网站配置环境。

    中间也考虑使用LXD来实现,但是网络环境始终无法配置完成,因此不再浪费时间,使用Docker实现功能。

    三、Docker的问题

    最大的问题是镜像源,国内能搜索到的镜像源很多都停止了服务。因此需要在/etc/docker/daemon.json里配置可以访问的镜像源,这个不再赘述。

    四、部署

    将供应商的WP文件夹上传到服务器,可以在同位置配置Docker的yml文件。

    version: '3.7'
    
    services:
      wordpress:
        build: .
        container_name: wordpress-container
        ports:
          - "8000:80"  # WordPress 端口
        volumes:
          - /# 本地 WordPress 目录 wp:/var/www/html  # 本地 WordPress 目录
        networks:
          - wordpress_network
        depends_on:
          - mariadb  # 依赖 MariaDB 服务
        environment:
          WORDPRESS_DB_HOST: mariadb:3306
          WORDPRESS_DB_NAME: wordpress
          WORDPRESS_DB_USER: # 用户名
          WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}  # 从 .env 读取密码
        restart: always
    
      mariadb:
        image: mariadb:latest
        container_name: mariadb-container
        environment:
          MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}  # 从 .env 读取密码
          MYSQL_DATABASE: wordpress
          MYSQL_USER: # 用户名
          MYSQL_PASSWORD: ${MYSQL_PASSWORD}
        volumes:
          - mariadb_data:/var/lib/mysql  # 使用 Docker Volume 代替宿主机路径
          - # 初始数据库地址:/docker-entrypoint-initdb.d/#初始数据库.sql  # 初始 SQL 数据
        ports:
          - "3307:3306"
        networks:
          - wordpress_network
        restart: always
    
      phpmyadmin:
        image: phpmyadmin/phpmyadmin
        container_name: phpmyadmin-container
        environment:
          PMA_HOST: mariadb
          MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}  # 从 .env 读取密码
        ports:
          - "8081:80"
        networks:
          - wordpress_network
        restart: always
    
    networks:
      wordpress_network:
        driver: bridge
    
    volumes:
      mariadb_data:

    从.env文件读取密码:

    MYSQL_ROOT_PASSWORD=# 密码1
    MYSQL_PASSWORD=# 密码2

    如果是使用mariadb,则需要在WP的Config文件中修改配置:

    define('DB_HOST', 'mariadb');

    后端启动之后,我们可以通过IP+端口来进行服务的测试,如果没有问题,就可以通过主机的Nginx代理,来配置域名访问。

    server {
        listen 80;
        server_name #域名;
        return 301 https://$host$request_uri;
    }
    
    server {
        listen 443 ssl;
        server_name #域名;
    
        ssl_certificate /etc/letsencrypt/live/#域名/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/#域名/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
    
        location / {
            proxy_pass http://127.0.0.1:8000;#映射docker端口
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_set_header X-Forwarded-Proto https;
            proxy_redirect off;
        }
    }
    

    五、其他注意事项:

    1、如果WP的重写规则不可用,检查WP文件夹权限。

    2、如果配置HTTPS发现页面正常,但是管理界面出现问题,考虑是配置文件问题,参考官方说明配置

    define( 'FORCE_SSL_ADMIN', true );
    // in some setups HTTP_X_FORWARDED_PROTO might contain 
    // a comma-separated list e.g. http,https
    // so check for https existence
    if( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false )
        $_SERVER['HTTPS'] = 'on';
  • 【网站】如何把多站点的WordPress网站从Nginx+PHP-FPM切换到Apache

    最近手搓基于WP架构的企业官网,发现用Nginx+PHP-FPM时,在多站点的情况下可能会有一些地方不容易调试,所以尝试使用Nginx作为代理服务器,Apache作为后端,发现这对WordPress有很好的兼容性。

    那么此前的网站如果已经完成在Nginx+PHP-FPM的部署,如何切换到Nginx+Apache呢?

    这里省略Apache和其PHP模块的配置说明,需要重点修改的有以下几点:

    1、Apache的配置:

    Apache的端口设置,因为我们要使用Nginx转发HTTPS,因此设置Apache的监听端口为8080,避免和Nginx的端口冲突。

    <VirtualHost *:8080>
        DocumentRoot /var/www/html/wordpress
        ServerName yourdomain.com
        <Directory /var/www/html/wordpress>
            AllowOverride All
            Require all granted
        </Directory>
        
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>

    2、WP-Config的配置修改:

    如果网站使用了HTTPS,那么需要按照WordPress的官方设置来进行修改WP-Config:

    define( 'FORCE_SSL_ADMIN', true );
    // in some setups HTTP_X_FORWARDED_PROTO might contain 
    // a comma-separated list e.g. http,https
    // so check for https existence
    if( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false )
        $_SERVER['HTTPS'] = 'on';

    3、修改Nginx的站点配置文件:

    删除此前用于PHP-FPM的配置代码,把HTTPS转发到Apache处理。

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }

    4、修改.htaccess文件:

    如果使用子文件夹模式:

    # BEGIN WordPress Multisite
    # Using subfolder network type: https://wordpress.org/documentation/article/htaccess/#multisite
    
    RewriteEngine On
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    
    # add a trailing slash to /wp-admin
    RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
    
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
    RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
    RewriteRule . index.php [L]
    
    # END WordPress Multisite
    

    如果使用子域模式:

    # BEGIN WordPress Multisite
    # Using subdomain network type: https://wordpress.org/documentation/article/htaccess/#multisite
    
    RewriteEngine On
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    
    # add a trailing slash to /wp-admin
    RewriteRule ^wp-admin$ wp-admin/ [R=301,L]
    
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
    RewriteRule ^(.*\.php)$ $1 [L]
    RewriteRule . index.php [L]
    
    # END WordPress Multisite

    5、修改PHP配置文件:

    选择适合自己的媒体处理限制,因为此前处理媒体的PHP服务是PHP-FPM,切换到Apache后需要配置对应的PHP服务。

    6、检验Nginx配置文件,重启Nginx服务和Apache服务。

  • 【网站】在WP区块中添加CSS+JS动画

    类似如下效果:

    H armony

    O ptimize

    N ature

    E nergy

    S ustainability

    S olutions

    ALL In

    HONESS.

    可以直接在区块代码编辑器中进行添加。

    <p class="has-text-align-left has-text-color"
        style="color:#ffffff61;font-size:72px;font-style:normal;font-weight:700;letter-spacing:-2px;line-height:1;text-transform:uppercase">
        <em>H</em> <strong>armony</strong></p>

    例如以上是第一行动画文字的代码,因为在区块中我们给不同的文字设定了不同的标签,我们可以直接在代码中修改后面strong标签的类,改成如下:

    <p class="has-text-align-left has-text-color"
        style="color:#ffffff61;font-size:72px;font-style:normal;font-weight:700;letter-spacing:-2px;line-height:1;text-transform:uppercase">
        <em>H</em> <strong class="change-word">armony</strong></p>

    这样我们就可以利用CSS+JS动画实现该标签的动画效果。

    CSS:

    <style>
        .change-word {
            display: inline-block;
            transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out;
        }
        .hidden {
            opacity: 0;
            transform: translateY(-10px);
        }
    </style>

    JS:

    <script>
        document.addEventListener("DOMContentLoaded", function () {
            const words = [
                ["armony", "ope", "ealth", "appiness"], // H
                ["ptimize", "pportunity", "rganic", "vergreen"], // O
                ["ature", "urture", "eeds", "urturing"], // N
                ["nergy", "arth", "cology", "nvironment"], // E
                ["ustainability", "ynergy", "upport", "olutions"], // S
                ["olutions", "trategy", "uccess", "ymbiosis"] // S
            ];
    
            function changeWord(index, element, delay) {
                setTimeout(() => {
                    element.classList.add("hidden"); // 先隐藏
                    setTimeout(() => {
                        let nextWord = words[index].shift();
                        words[index].push(nextWord);
                        element.textContent = nextWord;
                        element.classList.remove("hidden"); // 显示
                    }, 400);
                }, delay);
            }
    
            function startAnimation() {
                let elements = document.querySelectorAll(".change-word");
                let delay = 100;
                elements.forEach((el, index) => {
                    setInterval(() => changeWord(index, el, index * delay), 2500);
                });
            }
    
            startAnimation();
        });
    </script>
  • 【网站】让WordPress封面区块的视频背景支持在微信环境下自动切换为照片

    移动版本的微信,在其内置的微信WEB环境中,对视频播放有很多限制,如果我们的封面区块想设置视频,就会出现无法加载播放的情况。

    为了解决这个问题,我去看了一下一些大厂的做法,当然大厂用的肯定不是WP,不过思路是可以借鉴的。

    华为的做法是用一个JS来判断是否是微信环境,如果是微信环境则切换到图片。OPPO的做法更加直接,他们直接用图片+动画的形式解决了视频这个问题,emm通用性确实刚刚的。

    那么我们在WP下,使用原版的封面区块是否可以进行类似的切换呢?也是可以的,原理是在区块代码中插入一个图片,利用一个简单的IF判断来实现微信环境下的自动切换。

    具体代码如下:

    <!-- wp:cover {"url":"//默认图片地址","id":588,"dimRatio":50,"overlayColor":"contrast","isUserOverlayColor":true,"backgroundType":"video","minHeight":840,"minHeightUnit":"px","isDark":false,"className":"main-container","layout":{"type":"constrained"}} -->
    <div class="wp-block-cover is-light main-container" style="min-height:840px">
        <span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim"></span>
    
        <!-- 视频背景,添加 id="background-video" -->
        <video id="background-video" class="wp-block-cover__video-background intrinsic-ignore" autoplay muted loop playsinline 
            src="//视频地址" data-object-fit="cover">
        </video>
    
        <!-- 备用静态图片,默认隐藏 -->
        <img id="background-image" src="//图片地址" 
            alt="Background Image" style="display:none; width: 100%; height: 100%; object-fit: cover; position: absolute; top: 0; left: 0;">
    
        <div class="wp-block-cover__inner-container">
            <!-- 可添加文本或其他内容 -->
        </div>
    </div>
    <!-- /wp:cover -->
    
    <script>
    document.addEventListener("DOMContentLoaded", function () {
        function isWeChatBrowser() {
            return /MicroMessenger/i.test(navigator.userAgent);
        }
    
        if (isWeChatBrowser()) {
            let video = document.getElementById("background-video");
            let image = document.getElementById("background-image");
    
            if (video && image) {
                video.style.display = "none"; // 隐藏视频
                image.style.display = "block"; // 显示备用图片
            }
        }
    });
    </script>
    
  • 【网站】WordPress中如何获得无间隔的区块

        "spacing": {
            "blockGap": "1.2rem",
            "padding": {
                "left": "var:preset|spacing|50",
                "right": "var:preset|spacing|50"
            }
        },

    6.X版本的Wordpress中,区块之间会有一个默认1.2rem的间隔,这个间隔在页眉页脚设置中会与其他不同色彩的区块产生非常显眼的割裂,或者同一种非白色区块中间产生间隔缝隙。

    如何修改这个问题?很简单,我们可以在主题样式和区块设置 (theme.json)中进行更改,去掉这个间隔,让我们的区块背景更加融合或者分离的更明显,具体如上代码,找到 “spacing” 中的 “blockGap” 部分,将预设的 “1.2rem” 改为null即可(不用带引号),修改完毕的代码如下所示↓

    修改完毕后最好检查一下页面是否有其他区块被影响,目前观察容易受到影响的有导航栏,可以在其单独的样式中针对性调整区块间隔。

        "spacing": {
            "blockGap": null,
            "padding": {
                "left": "var:preset|spacing|50",
                "right": "var:preset|spacing|50"
            }
        },
  • 【小贴士】WordPress中锚点注意事项

    开始之前,建议先阅读:WordPress官方关于锚点的介绍

    WordPress在现在的古登堡编辑器中,可以在“侧边栏-高级-HTML锚点”中很方便为页面添加锚点,但是这其中有一些建议和注意事项。

    问题1、锚点的地址设置

    在WP目前的设置中,如果我们在页面中将锚点前加上完整的URL,点击锚点时,(在某些浏览器中)页面跳转会非常突兀,Not elegant。

    示例:

    因此我们在同一页面中只要直接设置“#锚点”,就可以实现顺滑的跳转。

    问题2、锚点的唯一性

    这是接着问题1引申出来的,我们在每个单独页面中肯定是会注意到锚点的唯一性,但是多页面的锚点一旦多起来呢?重复的话会发生什么问题?

    在一般的用户场景这个问题并不显著,但是在WP的合集页面(目录、标签)中,如果我们有多个关于MV的文章,里面每个MV都在各自页面设置了“#MV”的锚点,那么WP在合计页面只能选择第一个文章的“#MV”锚点进行跳转。

    因此,我们可以在结合WP文章的URL结构,在每个锚点单词前加上对应文章URL的数字码,可以一定程度确保每个锚点的唯一性。


    演示区域

    这是一个锚点

  • 【网站】通过nginx为站点启用HTTP2

    启用过程还是比较简单的,但是过程让我学习到,问Ai之前先看官方手册,,,毕竟Ai的知识库不一定是最新的,而且会一本正经的胡说八道!

    过程:

    1、通过nginx -v检查nginx版本,翻阅nginx的官网手册对nginx配置文件进行编辑。

    2、配置文件server部分要注意是:

    server {
    listen 443 ssl;
    http2 on;
    }

    有些教程会告诉你是listen 443 ssl http2;这一语法已经在1.25.1版本之后被弃用了!所以如果你的nginx版本较高,参考官网的语法进行启用!

    3、按照你的需求配置其他选项。

    4、最重要的,编辑完毕使用nginx -t指令测试nginx的配置文件是否通过,然后重启nginx服务即可。

    效果:

    访问网站,检查协议是否为h2。

  • 【WP插件】通过插件形式为WP导入回到顶部功能

    网站此前用的回到顶部按钮是通过插件市场中的插件实现,效果还不错,不过功能比较单一。

    我在其他的博客中看到了一个可以在页面下拉过程中实现当前页面进度的回顶按钮,觉得实用性很棒,因此尝试通过页面脚本的形式导入。

    但是这种方式过于低效,且没法很好自定义,所以便尝试是否可以通过插件的形式导入功能,通过和GPT的一番交流,大致明白了WP插件的制作过程。

    WP的插件可以通过Zip文件导入,其中的结构为:

    backtop/
    ├── assets/
    │ ├── backtop.css
    │ └── backtop.js
    ├── backtop.php
    └── readme.txt (可选)

    其中backtop.php是插件的核心文件,包含了插件的主要功能和初始化代码。

    backtop.css为样式表,js则是JavaScript 文件,写交互逻辑使用。

    参考代码:

    PHP:

    <?php
    /*
    Plugin Name: 写你的插件名称
    Description: 描述
    Version: 版本号
    Author: 作者
    Author URI: https://wanxuefeiyang.cn
    License: GPL2
    */
    
    // Enqueue CSS and JS,注意地址
    function backtop_enqueue_assets() {
        wp_enqueue_style('backtop-style', plugin_dir_url(__FILE__) . 'assets/backtop.css');
        wp_enqueue_script('backtop-script', plugin_dir_url(__FILE__) . 'assets/backtop.js', [], false, true);
    
        $options = get_option('backtop_options');
        wp_localize_script('backtop-script', 'backTopOptions', [
            'position' => $options['position'] ?? 'bottom-right',
            'color' => $options['color'] ?? '#000000',
            'size' => $options['size'] ?? '40px',
            'shape' => $options['shape'] ?? 'circle',
            'margin' => $options['margin'] ?? '20px 20px'
        ]);
    }
    add_action('wp_enqueue_scripts', 'backtop_enqueue_assets');
    
    // 插入 HTML
    function backtop_render_html() {
        echo '
        <div id="backtop-tool">
            <ul>
                <li id="backtop" class="hidden">
                    <span id="backtop-percentage">0%</span>
                </li>
            </ul>
        </div>';
    }
    add_action('wp_footer', 'backtop_render_html');
    
    // 设置页面,可以自定义一些内容
    function backtop_add_settings_page() {
        add_options_page(
            'BackTop Settings',
            'BackTop',
            'manage_options',
            'backtop-settings',
            'backtop_render_settings_page'
        );
    }
    add_action('admin_menu', 'backtop_add_settings_page');
    
    function backtop_render_settings_page() {
        ?>
        <div class="wrap">
            <h1>BackTop Settings</h1>
            <form method="post" action="options.php">
                <?php
                settings_fields('backtop_options_group');
                do_settings_sections('backtop-settings');
                submit_button();
                ?>
            </form>
        </div>
        <?php
    }
    
    function backtop_register_settings() {
        register_setting('backtop_options_group', 'backtop_options', [
            'type' => 'array',
            'sanitize_callback' => 'backtop_sanitize_options',
            'default' => [
                'position' => 'bottom-right',
                'color' => '#000000',
                'size' => '40px',
                'shape' => 'circle',
                'margin' => '20px 20px'
            ],
        ]);
    
        add_settings_section('backtop_main_section', 'Main Settings', null, 'backtop-settings');
    
        add_settings_field('position', 'Position', 'backtop_position_field', 'backtop-settings', 'backtop_main_section');
        add_settings_field('color', 'Background Color', 'backtop_color_field', 'backtop-settings', 'backtop_main_section');
        add_settings_field('size', 'Button Size', 'backtop_size_field', 'backtop-settings', 'backtop_main_section');
        add_settings_field('shape', 'Shape', 'backtop_shape_field', 'backtop-settings', 'backtop_main_section');
        add_settings_field('margin', 'Margin', 'backtop_margin_field', 'backtop-settings', 'backtop_main_section');
    }
    add_action('admin_init', 'backtop_register_settings');
    
    function backtop_position_field() {
        $options = get_option('backtop_options');
        ?>
        <select name="backtop_options[position]">
            <option value="bottom-right" <?php selected($options['position'], 'bottom-right'); ?>>Bottom Right</option>
            <option value="bottom-left" <?php selected($options['position'], 'bottom-left'); ?>>Bottom Left</option>
        </select>
        <?php
    }
    
    function backtop_color_field() {
        $options = get_option('backtop_options');
        ?>
        <input type="color" name="backtop_options[color]" value="<?php echo esc_attr($options['color']); ?>">
        <?php
    }
    
    function backtop_size_field() {
        $options = get_option('backtop_options');
        ?>
        <input type="text" name="backtop_options[size]" value="<?php echo esc_attr($options['size']); ?>" placeholder="e.g., 40px">
        <?php
    }
    
    function backtop_shape_field() {
        $options = get_option('backtop_options');
        ?>
        <select name="backtop_options[shape]">
            <option value="circle" <?php selected($options['shape'], 'circle'); ?>>Circle</option>
            <option value="square" <?php selected($options['shape'], 'square'); ?>>Square</option>
        </select>
        <?php
    }
    
    function backtop_margin_field() {
        $options = get_option('backtop_options');
        ?>
        <input type="text" name="backtop_options[margin]" value="<?php echo esc_attr($options['margin']); ?>" placeholder="e.g., 20px 20px">
        <?php
    }
    
    function backtop_sanitize_options($options) {
        $options['position'] = in_array($options['position'], ['bottom-right', 'bottom-left']) ? $options['position'] : 'bottom-right';
        $options['color'] = sanitize_hex_color($options['color']);
        $options['size'] = preg_match('/^\d+(px|em|%)$/', $options['size']) ? $options['size'] : '40px';
        $options['shape'] = in_array($options['shape'], ['circle', 'square']) ? $options['shape'] : 'circle';
        $options['margin'] = sanitize_text_field($options['margin']);
        return $options;
    }

    JS:

    (function () {
      const backTopTool = document.getElementById("backtop-tool");
      const backTopButton = document.getElementById("backtop");
      const percentageDisplay = document.getElementById("backtop-percentage");
    
      if (backTopTool && backTopButton) {
        const { position, color, size, shape, margin } = backTopOptions || {};
        const [vertical, horizontal] = position.split("-");
        const [marginY, marginX] = margin.split(" ");
        backTopTool.style[vertical] = marginY || "20px";
        backTopTool.style[horizontal] = marginX || "20px";
        backTopButton.style.backgroundColor = color || "#000";
        backTopButton.style.width = size || "40px";
        backTopButton.style.height = size || "40px";
        backTopButton.style.borderRadius = shape === "circle" ? "50%" : "0";
        percentageDisplay.style.fontSize = `${Math.max(parseInt(size) * 0.4, 8)}px`;
      }
    
      const updateScrollProgress = () => {
        const scrollTop = window.scrollY;
        const scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
        const scrollPercentage = Math.round((scrollTop / scrollHeight) * 100);
    
        if (percentageDisplay) {
          percentageDisplay.innerHTML = scrollPercentage >= 95 ? "▲" : `${scrollPercentage}%`;
        }
    
        if (backTopButton) {
          backTopButton.classList.toggle("hidden", scrollTop < 200);
        }
      };
    
      const scrollToTop = () => {
        window.scrollTo({ top: 0, behavior: "smooth" });
      };
    
      backTopButton?.addEventListener("click", scrollToTop);
      window.addEventListener("scroll", updateScrollProgress);
    })();

    CSS:

    #backtop-tool {
      position: fixed;
      z-index: 9999;
    }
    
    #backtop-tool ul {
      list-style: none;
      padding: 0;
      margin: 0;
    }
    
    #backtop-tool .hidden {
      display: none;
    }
    
    #backtop-tool li {
      display: flex; 
      justify-content: center; /* 水平居中 */
      align-items: center; /* 垂直居中 */
      width: var(--size, 40px);
      height: var(--size, 40px);
      background: var(--color, rgba(0, 0, 0, 0.7));
      color: #fff;
      border-radius: var(--shape, 50%);
      cursor: pointer;
      font-size: calc(var(--size, 40px) * 0.4); /* 根据按钮大小动态调整字体 */
      text-align: center; /* 对齐数字文本 */
      overflow: hidden;
      box-sizing: border-box;
    }

    管理界面如下:

    从插件制作到实现的过程,给我的感觉是通过插件对页面、功能进行修改,可以避免我们干扰WP核心文件,让页面中的其他功能不受影响,对灵活性、安全性都有一定的增强,这对于我这种半桶水来说非常利好,即便哪里设置错了直接把插件删了就好嘛,可玩性很高。