【tapd】一键查询所有项目中的wiki

为了方便在tapd的wiki中查找接口而开发

安装此脚本?
作者推荐脚本

您可能也喜欢一键生成 GitLab 周报汇总

安装此脚本
// ==UserScript==
// @name         【tapd】一键查询所有项目中的wiki
// @namespace    https://github.com/kiccer/tapd-search-wiki
// @version      3.7.2
// @description  为了方便在tapd的wiki中查找接口而开发
// @author       kiccer<[email protected]>
// @copyright    2020, kiccer (https://github.com/kiccer)
// @license      MIT
// @iconURL      https://www.google.com/s2/favicons?domain=www.tapd.cn
// @include      /^https:\/\/www\.tapd\.cn\/\d+\/markdown_wikis\/(show\/|search\?.*)$/
// @require      https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.js
// @require      https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.js
// @require      https://cdn.bootcdn.net/ajax/libs/tween.js/18.6.4/tween.umd.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/Sortable/1.9.0/Sortable.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/clipboard.js/2.0.6/clipboard.min.js
// @noframes     这个千万别删掉!会出现死循环的!
// @nocompat     Chrome
// @grant        none
// ==/UserScript==

/* global Vue axios TWEEN takePartInWorkspaces $ Sortable ClipboardJS */
// https://www.tampermonkey.net/documentation.php
// https://element.eleme.cn/#/zh-CN/component/button

