标签: 插件

  • 纯代码为WordPress自定义文章类型【产品】添加产品画廊

    纯代码为WordPress自定义文章类型【产品】添加产品画廊

    原来使用ACF插件,后面觉得就使用一个插件就为了实现这一个小功能觉得有点浪费插件性能,因此,改成使用纯代码来原生添加画廊。

    展示效果图:

    代码如下:

    产品画廊前端输出代码

    function display_custom_meta_gallery() {
        // 获取特色图像 URL
        $featured_image_id = get_post_thumbnail_id();
        $featured_image_url = $featured_image_id ? wp_get_attachment_image_url($featured_image_id, 'full') : '';
        // 获取自定义元框中的画廊字段数据 (存储的是图片的URL)
        $gallery = get_post_meta(get_the_ID(), '_custom_gallery', true);
        // 如果画廊不存在或者为空,并且没有特色图,则返回空
        if (!$gallery && !$featured_image_url) {
            return '';
        }
        $output = '<div class="custom-gallery-container">';
        // 主图
        $main_image_url = $featured_image_url ? $featured_image_url : $gallery[0]; // 使用画廊中的第一张图片作为主图
        $output .= '<div class="custom-main-image">';
        $output .= '<a id="main-image-link" href="' . esc_url($main_image_url) . '" data-fancybox="gallery">';
        $output .= '<img id="main-image" src="' . esc_url($main_image_url) . '" alt="Main Image">';
        $output .= '</a>';
        $output .= '</div>';
        // 只有在画廊存在时,才输出缩略图
        if ($gallery) {
            $output .= '<div class="custom-thumbnails-container">';
            $output .= '<div class="swiper-container custom-thumbnails">';
            $output .= '<div class="swiper-wrapper">';
            // 如果有特色图像,先输出特色图像的缩略图
            if ($featured_image_url) {
                $thumbnail_url = wp_get_attachment_image_url($featured_image_id, 'thumbnail');
                $output .= '<div class="swiper-slide custom-thumbnail">';
                $output .= '<a href="' . esc_url($featured_image_url) . '" data-fancybox="gallery" data-main-image="' . esc_url($featured_image_url) . '">';
                $output .= '<img src="' . esc_url($thumbnail_url) . '" alt="Thumbnail Image">';
                $output .= '</a>';
                $output .= '</div>';
            }
            // 输出自定义元框画廊的缩略图
            foreach ($gallery as $image_url) {
                $output .= '<div class="swiper-slide custom-thumbnail">';
                $output .= '<a href="' . esc_url($image_url) . '" data-fancybox="gallery" data-main-image="' . esc_url($image_url) . '">';
                $output .= '<img src="' . esc_url($image_url) . '" alt="Thumbnail Image">';
                $output .= '</a>';
                $output .= '</div>';
            }
            $output .= '</div>'; // swiper-wrapper
            $output .= '</div>'; // swiper-container
            // 左右滑动箭头放到缩略图区域
            $output .= '<div class="swiper-button-next custom-swiper-next"></div>';
            $output .= '<div class="swiper-button-prev custom-swiper-prev"></div>';
            $output .= '</div>'; // custom-thumbnails-container
        }
        $output .= '</div>'; // custom-gallery-container
        return $output;
    }
    // 注册短代码
    add_shortcode('custom_gallery', 'display_custom_meta_gallery');
    // 加载 FancyBox 和 Swiper 的样式与脚本,仅在自定义文章类型为 product 时
    function enqueue_fancybox_swiper_scripts() {
        // 检查当前文章类型是否为 product
        if (is_singular('product')) {
            // FancyBox
            wp_enqueue_style('fancybox-style', 'https://cdn.jsdelivr.net/npm/@fancyapps/ui@4.0/dist/fancybox.css');
            wp_enqueue_script('fancybox-script', 'https://cdn.jsdelivr.net/npm/@fancyapps/ui@4.0/dist/fancybox.umd.js', array('jquery'), null, true);
            
            // Swiper
            wp_enqueue_style('swiper-style', 'https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css');
            wp_enqueue_script('swiper-script', 'https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.js', array('jquery'), null, true);
            // 自定义初始化脚本,确保 FancyBox 和 Swiper 正确工作
            wp_add_inline_script('swiper-script', "
                document.addEventListener('DOMContentLoaded', function() {
                    // 初始化 FancyBox
                    Fancybox.bind('[data-fancybox=\"gallery\"]', {});
                    // 初始化 Swiper 幻灯片
                    var swiper = new Swiper('.swiper-container', {
                        slidesPerView: 4,  // 每次显示4张缩略图
                        spaceBetween: 5,  // 缩略图之间的间距
                        navigation: {
                            nextEl: '.custom-swiper-next',
                            prevEl: '.custom-swiper-prev',
                        },
                        loop: false,
                        watchOverflow: true,  // 当缩略图不足时隐藏导航按钮
                    });
                    // 当用户点击缩略图时,更新主图和 FancyBox 链接
                    const thumbnails = document.querySelectorAll('.custom-thumbnail a');
                    const mainImage = document.getElementById('main-image');
                    const mainImageLink = document.getElementById('main-image-link');
                    thumbnails.forEach(thumbnail => {
                        thumbnail.addEventListener('click', function(event) {
                            event.preventDefault();  // 阻止默认的<a>点击行为
                            const newImageUrl = this.getAttribute('data-main-image');
                            const newImageHref = this.getAttribute('href');
                            
                            // 更新主图的 src 和 FancyBox 链接
                            mainImage.setAttribute('src', newImageUrl);
                            mainImageLink.setAttribute('href', newImageHref);
                        });
                    });
                });
            ");
        }
    }
    add_action('wp_enqueue_scripts', 'enqueue_fancybox_swiper_scripts');

    画廊后台代码

    function custom_gallery_meta_box() {
        add_meta_box(
            'custom_gallery',
            'Custom Gallery',
            'custom_gallery_meta_box_callback',
            'product', // 仅作用于 'product'
            'normal',
            'high'
        );
    }
    add_action('add_meta_boxes', 'custom_gallery_meta_box');
    function custom_gallery_meta_box_callback($post) {
        wp_nonce_field(basename(__FILE__), 'custom_gallery_nonce');
        $gallery_data = get_post_meta($post->ID, '_custom_gallery', true);
        ?>
        <div id="custom-gallery-wrapper">
            <ul id="custom-gallery-list">
                <?php
                if (!empty($gallery_data)) {
                    foreach ($gallery_data as $image_url) {
                        echo '<li><img src="' . esc_url($image_url) . '"><input type="hidden" name="custom_gallery_images[]" value="' . esc_url($image_url) . '"><a href="#" class="remove-image">Remove</a></li>';
                    }
                }
                ?>
            </ul>
            <input type="button" class="button button-secondary" id="add-gallery-image" value="Add Gallery Image">
        </div>
        <style>
            #custom-gallery-wrapper ul {
                list-style-type: none;
                margin: 0;
                padding: 0;
            }
            #custom-gallery-wrapper ul li {
                display: inline-block;
                margin-right: 5px;
                position: relative;
            }
            #custom-gallery-wrapper ul li img {
                display: block;
            }
            #custom-gallery-wrapper ul li .remove-image {
                position: absolute;
                top: 5px;
                right: 5px;
                color: red;
                cursor: pointer;
            }
        </style>
        <script>
            jQuery(document).ready(function($) {
                var frame;
                $('#add-gallery-image').on('click', function(e) {
                    e.preventDefault();
                    if (frame) {
                        frame.open();
                        return;
                    }
                    frame = wp.media({
                        title: 'Select Images',
                        button: {
                            text: 'Add to Gallery',
                        },
                        multiple: true
                    });
                    frame.on('select', function() {
                        var attachments = frame.state().get('selection').toArray();
                        attachments.forEach(function(attachment) {
                            var image_url = attachment.attributes.url;
                            $('#custom-gallery-list').append('<li><img src="' + image_url + '" width="150" height="150"><input type="hidden" name="custom_gallery_images[]" value="' + image_url + '"><a href="#" class="remove-image">Remove</a></li>');
                        });
                    });
                    frame.open();
                });
                $(document).on('click', '.remove-image', function(e) {
                    e.preventDefault();
                    $(this).parent().remove();
                });
            });
        </script>
        <?php
    }
    function save_custom_gallery_meta_box($post_id) {
        if (!isset($_POST['custom_gallery_nonce']) || !wp_verify_nonce($_POST['custom_gallery_nonce'], basename(__FILE__))) {
            return $post_id;
        }
        if ('product' !== get_post_type($post_id)) {
            return $post_id;
        }
        if (!current_user_can('edit_post', $post_id)) {
            return $post_id;
        }
        if (isset($_POST['custom_gallery_images'])) {
            update_post_meta($post_id, '_custom_gallery', array_map('esc_url', $_POST['custom_gallery_images']));
        } else {
            delete_post_meta($post_id, '_custom_gallery');
        }
    }
    add_action('save_post', 'save_custom_gallery_meta_box');
    function enqueue_custom_gallery_scripts($hook) {
        global $post;
        if ($post->post_type == 'product') {
            wp_enqueue_media();
            wp_enqueue_script('jquery');
        }
    }
    add_action('admin_enqueue_scripts', 'enqueue_custom_gallery_scripts');
  • CPT UI (Custom Post Type UI) WP插件显示自定义文章类型的分类法标签

    CPT UI (Custom Post Type UI) WP插件显示自定义文章类型的分类法标签

    如题,当我们使用CPT UI插件创建自定义文章类型时,默认的自定义文章类型页面仅有标题和日期,这严重妨碍了正常的使用。如下:

    这,完全跟使用习惯不符,我更希望能够像普通文章一样,看到当前文章属于哪个分类,又有哪些标签,但CPT UI的默认设置里面,并没有相关的代码,因此,我们需要自己写代码,作为一个非专业码农,我直接找AI帮忙写了,如下:

    // 在 'url' 自定义文章类型的文章列表中添加 'url_cat' 分类和 'url_tag' 标签列
    function add_url_cat_tag_columns_to_url_post_type($columns) {
        $columns['taxonomy_url_cat'] = 'URL 分类';
        $columns['taxonomy_url_tag'] = 'URL 标签';
        return $columns;
    }
    add_filter('manage_url_posts_columns', 'add_url_cat_tag_columns_to_url_post_type');
    // 显示 'url_cat' 分类和 'url_tag' 标签的内容
    function show_url_cat_tag_column_content($column, $post_id) {
        if ($column === 'taxonomy_url_cat') {
            $terms = get_the_term_list($post_id, 'url_cat', '', ', ', '');
            if (is_string($terms)) {
                echo $terms;
            } else {
                echo '无分类';
            }
        }
        if ($column === 'taxonomy_url_tag') {
            $terms = get_the_term_list($post_id, 'url_tag', '', ', ', '');
            if (is_string($terms)) {
                echo $terms;
            } else {
                echo '无标签';
            }
        }
    }
    add_action('manage_url_posts_custom_column', 's
    how_url_cat_tag_column_content', 10, 2);

    验证下是否已实现:

    okay, 没什么问题。

    对了,如果希望快速编辑也适用,需要去CPT UI插件的设置里面,勾选 Show in quick/bulk edit panel.

  • 几个觉得不错的画廊插件分享,适合产品展示/多种不同排列组合

    几个觉得不错的画廊插件分享,适合产品展示/多种不同排列组合

    最近开发另一个网站,在实现某些功能的时候,翻了不少插件库,实际也使用了不少插件。在插件的选择上,我一贯的选择是只选则最能符合需求的最精简插件。

    这里记录下一些我觉得不错的插件。

    这是一款图库画廊插件,其使用自定义文章类型,然后通过短代码来调用画廊。

    其样式多样,可以适用各种使用场景。

    这款插件是一款产品展示插件,可以通过画廊来展示产品而无需woocommerce

    三、KPIS CTA Buttons

    这是一款侧边栏悬浮联系框插件,非常简单,也没有什么设置项。但其在手机端的显示效果不错,有底部粘性菜单。应该有很多CSS JS 特效也能实现一样的,图省事,这个可以。

    这应该是所有画廊插件中,样式最多的一个插件,同时功能也多,支持从各大云存储网盘读取图片(youtube, vimeo,google 盘),但是这个插件是使用短代码引入画廊,对我来说不是特别方便,我个人不喜欢这种引入方式,略显麻烦。但这不妨碍我将其收藏下,尽管目前已有一年没有更新了。

  • 兼容缤纷云云存储的WordPress上传插件 – Media Cloud配置介绍

    兼容缤纷云云存储的WordPress上传插件 – Media Cloud配置介绍

    一、前言

    逛论坛时,听到有人提及缤纷云有对每个实名认证的新用户赠送免费的云存储、流量以及请求数!这对纯博客的我来说挺香的,查看了一番介绍后,的确有看到:

    很棒,对比UPYUN的10GB,很良心了。

    二、使用

    新注册用户,会发放2个充值金额就赠送的优惠券,我这小博客没这么大的消耗量,指着免费过日子的,只能浪费了。

    缤纷云网站打开速度超快,甚至到每个选项都响应非常迅速,着实有点惊艳我了。

    但在实际使用上,就略微不方便了。检查了整个文档资料,发现,其并没有对终端用户有很好的指引,所以,普通消费者应该只是顺带为之,其目标是为企业用户服务。个人这三瓜两枣的,暂时还不是最主要的考虑因素吧。

    这些个文档,我是没有精力去看然后集成到各个项目的。

    因此只能找下现成的插件,本鱼因为使用wordpress,所以首先找下wordpress相关的S3插件。

    界定几个值:

    • 需要设置Endpoint 【支持这个的插件,我就只找到(一)(五)】
    • 需要设置cn-east-1
    • 需要设置access key
    • 需要设置access secret

    首先说结果【这几乎是唯一一款能够支持缤纷云的插件】,如果你有更精简更好用的插件,请一定不要吝啬!务必告知本鱼!

    (一)Media Cloud for Bunny CDN, Amazon S3, Cloudflare R2, Google Cloud Storage, DigitalOcean and more

    优点,仅针对缤纷云而言,能够完成缤纷云存储和wordpress的集成!

    缺点:这个插件真的太大了,来张图简单看下吧:

    (二)S3-Uploads

    太强太暴力,兼容性是一点都不考虑。类似于强制接管wp-upload目录来实现S3的云存储上传。同时,也不支持直接对endpoint等选项的配置,文档复杂,报错很多,反正我是没有配置缤纷云的上传成功。

    (三)WP Offload Media Lite for Amazon S3, DigitalOcean Spaces, and Google Cloud Storage

    亚马逊官方有教程使用的一款插件,我没有实际安装配置过,但是从配置截图上,发现也无法配置endpoint,故弃。

    (四)WordPress Amazon S3 Plugin

    这是在所有插件中,我最看好的一款插件,但可惜的是,它也无法配置endpoint。检索了它的开发文件后,似乎不是我的能力能改的,如果你有能力,也有需求,推荐!【PS: 请务必发本鱼一份】

    (五)Upcasted S3 Offload – AWS S3, Digital Ocean Spaces, Backblaze, Minio and more

    这款插件对比(一)要精简不少,但存在报错输出,

    Warning: is_readable(): open_basedir restriction in effect. File(/home/www/.aws/config) is not within the allowed path(s): (/www/wwwroot/www.waoww.com/:/tmp/

    解决办法:需要关闭open_basedir(即:防跨站攻击),因为它的配置文件会写入Home/www目录,我使用了这么多的配件,鲜少有插件要这么写入的,担心安全问题,因此用AI看看具体是什么问题:

    就是配置文件写到系统目录去了?这好办,安排修复:

    替换这行代码,将配置文件写死在相对目录:

    public static function ini(
        $profile = null,
        $filename = 'config/config.ini' // 使用相对路径
    ) {
        $configFilePath = __DIR__ . '/' . $filename; // 拼接工作目录和相对路径
    
        return function () use ($profile, $configFilePath) {
            if (!is_readable($configFilePath)) {
                return self::reject("Cannot read configuration from $configFilePath");
            }
            $data = \Aws\parse_ini_file($configFilePath, true);
            if ($data === false) {
                return self::reject("Invalid config file: $configFilePath");
            }
            if (!isset($data[$profile])) {
                return self::reject("'$profile' not found in config file");
            }
            if (!isset($data[$profile][self::INI_MODE])) {
                return self::reject("Required defaults mode config values
                    not present in INI profile '{$profile}' ({$configFilePath})");
            }
            return Promise\Create::promiseFor(
                new Configuration(
                    $data[$profile][self::INI_MODE]
                )
            );
        };
    }
    

    再检查下还有没有报错,okay,搞定,上传正常。:

    毛线正常!图片上传都错乱了,而自定义域名功能要付费!

    遂,弃!

  • 汇总整理下本站所有使用的WordPress插件-精选WP博客插件推荐

    汇总整理下本站所有使用的WordPress插件-精选WP博客插件推荐

    网站到目前为止应该不会再有大的变动了,为了后续其他网站也有类似需求,故此,整理下本站在使用的所有插件。

    本站所有样式、页面、文章内容、侧边栏等等都是基于古登堡块。因此,轻松地:

    由于发现很多古登堡的块/样式插件使用时都会出现类似样式丢失的情况,而多次刷新后又恢复正常,反反复复,不厌其烦,因此,本鱼直接抛弃了所有的古登堡区块插件,转而使用wordpress内置区块编辑器完成所有的一切样式。但部分不影响主功能的古登堡插件保留了下来。

    插件下载地址:

    后台设置图:

    二、Bookmark Card

    这是一款将外链嵌入文章变成卡片式/列表样式风格的美化插件,兼容Anylink插件,本站没有使用这个插件,此处仅为收藏。该插件也有2年没有更新了。

    样图:

    三、Cachify

    用于做下网站基础的缓存,连接服务器的memcached使用。可以方便的完成缓存清除!

    后台配置图:

    设置区域,需要在nginx conf中添加的部分:

    以下是本站的nginx配置代码,含伪静态:

    location /
    {
    	 try_files $uri $uri/ /index.php?$args;
    	 error_page 404 405 = @nocache;
     
        if ( $query_string ) {
            return 405;
        }
        if ( $request_method = POST ) {
            return 405;
        }
        if ( $request_uri ~ "/wp-" ) {
            return 405;
        }
        if ( $http_cookie ~ (wp-postpass|wordpress_logged_in|comment_author)_ ) {
            return 405;
        }
    
        default_type text/html;
        add_header X-Powered-By Cachify;
        set $memcached_key $host$uri;
        memcached_pass localhost:11211;
    }
    
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;
    ## GZIP
    gzip_static on;
    
    
    location @nocache {
        try_files $uri $uri/ /index.php?$args;
    }
    

    四、Code Snippets

    由于本站使用的是wordpress 默认的2024主题,因此没有地方可以用来放置部分php代码片段,因此,该插件能帮不少忙,同时能够清晰的知道那行代码是用来做什么的【手动备注】,删除修改也方便,

    免费版后台界面一览:

    五、IZ Block Editor Tooltips

    这个插件是找了好久才找到的一个插件,能够做到,快速添加tooltips,鼠标悬浮这里,你就可以看到什么叫tooltips了。强推!尤其是那些惜字如金的,有时候真的难以理解某些缩写/代号是什么意思,那使用这个插件即可,几乎没有占用。而且后台添加操作简直不要太丝滑!

    这就是所有的设置项:

    六、Prismatic

    这是一个代码高亮插件,内置了3种代码高亮方案【prissm.js/ highlight.js/ Plain Flavor】,对本鱼来说,其使用比很多代码高亮插件要简单,因此毫不犹豫地就上了。代码高亮对本站来说太有必要,还有代码复制功能,自己去集成这些总没有直接使用插件方便,快速。

    后台设置:

    七、Simply Static

    这个插件是为本站后续做纯静态化做准备,有试过 WP2STATIC ,出现了安装报错,可能是PHP版本问题,而这个插件还可以,可以生成全站链接的静态压缩包然后可以下载下来。方便地后续转移至任何一个能够托管纯静态资源的地方,如: GITEE, Serverless, Github, Cloudflare等等有太多的选择,而且几乎没有费用!

    八、TinyPNG – JPEG, PNG & WebP image compression

    一款图片压缩插件,后台简洁使用方便,压缩效果好。使用邮箱注册即可!如果有域名邮箱的话,可以注册多个,因为免费版限制每月500张图片,对于个人博客应该是足够了。

    九、USS Upyun

    wordpress图片上传UPYUN插件,搜一圈,upyun(及又拍云)可能太小众了?仅有2款插件:

    这款插件可以说是 bookmark card 插件的升级版【功能意思,非同一开发者,之间也没有关系】,做了许多自定义项。主要有:

    • 后台可设置自定义样式模板【不是特别好用以及方便设置】
    • 可点击选择下载获取到的图片/上传自定义图片作为目标网站图片
    • 可编辑网站名称/标题
    • 兼容古登堡编辑器
    • 可以放自己站的链接/站外的链接

    一些个图片:

    后台设置:

    十一、Wenprise Pinyin Slug

    自动转换 WordPress 中的中文文章别名、分类项目别名、图片文件名称为汉语拼音。

    【主要为了防止图片链接的中文编码乱码问题】

    十二、WP Sitemap Page

    通过短代码来输出网站地图,即,短代码输出站点地图。本鱼主要拿来做网站的搜索用,为后续的纯静态化做准备。放弃使用该插件了, 因为短代码有冲突,古登堡编辑器中存在与其他插件不兼容的情况。

    目前改为使用另一款插件:

    输出界面选项够用,html版本样式也不错,可以用来做后续的静态化搜索。

    关于Html的展示效果,你可以点击这里查看。

    十三、Wenprise Pinyin Slug

    一款能将URL中的中文字符转拼音的插件。旨在解决:很多图片上传时命名是用标题命名的,不使用转换直接上传的话,图片的文件名将是中文的,然后,图片链接就会是中文转编码之后的,很长而且可读性差不利于SEO,故此使用这类转换插件就可以解决这个问题。

    设置页面图:

    十四、WPOPT

    一款由果壳剥壳站长开发的wordpress优化插件,功能实用实测能有不错的优化效果,应该说,该插件并不改变站点结构,只是优化掉原wordpress中不适应中国地区使用习惯的部分选项,

    插件作者自述:

    WPOPT插件,是由本站开发的一款WordPress优化插件,能对WordPress底层功能进行优化,支持功能开关,系统加速等功能。

    目前有几十种开关可以供使用,同时,每个功能都有开关说明。将永久免费提供给广大WordPress用户使用。

    2.0版本全新发布,采用vite打包,界面采用Vue3+element-plus制作。无论是外观,还是框架功能,都是空前的强大。

    十五、简易目录(EOC(easy table of contents))

    一款精简的wordpress文章目录插件。具体效果你可以看本文章右侧边栏的文章目录区域。

  • WordPress纯代码实现自定义弹窗|支持多弹窗并存,简码,兼容古登堡

    WordPress纯代码实现自定义弹窗|支持多弹窗并存,简码,兼容古登堡

    如题,该代码来源于sola博客,原文链接如下:

    本站根据自己的实际情况分别相关文件放置在了:

    1. 古登堡编辑器全局样式内/任意供插入js/css代码的部分
    2. php部分使用的code snippes插件进行插入
    3. 代码输出部分,使用自定义html添加的按钮

    一些使用心得:

    (1)该弹窗可以多个同时启用进行

    不同地方使用不同弹窗,只需要修改弹窗的序号即可,同时,可以对弹窗的宽度样式进行自定义的调整,使其适应不同使用场景。非常好用,对本站这种不喜欢装那些繁杂的弹窗插件的人来说,很满足了。

    (2)弹窗简码的置入

    使用古登堡编辑器时,可以中间放很多内容类似,可以这样放入,不用像原文的演示那样全部放在一个简码后:

    (3)弹窗的触发

    以下这个触发代码,似乎只能使用按钮类型进行触发。因为如果时古登堡区块的话,似乎无法指定 data-modal-target=”#popup-1″ 元素。由于该项对本站来说没有影响,不想再去修改源代码了以适应了,如果有修改好,能够通过其他类型点击进行触发的,请务必发我一份!

    <button class="sola-modal-trigger" data-modal-target="#popup-1">打开</button>

    以下随附了本站使用的代码,供留存记录。

    Js代码放在页脚,css代码随意,我是放在古登堡编辑器样式内部,php代码可以放在function.php内,我是使用code snippes插件来放置的。

    <script type="text/javascript">
          jQuery(function($){
            const $overlay = $('<div class="sola-modal-overlay hidden">');
            const closeBtn = '<a class="sola-modal-close">×</a>';
            
            $overlay.appendTo('body');
            $('body').on('click','.sola-modal-trigger',openModal);
            $('body').on('click', '.sola-modal-close,sola-modal-overlay', closeModal);
            $('body').on('click', '.sola-modal-overlay', closeAllModals);
            
            function openModal(){
              const target = $(this).data('modal-target');
        
              if( target && $(target).length ){
                const $target = $(target);
        
                // Add close btn
                if( ! $target.find('sola-modal-close').length ){
                  $(closeBtn).appendTo($target);
                }
                
                // Set custom width
                const modalCustomWidth = $target.data('modal-width');
                if( modalCustomWidth ){
                  $target.css('width',modalCustomWidth);
                }
                
                // Open
                $target.removeClass('hidden');
                $overlay.removeClass('hidden');
              }   
            }
            
            function closeModal(){
        
              if( $('.sola-modal:not(.hidden)').length == 1 ){
                $overlay.addClass('hidden');
              }
        
              $(this).closest('.sola-modal').addClass('hidden');   
              
            }
            
            function closeAllModals(){
              $('.sola-modal').addClass('hidden');
              $overlay.addClass('hidden');
            }
          });
        </script>
    /*添加一个弹窗*/
    .sola-modal-close {
        position: absolute;
        top: 1.2rem;
        right: 2rem;
        font-size: 2rem;
        color: #333;
        cursor: pointer;
        border: none;
        background: none;
        text-decoration: none !important;
      }
      .hidden {
        display: none;
      }
      .sola-modal {
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 30%;
        max-width:100%;
        max-height:100vh;
        background-color: white;
        padding: 3rem;
        border-radius: 5px;
        box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.3);
        z-index: 99999;
        overflow-y:auto;
      }
    
      .sola-modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.6);
        backdrop-filter: blur(3px);
        z-index: 9999;
      }
    //添加弹窗主体
    add_shortcode( 'sola-popup', 'sola_popup_handler');
    function sola_popup_handler( $atts, $content ){
      $atts = shortcode_atts( array(
        'width' => '',
        'id' => '',
      ), $atts );
      extract($atts);
      ob_start();?>
    
      <div class="sola-modal hidden" data-modal-width="<?php echo $width;?>" id="<?php echo $id;?>">
        <a class="sola-modal-close">×</a>
        <?php echo apply_filters('the_content',$content);?>
      </div>
      <?php
      return ob_get_clean();
    }