百合会漫画

百合会漫画阅读与下载

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         百合会漫画
// @namespace    https://bbs.yamibo.com/
// @version      0.9.1
// @description  百合会漫画阅读与下载
// @author       mooth
// @match        https://bbs.yamibo.com/forum-30*
// @match        https://bbs.yamibo.com/forum.php?mod=forumdisplay&fid=30*
// @match        https://bbs.yamibo.com/thread*
// @match        https://bbs.yamibo.com/forum.php?mod=viewthread&tid*

// @icon         https://www.google.com/s2/favicons?domain=yamibo.com

// @require      https://update.greatest.deepsurf.us/scripts/471654/1225059/vue3js.js
// @require      https://unpkg.com/[email protected]/umd/index.js
// @require      https://unpkg.com/[email protected]/dist/axios.min.js

// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_addElement

// @noframes
// ==/UserScript==


(function () {
    GM_addElement('link', {
        href: 'https://unpkg.com/element-plus/dist/index.css',
        rel: 'stylesheet'
    });
    GM_addElement('script', {
        src: 'https://unpkg.com/element-plus',
        type: 'text/javascript',
        id: "element"
    });

    const { createApp, computed, ref, reactive, onMounted, watch } = Vue;

    // 公用方法 ---------------------------------------------------------------------------
    const PubFun = {
        // 创建碎片
        Fragment: (id) => {
            const f = document.createDocumentFragment();
            f.appendChild(document.querySelector(id));
            return f;
        },

        // 解析地址 html_str 返回页面中解析的地址数组
        getUrls: (html) => {
            try {
                // 定义三个数组,分别存储三种不同目的的图片地址
                let urlCandidates = [new Array(), new Array(), new Array()];

                // 定义一个数组,用于存储最终解析出的图片地址
                let urls = [];

                // 使用正则表达式匹配三种不同格式的图片地址
                if (html.match(/zoomfile="(.*?)"/g)) {
                    urlCandidates[2] = html.match(/zoomfile="(.*?)"/g);
                }
                if (html.match(/https:\/\/bbs\.yamibo\.com\/data.*?"/g)) {
                    urlCandidates[1] = html.match(/https:\/\/bbs\.yamibo\.com\/data.*?"/g);
                }
                if (html.match(/<img.*?class="zoom".*?file="(.*?)"/g)) {
                    urlCandidates[0] = html.match(/<img.*?class="zoom".*?file="(.*?)"/g);
                }

                // 将三个数组按照长度排序,优先处理匹配数量最多的情况
                urlCandidates.sort((a, b) => b.length - a.length);

                // 遍历三个数组,将匹配到的图片地址进行格式化后存储到 urls 数组中
                urlCandidates.forEach(list => {
                    list.forEach(str => {
                        if (str.match("bbs.yamibo.com")) {
                            urls.push(str.match(/(https.*?)"/)[1]);
                            // "zoomfile=\"data/attachment/forum/202406/06/120022lu22z92dtq3hedq9.jpg\""
                        } else if (str.match(/zoomfile="(.*?)"/)) {
                            urls.push("https:\/\/bbs.yamibo.com/" + str.match(/zoomfile="(.*?)"/)[1]);
                        } else {
                            urls.push(
                                str.match(/<img.*?class="zoom".*?file="(.*?)"/)[1]
                                    .replace("http", "https")
                            );
                        }
                    });
                });

                // 过滤不需要的图片 和错误匹配
                urls = [...new Set(urls.filter(url => {
                    if (!url.endsWith('.gif') && !url.match('static')) {
                        return url
                    }
                }))];

                // 返回解析出的图片地址数组
                return urls;
            } catch (error) {
                console.log("html地址解析", error);
            }
        },

        // 获取图片生成本地地址 解决跨域 设置同源
        cross: async (url) => {
            return new Promise((resolve, reject) => {
                try {
                    // 将 httpss 替换为 https
                    url = url.replace("httpss", "https");
                    // 定义一个变量,用于存储 blob URL
                    let blobUrl = "";
                    GM_xmlhttpRequest({
                        method: "get",
                        url: url,
                        responseType: "blob",
                        headers: {
                            Referer: "https://bbs.yamibo.com/forum.php?"
                        },
                        onload: (res) => {
                            try {
                                // 将响应数据转换为 blob URL
                                blobUrl = window.URL.createObjectURL(res.response);
                            } catch (error) {
                                reject(`PubFun.cross图片转本地地址 \n${url}\n ${error}`)
                            }

                            resolve(blobUrl);
                        },
                        onerror: () => {
                            console.error("无法链接到该页面");
                            reject("无法链接到该页面");
                        }
                    });
                } catch (error) {
                    reject(`PubFun.cross\n${error}\n${url}`);
                }

            });
        },

        // 文件储存
        saveAs: (data, name, type) => {
            // 创建一个 a 标签
            const link = document.createElement("a");
            // 创建一个 blob URL
            const url = window.URL.createObjectURL(data);
            // 设置 a 标签的 href 属性为 blob URL
            link.href = url;
            // 设置 a 标签的 download 属性为文件名
            link.download = name + "." + type;
            // 模拟点击 a 标签,触发下载
            link.click();
        },

        // fflate打包
        fflate_zip: async (name, urlList, state) => {
            let num = 0;
            let file_b_list = {}
            await new Promise(async resolve => {
                for (let i = 0; i < urlList.length; i++) {
                    const url = urlList[i];
                    try {
                        const res = await axios.get(url, { responseType: 'blob' });
                        num++;

                        if (state) {
                            state.done = num;
                            state.allTasks = urlList.length;
                        }

                        const file_blob = res.data;
                        const file_type = file_blob.type.match(/\/(.*)/)?.[1];
                        const file = new Uint8Array(await file_blob.arrayBuffer());

                        file_b_list[`${i + 1}.${file_type}`] = file;
                    } catch (error) {
                        num++;
                        if (state) {
                            state.error++;
                        }
                        console.error("下载失败:", url, error);
                    }

                    // 完成
                    if(i === urlList.length-1){
                        resolve()
                    }
                }
            });

            // 更新下载状态
            if (state) { state.pack = 1; }
            console.log(file_b_list);
            fflate.zip(file_b_list, { level: 0 }, (err, content) => {
                console.log(content);
                // 生成 zip 包并下载
                try {
                    PubFun.saveAs(new Blob([content], { type: "application/zip" }), name, "zip")
                    // 更新下载状态
                    if (state) {
                        state.pack = 2;
                    }
                } catch (e) {
                    // 更新下载状态
                    if (state) {
                        state.pack = 3;
                    }
                    // 打印错误信息
                    console.error("打包失败:", e);
                }
            })

        },

        // 任务队列 a = new createTaskQueue(10), a.addTask(()=>fun())
        createTaskQueue: (concurrency = 10) => {
            const queue = [];
            let running = 0;

            const run = () => {
                while (running < concurrency && queue.length > 0) {
                    const task = queue.shift();
                    running++;
                    task()
                        .then(() => {
                            running--;
                            run();
                        })
                        .catch(() => {
                            running--;
                            run();
                        });
                }
            };

            const addTask = (task) => {
                queue.push(task);
                run();
            };

            return {
                addTask
            }
        }
    };

    // 漫画列表组件 ---------------------------------------------------------------------------
    class MangaListComponent {
        static f = []
        static columns = 2

        constructor() {
            // 初始化 列数
            if (!GM_getValue("columns")) {
                GM_setValue("columns", 2)
                MangaListComponent.columns = 2
            } else {
                MangaListComponent.columns = GM_getValue("columns")
            }

            // 获取表格节点
            MangaListComponent.f = PubFun.Fragment("#threadlisttableid")

            // 模板样式
            MangaListComponent.style = `
                .columns_container{
                    display: flex;
                    gap: 10px;
                    align-items: center;
                    svg{
                        /* height: 40px; */
                        fill: burlywood;
                        cursor: pointer;
                    }
                }
                .columns_selected{
                    fill: #551201 !important;
                }
                .mt_manga_message_container{
                    display: grid;
                }
                .mt_manga_message {
                    display: flex;
                    height: 150px;
                    padding: 10px 0;
                    /* width: 50%; */
                    float: left;
                    overflow: auto;
                }

                .mt_manga_message::-webkit-scrollbar {
                    /* 整体*/
                    width: 5px;
                }

                .mt_manga_face {
                    height: 100%;
                    img{
                        height: 100%;
                    }
                }

                .mt_manga_info {
                    padding: 0 10px;
                }
                        `
            GM_addStyle(MangaListComponent.style)

            // 模板
            MangaListComponent.template = `
                            <div class="columns_container">
                                <svg @click="columns=1" :class="{'columns_selected':columns==1}" height="40px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1467" ><path d="M868.352 568.32q32.768 0 53.248 19.456t20.48 52.224l0 221.184q0 35.84-19.968 54.784t-52.736 18.944l-706.56 0q-33.792 0-56.832-22.528t-23.04-55.296l0-212.992q0-35.84 19.968-55.808t54.784-19.968l710.656 0zM868.352 90.112q32.768 0 53.248 18.944t20.48 52.736l0 220.16q0 35.84-19.968 54.784t-52.736 18.944l-706.56 0q-33.792 0-56.832-22.528t-23.04-55.296l0-211.968q0-35.84 19.968-55.808t54.784-19.968l710.656 0z" p-id="1468"></path></svg>
                                <svg @click="columns=2" :class="{'columns_selected':columns==2}" height="47px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1618" ><path d="M433.1 480 174.9 480c-25.9 0-46.9-21-46.9-46.9L128 174.9c0-25.9 21-46.9 46.9-46.9l258.2 0c25.9 0 46.9 21 46.9 46.9l0 258.2C480 459 459 480 433.1 480z" p-id="1619"></path><path d="M433.1 896 174.9 896c-25.9 0-46.9-21-46.9-46.9L128 590.9c0-25.9 21-46.9 46.9-46.9l258.2 0c25.9 0 46.9 21 46.9 46.9l0 258.2C480 875 459 896 433.1 896z" p-id="1620"></path><path d="M849.1 480 590.9 480c-25.9 0-46.9-21-46.9-46.9L544 174.9c0-25.9 21-46.9 46.9-46.9l258.2 0c25.9 0 46.9 21 46.9 46.9l0 258.2C896 459 875 480 849.1 480z" p-id="1621"></path><path d="M849.1 896 590.9 896c-25.9 0-46.9-21-46.9-46.9L544 590.9c0-25.9 21-46.9 46.9-46.9l258.2 0c25.9 0 46.9 21 46.9 46.9l0 258.2C896 875 875 896 849.1 896z" p-id="1622"></path></svg>
                                <svg @click="columns=3" :class="{'columns_selected':columns==3}" height="40px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1768" ><path d="M248.832 63.488q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-116.736 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l116.736 0zM572.416 63.488q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-118.784 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l118.784 0zM891.904 63.488q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-118.784 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l118.784 0zM248.832 385.024q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-116.736 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l116.736 0zM572.416 385.024q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-118.784 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l118.784 0zM891.904 385.024q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-118.784 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l118.784 0zM248.832 706.56q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-116.736 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l116.736 0zM572.416 706.56q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-118.784 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l118.784 0zM891.904 706.56q28.672 0 48.64 19.968t19.968 48.64l0 116.736q0 28.672-19.968 48.64t-48.64 19.968l-118.784 0q-28.672 0-48.64-19.968t-19.968-48.64l0-116.736q0-28.672 19.968-48.64t48.64-19.968l118.784 0z" p-id="1769"></path></svg>
                            </div>

                            <div class="mt_manga_message_container">
                                <div v-for="(item, index) in comicList" :key="item.id" class="mt_manga_message" >
                                    <div class="mt_manga_face">
                                        <img :src="item.face" class="face" alt="封面">
                                    </div>
                                    <div v-html="item.innerHTML" class="mt_manga_info"></div>
                                </div>
                            </div>

                            `
            // 创建
            createApp(
                {
                    template: MangaListComponent.template,
                    setup() {
                        // 获取主题列表
                        const fragment = MangaListComponent.f;
                        // 定义主题链接
                        const url = "https://bbs.yamibo.com/forum.php?mod=viewthread&tid=";
                        // 列数
                        const columns = ref(MangaListComponent.columns)
                        // 定义漫画列表数据
                        const comicList = ref([]);
                        // 计算属性,为每个漫画项添加默认封面
                        const computedComicList = computed(() => comicList.value.map((item) => ({
                            ...item,
                            face: item.face || "https://bbs.yamibo.com/data/attachment/forum/201504/01/110518de9s4qsd6qtzbn9m.jpg"
                        })));

                        ////////////////////////////////////////////////////////////////////////////////////
                        // 获取漫画封面
                        const getComicCover = async (url_0, index) => {
                            return new Promise(async (resolve, reject) => {
                                let url = url_0

                                // 判断是否需要重定向
                                let res = await axios.get(url)
                                // 不需要,直接解析html
                                if (res.data.match('html')) {
                                    console.log(`直接解析,${url}`);
                                    // 解析HTML获取图片地址
                                    const urls = PubFun.getUrls(res.data);
                                    // 将解析到的第一个图片地址设置为封面
                                    comicList.value[index].face = await PubFun.cross(urls[0]);
                                    resolve()
                                    return
                                }

                                // 需要重定向
                                try {
                                    // 获取重定向地址
                                    url = await get_redirect_url_eval(url_0)
                                    // 通过iframe 获取重定向地址
                                    if (url == url_0) {
                                        url = await get_redirect_url_iframe(url_0)
                                    }
                                } catch (error) {
                                    // 通过iframe 获取重定向地址
                                    url = await get_redirect_url_iframe(url_0)
                                }

                                // 发送请求获取对应漫画页面的HTML
                                try {
                                    axios.get(url)
                                        .then(async (res) => {

                                            const data = res.data;

                                            // 是否重新加载
                                            if (!data.match('https')) {
                                                console.log("二次加载封面");
                                                getComicCover(url, index)
                                                return
                                            }

                                            let urls = []
                                            try {
                                                // 解析HTML获取图片地址
                                                urls = PubFun.getUrls(data);
                                                // 将解析到的第一个图片地址设置为封面
                                                comicList.value[index].face = await PubFun.cross(urls[0]);

                                                resolve()
                                            } catch (e) {
                                                console.error({
                                                    "漫画页地址": url_0,
                                                    "地址重定向转换": url,
                                                    "封面地址": urls,
                                                    "请求响应": res,
                                                    e
                                                });
                                                resolve()
                                            }
                                        })
                                        .catch((error) => {
                                            // 打印错误信息
                                            console.error(`请求失败${url_0}:\n ${error}`);
                                            resolve()
                                        });
                                } catch (error) {
                                    resolve()
                                }


                            })
                        };

                        // 获取重定向地址 通过eval直接执行地址重定向代码
                        const get_redirect_url_eval = (url) => {
                            return new Promise((resolve, reject) => {
                                try {
                                    // 定义自定义 window 对象
                                    const myWindow = {
                                        window: {
                                            location: {
                                                og: url,
                                                href: url,
                                                assign: (newUrl) => { myWindow.window.location.href = newUrl },
                                            }
                                        },
                                        location: {
                                            href: url,
                                            assign: (newUrl) => { myWindow.location.href = newUrl },
                                            replace: (newUrl) => { myWindow.location.href = newUrl }
                                        },
                                    };

                                    // 定义eval上下文
                                    function evalWithContext(code, context) {
                                        return (function () {
                                            // 通过参数解构来获取上下文中的变量和函数
                                            const contextKeys = Object.keys(context);
                                            const contextValues = contextKeys.map(key => context[key]);
                                            // 创建一个新的函数,该函数的参数是上下文中的变量名,并在函数体中执行代码
                                            const func = new Function(...contextKeys, code);
                                            // 调用函数,并将上下文中的变量值作为参数传递给函数
                                            return func(...contextValues);
                                        })();
                                    }

                                    axios.get(url)
                                        .then(response => {
                                            // 获取跳转代码
                                            const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/i;
                                            const match = response.data.match(scriptRegex);

                                            if (match && match[1]) {
                                                const scriptContent = match[1];

                                                // 执行跳转代码
                                                evalWithContext(scriptContent, myWindow);

                                                console.log(myWindow);

                                                if (myWindow.location.href.match("https")) {
                                                    resolve(myWindow.location.href);
                                                } else {
                                                    resolve("https://bbs.yamibo.com" + myWindow.location.href);
                                                }
                                            } else {
                                                reject(url);
                                            }
                                        })
                                        .catch(error => {
                                            console.error('get_redirect_url_eval错误:', myWindow, error);
                                            reject(url);
                                        });
                                } catch (error) {
                                    reject(url)
                                }

                            });
                        };

                        // 在eval失败后 通过iframe直接执行地址重定向代码
                        const get_redirect_url_iframe = (url) => {
                            return new Promise((resolve, reject) => {

                                // 创建一个 iframe 元素并设置其 src 属性
                                const iframe = document.createElement('iframe');
                                iframe.src = "";
                                // iframe.sandbox = "allow-same-origin allow-scripts"
                                iframe.style.display = "none"
                                document.body.appendChild(iframe)

                                iframe.onload = () => {
                                    console.log("get_redirect_url_iframe", iframe.contentDocument.location.href);
                                    resolve(iframe.contentDocument.location.href)
                                    document.body.removeChild(iframe)
                                }

                                iframe.contentDocument.location.href = url
                            })
                        }

                        // 渐进式加载漫画封面
                        const progressiveLoad = () => {
                            // 10个10个加载
                            const queue = PubFun.createTaskQueue(10);
                            comicList.value.forEach((item, index) => {
                                queue.addTask(() => getComicCover(item.url, index))
                            })
                        };


                        ///////////////////////////////////////////////////////////////////////////////
                        // 通过原始网页数据初始化漫画列表数据 获取对应漫画地址
                        const inti_manga_data = () => {
                            for (let i in fragment.children[0].children) {
                                const el = fragment.children[0].children[i];
                                if (typeof el === "object" && el.id && el.id.match("normalthread")) {
                                    comicList.value.push({
                                        id: el.id,
                                        innerHTML: el.innerHTML,
                                        children: el.children,
                                        url: url + el.id.match("normalthread_(.*)")[1]
                                    });
                                }
                            }
                        }
                        // 组件挂载后执行
                        onMounted(() => {
                            // 初始化漫画列表数据
                            inti_manga_data()
                            // 开始渐进式加载漫画封面
                            progressiveLoad();
                        });

                        ///////////////////////////////////////////////////////////////////////////////
                        // 修改列数
                        watch(columns, (new_val, old_val) => {
                            const el = document.querySelector(".mt_manga_message_container")
                            el.style.gridTemplateColumns = `repeat(${new_val}, 1fr)`;

                            GM_setValue("columns", new_val)
                        })
                        onMounted(() => {
                            const el = document.querySelector(".mt_manga_message_container")
                            el.style.gridTemplateColumns = `repeat(${columns.value}, 1fr)`;
                        })

                        ///////////////////////////////////////////////////////////////////////////////////////////
                        return {
                            comicList: computedComicList,
                            columns
                        };
                    }
                }
            )
                .mount("#moderate")
        }

    };


    // 漫画页面组件 ---------------------------------------------------------------------------
    class MangaComponent {
        constructor() {
            console.log("漫画页面加载");

            // 用于 与vue通信,是否加载漫画模式
            MangaComponent.model_state = 1

            // 百合会»论坛›江湖›貼圖區›中文百合漫画区›【提灯喵汉化组】[金子ある]平良深姐妹都“病”得不轻 1 ...
            // 获取索要创建按钮的位置
            MangaComponent.pt = document.querySelector("#pt")

            // 创建 模式切换按钮
            MangaComponent.mt_cut = document.createElement("div")
            MangaComponent.mt_cut.id = "mt_cut"
            MangaComponent.mt_cut.innerText = "[漫画模式]"
            // 切换模式
            MangaComponent.mt_cut.addEventListener("click", () => {
                if (MangaComponent.model_state === 1) {
                    document.querySelector("#mt_old").style.display = "none"
                    document.querySelector("#mt_manga").style.display = "block"
                    MangaComponent.mt_cut.innerText = "[原始模式]"
                    MangaComponent.model_state = 2
                } else {
                    document.querySelector("#mt_old").style.display = "block"
                    document.querySelector("#mt_manga").style.display = "none"
                    MangaComponent.mt_cut.innerText = "[漫画模式]"
                    MangaComponent.model_state = 1
                }
            })
            // 添加
            MangaComponent.pt.appendChild(MangaComponent.mt_cut)

            // 定义自己el,用于存放两种模式
            MangaComponent.my_el = document.createElement("div")
            MangaComponent.my_el.id = "my_el"
            // 在他的下方插入自己的el
            MangaComponent.pt.parentNode.insertBefore(MangaComponent.my_el, MangaComponent.pt.nextSibling)

            // html模板
            MangaComponent.my_el.innerHTML = `
                    <div id="mt_old"></div>
                    <div id="mt_manga" style="display: none;">
                        <div >
                            <div v-if="urlsLoaded" id = "mt_manga_box" @dblclick="fullScreen = !fullScreen"
                            :class="{is_one_p:onePage , not_one_p:!onePage , is_r_to_l:rightToLeft, is_w_bg:whiteBg}">
                                <div id="mt_set" v-show="showSettings">
                                    <div class="block">
                                        <el-slider v-model="imageSize"></el-slider>
                                        <el-checkbox v-model="rightToLeft">从右向左</el-checkbox>
                                        <el-checkbox v-model="onePage">单开</el-checkbox>
                                        <el-checkbox v-model="whiteBg">白底</el-checkbox>
                                        <el-radio>
                                            <span @click="download()">{{downloadInfo}}</span>
                                        </el-radio>
                                    </div>
                                </div>

                                <img :style="{'width':imageSize +'%'}"
                                    :src="url" class="mt_manga_img" v-for="url in urlList"
                                    @click="showSettings = !showSettings"
                                    >
                            </div>
                        </div>
                    </div>
                `
            // 样式
            MangaComponent.style = `
                        #mt_cut{
                            float: right;
                            font-weight: bold;
                        }
                        .mt_manga_img{
                        }
                        #mt_manga_box{
                            overflow: auto;
                            display: flex;
                        }
                        .is_one_p{
                            flex-direction: column;
                            flex-wrap: nowrap;
                            align-items: center
                        }
                        .not_one_p{
                            flex-wrap: wrap;
                            align-content: flex-start;
                            justify-content: center
                        }
                        .is_r_to_l{
                            flex-direction: row-reverse;
                        }
                        .is_w_bg{
                            background-color: white;
                        }

                        #mt_manga_box::-webkit-scrollbar{    /* 整体*/
                            width: 3px;
                            height: 10px
                        }
                        #mt_manga_box::-webkit-scrollbar-thumb{  /* 滑动条*/
                            background-color: rgb(208, 208, 208);
                            border-radius: 1px;
                        }
                        #mt_set{
                            width: 360px;
                            height: 70px;
                            background-color: rgb(235 179 82);
                            position: fixed;
                            border-radius: 5px;
                            padding: 10px;
                            top: 50%;
                            left: 50%;
                            transform: translate(-50%, -50%);
                        }
                    `
            GM_addStyle(MangaComponent.style)

            // 碎片 用户发布部分
            MangaComponent.f = PubFun.Fragment("#ct")
            document.querySelector("#mt_old").appendChild(MangaComponent.f)


            // 初始化数据
            // 解析页面中的图片地址
            MangaComponent.urls = PubFun.getUrls(document.querySelector("#mt_old").innerHTML);

            localStorage.getItem('size') ? null : localStorage.setItem('size', "100")

            // 创建
            createApp(
                {
                    setup() {
                        // 定义模式状态,1为默认模式,2为漫画模式
                        const model_state = ref(MangaComponent.model);
                        // 定义图片地址列表
                        const urlList = MangaComponent.urls;
                        // 定义图片地址是否加载完成状态
                        const urlsLoaded = ref(true);
                        // 定义是否全屏状态
                        const fullScreen = ref(false);
                        // 定义是否显示设置面板状态
                        const showSettings = ref(false);
                        // 定义下载按钮文字
                        const downloadInfo = ref("下载");
                        // 定义下载状态
                        const downloadState = reactive({ done: 0, error: 0, allTasks: 0, pack: 0 });
                        // 定义图片大小
                        const imageSize = ref(localStorage.getItem('size') - 0);
                        // 定义是否从右向左阅读
                        const rightToLeft = ref(localStorage.getItem("rightToLeft") === "1");
                        // 定义是否单页显示
                        const onePage = ref(localStorage.getItem("onePage") === "1");
                        // 定义是否使用白色背景
                        const whiteBg = ref(localStorage.getItem("whiteBg") === "1");

                        // 下载漫画
                        const download = async () => {
                            // 如果打包状态为0,则开始打包下载
                            if (downloadState.pack === 0) {
                                // 获取漫画名称
                                const packName = document.getElementById("thread_subject").innerText;
                                // 调用下载方法
                                await PubFun.fflate_zip(packName, urlList, downloadState);
                            }
                        };

                        // 监听图片大小变化
                        watch(imageSize, (newSize) => {
                            localStorage.setItem("size", newSize);
                        });
                        // 监听阅读方向变化
                        watch(rightToLeft, (newValue) => {
                            localStorage.setItem("rightToLeft", newValue ? "1" : "0");
                        });
                        // 监听单页显示状态变化
                        watch(onePage, (newValue) => {
                            localStorage.setItem("onePage", newValue ? "1" : "0");
                        });
                        // 监听白色背景状态变化
                        watch(whiteBg, (newValue) => {
                            localStorage.setItem("whiteBg", newValue ? "1" : "0");
                        });
                        // 监听下载状态变化
                        watch(downloadState, () => {
                            // 更新下载按钮文字
                            downloadInfo.value = `${downloadState.done}/${downloadState.allTasks}`;
                            if (downloadState.pack === 1) {
                                downloadInfo.value = "开始打包,请等待...";
                            }
                            if (downloadState.pack === 2) {
                                downloadInfo.value = "完成";
                            }
                        }, { deep: true });
                        // 全屏
                        watch(fullScreen, (new_val) => {
                            const elem = document.documentElement;
                            // 全屏模式的函数
                            function full_screen() {
                                if (elem.requestFullscreen) {
                                    elem.requestFullscreen();
                                } else if (elem.mozRequestFullScreen) { // 兼容 Firefox
                                    elem.mozRequestFullScreen();
                                } else if (elem.webkitRequestFullscreen) { // 兼容 Chrome, Safari 和 Opera
                                    elem.webkitRequestFullscreen();
                                } else if (elem.msRequestFullscreen) { // 兼容 IE/Edge
                                    elem.msRequestFullscreen();
                                }
                            }

                            // 退出全屏模式的函数
                            function exit_fullscreen() {
                                if (document.exitFullscreen) {
                                    document.exitFullscreen();
                                } else if (document.mozCancelFullScreen) { // 兼容 Firefox
                                    document.mozCancelFullScreen();
                                } else if (document.webkitExitFullscreen) { // 兼容 Chrome, Safari 和 Opera
                                    document.webkitExitFullscreen();
                                } else if (document.msExitFullscreen) { // 兼容 IE/Edge
                                    document.msExitFullscreen();
                                }
                            }

                            if (new_val) {
                                full_screen()
                            } else {
                                exit_fullscreen()
                            }
                        })


                        return {
                            model_state,
                            urlList,
                            urlsLoaded,
                            fullScreen,
                            showSettings,
                            downloadInfo,
                            downloadState,
                            imageSize,
                            rightToLeft,
                            onePage,
                            whiteBg,
                            download
                        };
                    }
                })
                .use(ElementPlus)
                .mount("#mt_manga")
        }
    };

    ///////////////////////////////////////////////////////////////////////////////////
    // 根据当前页面挂载组件
    // 主题页面
    if (window.location.href.match("https://bbs.yamibo.com/thread") || window.location.href.match(/forum.php\?mod=viewthread/)) {
        // 依赖加载完成
        document.querySelector("#element").onload = () => {
            new MangaComponent()
        }
    }

    // 主题列表页面
    else if (window.location.href.match("https://bbs.yamibo.com/forum")) {
        new MangaListComponent()
    }
})();