(() => {
    'use strict'

    // 当前是否是 show 页面
    const IN_SHOW_PAGE = /^https:\/\/www\.tapd\.cn\/\d+\/markdown_wikis\/show\/.*$/.test(location.href)
    // 当前是否是 search 页面
    const IN_SEARCH_PAGE = /^https:\/\/www\.tapd\.cn\/\d+\/markdown_wikis\/search\?.*$/.test(location.href)
    // 当前项目id
    const CURR_PROJECT_ID = location.href.match(/(?<=https:\/\/www.tapd.cn\/)\d+(?=\/markdown_wikis\/)/g)[0] || ''
    // 随机字符串
    const GM_ADD_STYLE_HASH = `GM_addStyle_${parseInt(Math.random() * Date.now())}`
    // 从 session 中获取缓存的搜索词
    const SEARCH_WORD = sessionStorage.getItem('tapd-search-wiki/search_word') || ''
    // 页面 query 参数
    const URL_QUERY = (() => {
        const queryStr = location.href.split('?')[1]
        if (queryStr) {
            const res = {}
            queryStr.split('&').forEach(n => {
                const [key, val] = n.split('=')
                res[key] = val
            })
            return res
        } else {
            return {}
        }
    })()

    // GM_addStyle 方法
    function GM_addStyle (css, dom = document.head, id = GM_ADD_STYLE_HASH) {
        const style = document.getElementById(id) || (() => {
            const style = document.createElement('style')
            style.type = 'text/css'
            style.id = id
            dom.appendChild(style)
            return style
        })()
        const sheet = style.sheet
        // sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length)
        css.split('\n\n').forEach(n => sheet.insertRule(n, (sheet.rules || sheet.cssRules || []).length))
    }

    // 自写 Promise.all 方法
    function PromiseAll (arr = []) {
        return new Promise((resolve, reject) => {
            const resVal = Array(arr.length).fill()

            arr.forEach((func, index) => {
                func().then(res => {
                    resVal[index] = res
                    if (resVal.every(n => n)) resolve(resVal)
                }).catch(err => {
                    reject(err)
                })
            })
        })
    }

    // 加载 element-ui script
    function elementScript () {
        return new Promise((resolve, reject) => {
            const vueListener = setInterval(() => {
                // 注册(不可用)全局 Vue
                window.Vue || (window.Vue = Vue)
                if (window.Vue) {
                    clearInterval(vueListener)
                    const elScript = document.createElement('script')
                    elScript.setAttribute('src', 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/index.min.js')
                    document.head.appendChild(elScript)
                    elScript.addEventListener('load', resolve)
                    elScript.addEventListener('error', reject)
                }
            }, 100)
        })
    }

    // 加载 element-ui css
    function elementStyle () {
        return new Promise((resolve, reject) => {
            const elStyle = document.createElement('link')
            elStyle.setAttribute('href', 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/theme-chalk/index.min.css')
            elStyle.setAttribute('rel', 'stylesheet')
            document.head.appendChild(elStyle)
            elStyle.addEventListener('load', resolve)
            elStyle.addEventListener('error', reject)
        })
    }

    // 等待所有依赖项加载完毕后再执行
    PromiseAll([
        elementScript,
        elementStyle
    ]).then(() => {
        init()
    }).catch(err => {
        console.log(222, err)
    })

    // vue 组件 (搜索框)
    Vue.component('search-input', {
        name: 'search-input',

        template: `
            <div>
                <el-input
                    placeholder="在你所有项目的wiki中搜索..."
                    size="medium"
                    v-model="keyword"
                    @keydown.enter.native="search"
                >
                    <el-button
                        slot="append"
                        icon="el-icon-search"
                        :loading="loading"
                        @click="search"
                    />
                </el-input>
            </div>
        `,

        props: {
            enter: Function,
            loading: Boolean
        },

        data () {
            return {
                keyword: ''
            }
        },

        created () {
            if (IN_SEARCH_PAGE) {
                this.keyword = SEARCH_WORD || decodeURIComponent(URL_QUERY.search) || ''
            }
        },

        methods: {
            search () {
                if (this.loading) return

                // 去除前后空格
                this.keyword = this.keyword.replace(/^\s+|\s+$/g, '')

                // 保存搜索词
                sessionStorage.setItem('tapd-search-wiki/search_word', this.keyword)

                // 如果绑定了 enter 方法,那就支持无刷新更新数据
                if (this.enter) {
                    this.enter(this.keyword)
                } else {
                    location.href = `https://www.tapd.cn/${CURR_PROJECT_ID}/markdown_wikis/search?search=${encodeURIComponent(this.keyword.replace(/\*/g, ' '))}`
                }
            }
        }
    })

    // 初始化
    function init () {
        // 添加 vue 容器
        const headerBar = document.getElementById('hd')
        const app = document.createElement('div')
        const mainSearchArea = document.querySelector('.main-search-area')
        headerBar.appendChild(app)
        mainSearchArea && headerBar.removeChild(mainSearchArea)

        new Vue({
            el: app,

            name: 'kiccer-tampermonkey-tapd-wiki-search',

            template: `
                <div class="kiccer-tampermonkey-tapd-wiki-search">
                    <search-input />
                </div>
            `
        })

        // 如果是 search 页面则添加搜索结果列表容器
        if (IN_SEARCH_PAGE) {
            const searchResultContainer = document.querySelector('.search-result')

            new Vue({
                el: searchResultContainer,

                name: 'kiccer-tampermonkey-tapd-wiki-result',

                template: `
                    <div class="search-result">
                        <!-- 左侧搜索结果列表 -->
                        <div class="wiki-list">
                            <search-input
                                :loading="!allLoaded"
                                :enter="onSearchInputEnter"
                            />

                            <el-alert
                                title="支持匹配符号:* (任意个数任意字符)"
                                type="info"
                            />

                            <iframe
                                class="hide-iframe"
                                v-for="(n, i) in projects"
                                :key="n.id"
                                :src="iframeSrc(n)"
                                @load="e => iframeLoaded(e, i)"
                            />

                            <el-tabs
                                type="card"
                                v-model="activeTab"
                                v-if="projectsInTab.length"
                            >
                                <el-tab-pane
                                    v-for="(n, i) in projectsInTab"
                                    :key="n.id"
                                    :label="n.project_name"
                                    :name="n.pretty_name"
                                >
                                    <div
                                        class="tab-label"
                                        slot="label"
                                        v-html="tabLabelHtml(n.index)"
                                    />

                                    <transition name="fade">
                                        <div v-if="n.pretty_name === activeTab">
                                            <!-- <div v-html="wikiHTMLList[n.index]" /> -->
                                            <component
                                                :is="wikiHtmlComp(wikiHTMLList[n.index])"
                                                @open-preview="openPreview"
                                            />

                                            <el-pagination
                                                layout="prev, pager, next"
                                                :current-page.sync="n.pageInfo.current"
                                                :page-count="n.pageInfo.total"
                                                v-if="n.pageInfo.total > 1"
                                            />
                                        </div>
                                    </transition>
                                </el-tab-pane>
                            </el-tabs>

                            <div v-else>{{ allLoaded ? '啥也没找到' : '正在搜索中' }}...</div>

                            <transition name="fade">
                                <div
                                    class="back-top"
                                    v-show="toggle.showBackTop"
                                    @click="backTop"
                                >
                                    <i class="el-icon-arrow-up" />
                                </div>
                            </transition>
                        </div>
                        
                        <!-- 右侧预览页面 -->
                        <div
                            class="wiki-preview"
                            v-show="previewFrames.length"
                        >
                            <el-tabs
                                editable
                                type="card"
                                v-sortable="{
                                    list: previewFrames,
                                    container: '.el-tabs__nav',
                                    draggable: '.el-tabs__item'
                                }"
                                v-model="activePreviewTab"
                                @tab-remove="removeWikiPreviewIframe"
                                @tab-add="addWikiPreviewIframe"
                            >
                                <el-tab-pane
                                    v-loading="n.loading"
                                    v-for="(n, i) in previewFrames"
                                    :key="n.url"
                                    :name="n.url"
                                >
                                    <el-tooltip
                                        slot="label"
                                        effect="light"
                                        placement="bottom"
                                        :open-delay="500"
                                    >
                                        <div slot="content" style="line-height: 1.5;">
                                            <h3>{{ n.name }}</h3>
                                            <p><a :href="n.url" target="_blank">{{ n.url }}</a></p>
                                            <template v-if="n.keywordList && n.keywordList.length > 1">
                                                <p>关键词:(点击关键词触发滚动预测)</p>
                                                <p
                                                    v-for="keyword in n.keywordList"
                                                    :key="keyword"
                                                >
                                                    <el-button
                                                        type="text"
                                                        icon="el-icon-close"
                                                        style="padding: 0px; margin-right: 10px;"
                                                        @click="removeKeywordItem(n, keyword)"
                                                    />

                                                    <a
                                                        :class="{
                                                            red: keyword === n.wd
                                                        }"
                                                        @click="scrollForecastItem(n, keyword)"
                                                    >{{ keyword }}</a>
                                                </p>
                                            </template>
                                            <p>(注:双击标题可以激活滚动预测功能)</p>
                                        </div>

                                        <span
                                            class="ellipsis"
                                            @dblclick="scrollForecast(n)"
                                        >{{ n.name || n.url }}</span>
                                    </el-tooltip>

                                    <transition name="fade">
                                        <iframe
                                            class="wiki-preview-iframe"
                                            v-show="n.url === activePreviewTab"
                                            :src="n.url"
                                            @load="e => previewIframeLoaded(e, n)"
                                        />
                                    </transition>
                                </el-tab-pane>
                            </el-tabs>
                        </div>

                        <!-- 增加预览页面弹窗 -->
                        <el-dialog
                            title="增加预览页面"
                            width="30%"
                            :visible.sync="toggle.addPreviewPopup"
                            @closed="addPreviewPopupClosed"
                        >
                            <el-input
                                autosize
                                type="textarea"
                                placeholder="请输入网址"
                                v-model="addPreviewPopupData.url"
                                @keydown.enter.native="addPreviewFrame"
                            />

                            <span slot="footer" class="dialog-footer">
                                <el-button
                                    @click="toggle.addPreviewPopup = false"
                                >取 消</el-button>

                                <el-button
                                    type="primary"
                                    @click="addPreviewFrame"
                                >确 定</el-button>
                            </span>
                        </el-dialog>
                    </div>
                `,

                data () {
                    return {
                        toggle: {
                            showBackTop: window.scrollY >= 200,
                            addPreviewPopup: false
                        },
                        projects: [],
                        wd: '',
                        wikiHTMLList: [],
                        loaded: [],
                        scroll: { x: 0, y: 0 },
                        activeTab: '',
                        previewFrames: [],
                        activePreviewTab: sessionStorage.getItem('tapd-search-wiki/active_preview_tab') || '',
                        addPreviewPopupData: {
                            url: ''
                        }
                    }
                },

                computed: {
                    allLoaded () {
                        return !(this.loaded || []).includes(false)
                    },

                    projectsInTab () {
                        return this.projects.filter((n, i) => this.wikiHTMLList[i])
                    }
                },

                watch: {
                    allLoaded (val, old) {
                        if (val) {
                            const firstTab = this.projectsInTab[0]
                            this.activeTab = firstTab ? firstTab.pretty_name : ''
                        }
                    },

                    previewFrames: {
                        handler (val, old) {
                            // console.log(111, val)
                            sessionStorage.setItem('tapd-search-wiki/preview_frames', JSON.stringify(val))
                        },
                        deep: true
                    },

                    activePreviewTab (val, old) {
                        sessionStorage.setItem('tapd-search-wiki/active_preview_tab', val)
                    }
                },

                created () {
                    this.wd = SEARCH_WORD || decodeURIComponent(URL_QUERY.search) || ''
                    this.previewFrames = JSON.parse(sessionStorage.getItem('tapd-search-wiki/preview_frames')).map(n => ({
                        ...n,
                        loading: true
                    })) || []
                },

                mounted () {
                    // 设置返回顶部按钮
                    this.setBackTopBtn()

                    // 获取所有项目 id
                    axios({
                        url: 'https://www.tapd.cn/company/my_take_part_in_projects_list?project_id=' + CURR_PROJECT_ID
                    }).then(res => {
                        // console.log(res.data)
                        this.projects = takePartInWorkspaces.map((n, i) => ({
                            ...n,
                            index: i,
                            pageInfo: {
                                current: 1,
                                total: 1
                            },
                            switches: JSON.parse(n.switches)
                        }))
                        this.wikiHTMLList = Array(this.projects.length).fill().map(_ => '')
                        this.loaded = Array(this.projects.length).fill().map(_ => false)
                    })
                },

                methods: {
                    iframeLoaded (e, i) {
                        const frameBody = e.path[0].contentDocument.body
                        const list = frameBody.querySelector('.wiki-list')
                        const page = frameBody.querySelector('.simple-pager .current-page')
                        const [current, total] = page ? page.innerText.split('/').map(n => +n) : [1, 1]
                        // console.log([current, total])
                        this.$set(this.wikiHTMLList, i, list ? list.innerHTML : '')
                        this.$set(this.loaded, i, true)
                        this.$set(this.projects[i].pageInfo, 'current', current)
                        this.$set(this.projects[i].pageInfo, 'total', total)
                    },

                    onSearchInputEnter (val) {
                        if (val === this.wd) return
                        this.wd = val
                        this.loaded = Array(this.projects.length).fill().map(_ => false)
                        this.projects.forEach((n, i) => {
                            this.$set(this.projects[i].pageInfo, 'current', 1)
                        })
                    },

                    setBackTopBtn () {
                        function animate (time) {
                            requestAnimationFrame(animate)
                            TWEEN.update(time)
                        }
                        requestAnimationFrame(animate)
                        window.addEventListener('scroll', e => {
                            // console.log(e)
                            this.toggle.showBackTop = window.scrollY >= 200
                        })
                    },

                    backTop () {
                        this.scroll = {
                            x: window.scrollX,
                            y: window.scrollY
                        }

                        new TWEEN.Tween(this.scroll) // Create a new tween that modifies 'coords'.
                            .to({ x: 0, y: 0 }, 500) // Move to (300, 200) in 1 second.
                            .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
                            .onUpdate(() => {
                                // Called after tween.js updates 'coords'.
                                // Move 'box' to the position described by 'coords' with a CSS translation.
                                window.scrollTo(this.scroll.x, this.scroll.y)
                            })
                            .start() // Start the tween immediately.
                    },

                    // 搜索结果列表标签结构
                    tabLabelHtml (index) {
                        const projectInfo = this.projects[index]
                        const logo = projectInfo.logo_src
                            ? `<img class="project-logo" src="${projectInfo.logo_src}" />`
                            : `<i class="project-logo project-logo-${projectInfo.logoId}">${projectInfo.project_name[0]}</i>`

                        return `
                            <div class="current-project">
                                ${logo}
                                <span class="project-name">${projectInfo.project_name}</span>
                            </div>
                        `
                    },

                    iframeSrc (n) {
                        return `https://www.tapd.cn/${n.id}/markdown_wikis/search?search=${encodeURIComponent(this.wd.replace(/\*/g, ' '))}&page=${n.pageInfo.current}`
                    },

                    // wiki搜索结果列表组件 (为了让右侧打开按钮绑定事件)
                    wikiHtmlComp (html) {
                        const urls = html.match(/(?<=<a target="_blank" href=").+?(?=">)/g)
                        const names = html.match(/(?<=<div class="one-wiki-title" title=").+(?=">)/g)
                        let index = -1

                        html = html.replace(/(?<=<div class="one-wiki-title" title=".+">)[\s\n]+?(?=<a target="_blank" href=")/g, _ => {
                            index++
                            return `
                                <el-button
                                    type="text"
                                    icon=""
                                    @click="$emit('open-preview', {
                                        url: '${urls[index]}',
                                        name: '${names[index]}'
                                    })"
                                >
                                    在右侧打开预览
                                    <i class="el-icon-d-arrow-right el-icon--right" />
                                </el-button>
                            `
                        })

                        return {
                            name: 'wiki-html-comp',
                            template: `
                                <div>
                                    ${html}
                                </div>
                            `
                        }
                    },

                    // 打开预览页面
                    openPreview ({ url, name }) {
                        // console.log(url, name)
                        this.activePreviewTab = url

                        if (this.previewFrames.every(n => n.url !== url)) {
                            this.previewFrames.push({
                                url,
                                name,
                                wd: this.wd,
                                keywordList: [this.wd],
                                loading: true
                            })
                        } else {
                            const frameInfo = this.previewFrames.find(n => n.url === url)
                            if (frameInfo) {
                                frameInfo.keywordList = [...new Set([
                                    ...(frameInfo.keywordList || []),
                                    this.wd
                                ])]
                                this.$set(frameInfo, 'wd', this.wd)
                                // 滚动预测
                                this.scrollForecast(frameInfo)
                            }
                        }
                    },

                    // 关闭预览页面
                    removeWikiPreviewIframe (url) {
                        const lastUrl = (this.previewFrames.slice(-1)[0] || {}).url || ''
                        let prevUrl = ''

                        this.previewFrames = this.previewFrames.filter((n, i) => {
                            const isRemove = n.url === url

                            if (this.activePreviewTab === n.url && isRemove) {
                                this.activePreviewTab = prevUrl || lastUrl || ''
                            }

                            prevUrl = n.url

                            return n.url !== url
                        })
                    },

                    // 增加预览页面
                    addWikiPreviewIframe () {
                        this.toggle.addPreviewPopup = true
                    },

                    // 当预览页面加载完毕时
                    previewIframeLoaded (e, frameInfo) {
                        // 保存 document
                        this.$set(frameInfo, 'loading', false)
                        this.$set(frameInfo, 'document', e.path[0].contentDocument)

                        // 如果没有标题就设置标题
                        if (!frameInfo.name) {
                            const timer = setInterval(() => {
                                const dom = frameInfo.document.querySelector('#wikiName')
                                if (dom) {
                                    this.$set(frameInfo, 'name', dom.innerText)
                                    clearInterval(timer)
                                }
                            }, 100)
                        }

                        // 滚动预测
                        this.scrollForecast(frameInfo)
                        // 增加锚点链接复制按钮
                        this.addAnchorLinkCopyButton(frameInfo)

                        // 样式覆盖
                        GM_addStyle(`
                            #display_headers,
                            #headers_block,
                            #left-tree,
                            #wiki_tag,
                            #wiki_attachment,
                            #wiki_comment,
                            .wiki-nav,
                            .nav-main-wrap,
                            .main-search-area,
                            .cloud-guide-switch,
                            .toolbar,
                            .wiki-option-warp,
                            .attachment-upload-wrap,
                            .wiki-nav-small {
                                display: none !important;
                            }

                            .project-nav {
                                left: 0;
                                min-width: auto;
                            }

                            .frame-main {
                                min-width: auto !important;
                            }

                            .wiki-main {
                                padding: 20px !important;
                                margin-left: 0 !important;
                                max-width: 100%;
                            }

                            #page-content, #page-wrapper {
                                margin-left: 0 !important;
                            }

                            .tui-skin-lego #page-content {
                                min-width: auto;
                            }

                            .wiki-tag-wrapper {
                                margin-right: 0;
                            }

                            .wiki-wrap {
                                margin-top: 15px !important;
                            }

                            .cherry-markdown .cherry-table-container .cherry-table td {
                                min-width: auto;
                            }
                        `, frameInfo.document.head, 'wiki-preview-iframe-css')
                    },

                    // 点击 tooltips 中的关键词进行滚动
                    scrollForecastItem (frameInfo, keyword) {
                        frameInfo.wd = keyword
                        this.scrollForecast(frameInfo)
                    },

                    // 移除关键字
                    removeKeywordItem (frameInfo, keyword) {
                        frameInfo.keywordList = frameInfo.keywordList.filter(n => n !== keyword)
                        if (keyword === frameInfo.wd) {
                            frameInfo.wd = frameInfo.keywordList[0] || ''
                        }
                    },

                    // 滚动预测
                    scrollForecast (frameInfo) {
                        setTimeout(() => {
                            const {
                                // url,
                                // name,
                                wd = '',
                                document
                            } = frameInfo

                            if (!(wd && document)) return

                            let isFind = false // 只滚动至第一个找到的元素位置

                            ;[
                                $(document).find('#searchable > *'),
                                $(document).find('#wiki_right .wiki-body .wiki-info + * > *')
                            ].forEach(queryDoms => {
                                queryDoms.each((i, n) => {
                                    if (isFind) return
                                    if (new RegExp(wd.split(' ').join('|').replace(/\*/g, '.*?'), 'ig').test($(n).text())) {
                                        const wikiRight = document.querySelector('#wiki_right')
                                        const tweenData = {
                                            x: wikiRight.scrollLeft,
                                            y: wikiRight.scrollTop
                                        }

                                        if (n.offsetTop + 100 !== wikiRight.scrollTop) {
                                            new TWEEN.Tween(tweenData) // Create a new tween that modifies 'coords'.
                                                .to({ x: 0, y: n.offsetTop + 100 }, 500) // Move to (300, 200) in 1 second.
                                                .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
                                                .onUpdate(() => {
                                                    // Called after tween.js updates 'coords'.
                                                    // Move 'box' to the position described by 'coords' with a CSS translation.
                                                    wikiRight.scrollTo(tweenData.x, tweenData.y)
                                                })
                                                .start() // Start the tween immediately.
                                        }

                                        isFind = true
                                    }
                                })
                            })
                        })
                    },

                    // 增加锚点链接复制按钮
                    addAnchorLinkCopyButton (frameInfo) {
                        setTimeout(() => {
                            const {
                                url,
                                // name,
                                // wd = '',
                                document
                            } = frameInfo

                            ;[
                                $(document).find('#searchable > *'),
                                $(document).find('#wiki_right .wiki-body .wiki-info + * > *')
                            ].forEach(queryDoms => {
                                queryDoms.each((i, n) => {
                                    const anchorDiv = $(n).find('.anchor-div')[0]

                                    if (anchorDiv) {
                                        // 复制按钮
                                        const copyBtn = $(`
                                            <div
                                                title="点击复制锚点链接"
                                                class="anchor-div font-public font font-copy"
                                                style="position: relative; height: 20px; width: 22px;"
                                                data-clipboard-text="${url}@${$(anchorDiv).attr('data-head')}"
                                            ></div>
                                        `).appendTo(n)

                                        // 复制代码
                                        const clipboard = new ClipboardJS(copyBtn[0])

                                        clipboard.on('success', e => {
                                            this.$message.success('已复制锚点链接到剪贴板 ٩(๑>◡<๑)۶ ')
                                        })

                                        clipboard.on('error', e => {
                                            this.$message.error('复制失败!请重试! (;´д`)ゞ')
                                        })
                                    }
                                })
                            })
                        }, 1000)
                    },

                    // 在增加预览页面弹窗关闭之后
                    addPreviewPopupClosed () {
                        this.addPreviewPopupData.url = ''
                    },

                    // 增加预览页面(弹窗点击确定)
                    addPreviewFrame () {
                        const url = this.addPreviewPopupData.url

                        if (!url) {
                            this.$message.error('请输入网址')
                            return
                        }

                        if (!/^https:\/\/www\.tapd\.cn\/\d+\/markdown_wikis\/show\/#\d+/.test(url)) {
                            this.$message.error('只能输入wiki结果页地址 ')
                            return
                        }

                        const item = this.previewFrames.find(n => n.url === url)

                        this.activePreviewTab = url

                        if (!item) {
                            this.previewFrames.push({ url })
                        }

                        this.toggle.addPreviewPopup = false
                    }
                },

                directives: {
                    sortable (el, binding, vnode, oldVnode) {
                        // console.log({ el, binding, vnode, oldVnode })
                        const {
                            container,
                            draggable,
                            list = []
                        } = (binding.value || {})

                        el.sortable = new Sortable(el.querySelector(container), {
                            draggable,
                            animation: 150,
                            easing: 'cubic-bezier(1, 0, 0, 1)',
                            onEnd: (evt) => {
                                // console.log(evt)
                                const {
                                    newIndex,
                                    oldIndex
                                } = evt

                                const _list = list.map(n => n.url)
                                const item = _list.splice(oldIndex)[0]
                                _list.splice(newIndex, 0, item)
                                list.sort((n, m) => _list.indexOf(n.url) - _list.indexOf(m.url))
                            }
                        })
                    }
                }
            })
        }
    }

    // 公共样式
    GM_addStyle(`
        * {
            outline: none;
        }

        .kiccer-tampermonkey-tapd-wiki-search {
            float: right;
            height: 100%;
            display: flex;
            align-items: center;
            margin-right: 15px;
        }

        .kiccer-tampermonkey-tapd-wiki-search .el-input input {
            width: 200px;
        }

        .fade-enter-active, .fade-leave-active {
            transition: opacity .5s;
        }

        .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
            opacity: 0;
        }
    `)

    // 搜索页样式
    if (IN_SEARCH_PAGE) {
        GM_addStyle(`
            .search-result {
                min-height: calc(100vh - 184px) !important;
            }

            .el-tabs__item {
                padding: 0 10px !important;
                user-select: none;
            }

            .wiki-list-wrapper {
                padding: 20px;
                margin-bottom: 20px;
                border-radius: 4px;
                // box-shadow: 0 0 10px rgba(128,145,165,0.2);
                border: 1px solid #dcdfe6;
            }

            .wiki-list .el-alert {
                margin-bottom: 20px;
            }

            .wiki-list .el-tabs__item {
                padding: 0 10px !important;
            }

            .wiki-list .el-tabs__item.is-active .project-name {
                font-weight: bold;
            }

            .wiki-list .el-tabs__header {
                margin-bottom: 0;
            }

            .wiki-list .el-tabs__content {
                border: 1px solid #e4e7ed;
                border-top: none;
                border-radius: 0 0 4px 4px;
                padding: 15px;
            }

            .wiki-list .el-pagination {
                text-align: right;
            }

            .wiki-list .tab-label {
                display: inline-flex;
                align-items: center;
                width: auto;
                height: 100%;
            }

            .wiki-list .current-project {
                display: inline-block;
                width: auto;
                min-width: auto;
                margin: 0 !important;
                height: 24px;
                line-height: 24px;
            }

            .wiki-list .el-input {
                margin-bottom: 20px;
            }

            .wiki-list .el-input input {
                width: 100%;
            }

            .wiki-list .back-top {
                display: flex;
                justify-content: center;
                align-items: center;
                width: 50px;
                height: 50px;
                background-color: #f5f7fa;
                position: fixed;
                left: 790px;
                bottom: 61px;
                border: 1px solid rgb(220, 223, 230);
                font-size: 20px;
                border-radius: 4px;
                cursor: pointer;
            }

            .wiki-list .one-wiki-title .el-button {
                padding: 0;
                float: right;
                line-height: 21px;
            }

            .hide-iframe {
                display: none;
            }

            .wiki-preview {
                position: fixed;
                top: 124px;
                right: 60px;
                bottom: 60px;
                left: 860px;
            }

            .wiki-preview .wiki-preview-iframe {
                width: 100%;
                height: 100%;
                border-radius: 4px;
                border: none;
            }

            .wiki-preview .el-tabs {
                height: 100%;
            }

            .wiki-preview .el-tabs .el-tabs__header {
                margin-bottom: 0;
            }

            .wiki-preview .el-tabs .el-tabs__content {
                height: calc(100% - 41px);
                border: 1px solid #e4e7ed;
                border-top: none;
                border-radius: 0 0 4px 4px;
            }

            .wiki-preview .el-tabs .el-tabs__content .el-tab-pane {
                height: 100%;
            }

            .wiki-preview .el-tabs__new-tab:focus {
                outline: none;
            }

            .wiki-preview .el-tabs__item .ellipsis {
                display: inline-block;
                line-height: 1;
                max-width: 150px;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                margin-bottom: -2px;
            }

            a.red:hover {
                color: red;
            }
        `)
    }

    // 展示页样式
    if (IN_SHOW_PAGE) {
        // GM_addStyle(``)
    }
})()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址