uploadImgeToGitlab

upload imge to gitlab

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         uploadImgeToGitlab
// @namespace    http://tampermonkey.net/
// @version      0.12
// @description  upload imge to gitlab
// @author       shinwoow & axia
// @match        https://knowledgeplanet.genew.com/-/ide/project/visulization/project-web-components-store/**/publish/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @license      MIT
// ==/UserScript==


const imgSub = ['png', 'jpg', 'jpeg', 'tif', 'psd', 'icon']

const preUrl = '/store/api/' // http://newdev.rdapp.com:10066
const gitPreUrl = 'https://knowledgeplanet.genew.com/visulization/project-web-components-store/raw/publish/dist/'

const listenDist = ['dist/coverImages/', 'dist/images']

const listNameMap = {
    coverImages: '封面图',
    images: '定制库'
}

let jsonData = []

let curUploadPath = ''
let curDisplayPath = ''
let curDisplayStyle = ''
let showStatus = ''

// 拦截响应
var originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
    // 全部请求相关信息
    var self = this;

    // 监听readystatechange事件
    this.addEventListener('readystatechange', function() {
        // 当readyState变为4时获取响应
        if (self.readyState === 4) {
            if(self.responseURL.includes('publish?format=json')){
                jsonData = JSON.parse(self.response).filter(item => listenDist.some(i => item.startsWith(i)))
            }
        }

    })

    // 调用原始的send方法
    originalSend.apply(this, arguments);
};

async function sleep(ms){
    return new Promise(resolve => setTimeout(() => {console.log('等待' + ms + 'ms');resolve()}, ms))
}

function replacePreCustomStr(str) {
    const list = Object.keys(listNameMap)
    for(let i = 0; i< list.length; i++) {
        if(str.startsWith(list[i])) {
            return str.replace(list[i], listNameMap[list[i]])
        }
    }

    return str
}

function resetPreCustomStr(str) {
    let list = []
    let keys = []

    Object.entries(listNameMap).forEach(([key, val]) => {
        list.push(val)
        keys.push(key)
    })
    for(let i = 0; i< list.length; i++) {
        if(str.startsWith(list[i])) {
            return str.replace(list[i], keys[i])
        }
    }
}

function findElByInnerText(el, text) {
    if(Array.isArray(text)) {
        for(let i = 0; i< text.length; i++) {
            const f = Array.prototype.filter.call(el, item => item.innerText.trim() === text[i]).at(0)

            if(f) return f
        }
    } else {
        return Array.prototype.filter.call(el, item => item.innerText.trim() === text).at(0)
    }
}


function toast(msg) {
    const toastEl = document.createElement('div')
    toastEl.className = 'shin-toast'

    toastEl.innerHTML = `
        <p>${msg}</p>
    `
    document.body.append(toastEl)

    setTimeout(() => {toastEl.remove()}, 1500)
}

// 展开所有 folder
async function openImageFolder (level = 0) {
    const folderList = document.getElementsByClassName('file-row folder')

    let count = 0
    Array.prototype.forEach.call(folderList, item => {
        if(!item.classList.contains('is-open')){
            item.click()
            count++
        }
    })

    if(level < 3 && count) {
        await sleep(100)
        await openImageFolder(++level)
    }
}



async function uploadImge() {
    // 等待页面 dom 加载完毕
    while(!document.getElementsByClassName('file-row folder')){
        await sleep(1000)
    }

    await openImageFolder()

    await sleep(100)

    const folderElList = document.getElementsByClassName('file-row folder')
    const transPath = resetPreCustomStr(curUploadPath)
    const imageFolderEl = transPath.split('/').reduce((pre, cur) => findElByInnerText(folderElList, cur), folderElList)
    console.log(transPath)

    const menuListEl = imageFolderEl.getElementsByClassName('dropdown-menu dropdown-menu-right')[0]
    const uploadBtn = findElByInnerText(menuListEl.children, ['上传文件', 'Upload file'])
    const uploadInput = uploadBtn.querySelector('#file-upload')
    uploadInput.click()

    async function inputLinsten (...rest) {
        uploadInput.removeEventListener("input", inputLinsten) // 取消监听

        const commitBtn = document.querySelector('.qa-begin-commit-button')

        try {
            // 自动提交
            await sleep(100)
            const commitBtn = document.querySelector('.qa-begin-commit-button')
            commitBtn.click()

            await sleep(100)
            const pushBtn = document.querySelector('.qa-commit-button')
            pushBtn.click()
        } catch(err) {
            alert('自动提交失败,请点击左下角手动提交')

        }
    }


    uploadInput.addEventListener("input", inputLinsten)
}

function getImgList() {
    curDisplayPath ||= getfolderList().at(0)
    const imgNamelist = jsonData.reduce((pre, cur) => {
        if(cur.replace(`dist/${curDisplayPath}/`, '').indexOf('/') === -1) {
            pre.push(cur.split('/').pop())
        }
        return pre
    }, [])

    return imgNamelist
}

const getImgItem = (name) => {
    if(!name) {
        return '<div style="width: 200px;height:0;"></div>'
    }

    return `
        <div class="img-item-content">
            <img loading="lazy" class="img-item" src="${gitPreUrl + curDisplayPath}/${name}" alt="加载失败,请稍后再试" />
            <div style="cursor: pointer;color: rgba(0,0,0,0.8); font-size: 20px;" class="copy-name singe-line" title="${name}">
                ${name}
            </div>
        </div>
    `
}

const copy = (text) => {
    // 挂载一个不可见元素
    const target = document.createElement('input');
    target.readOnly = 'readonly';
    target.setAttribute('value', text);
    target.id = 'tempTarget';
    target.style.opacity = '0';
    target.style.position = 'fixed';
    target.style.left = '-9999px';
    target.style.top = '0px';
    target.style.zIndex = '-9999';
    document.body.appendChild(target);

    if (target.value === '') {
        console.log('copy 空字符串无法被复制');
        toast('空字符串无法被复制')
    } else {
        try {
            // 选中、复制操作
            const selected =
                  document.getSelection().rangeCount > 0
            ? document.getSelection().getRangeAt(0)
            : false;
            target.focus();
            target.select();
            document.execCommand('copy');
            target.setSelectionRange(0, 0);
            target.blur();

            if (selected) {
                window.getSelection().removeAllRanges();
                document.getSelection().addRange(selected);
            }

            toast('复制成功')
        } catch (e) {
            toast('复制失败')
        }
    }

    // 移除添加的元素
    document.body.removeChild(target);
}

function setStyle () {
    // 样式
    const styleEl = document.createElement('style')
    styleEl.setAttribute('type', 'text/css')
    styleEl.innerHTML = `
        .white-background {
            background: #fff;
            opacity: 1 !important;
        }
        .shin-close {
            font-size: 66px;
            font-weight: 100;
            position: absolute;
            z-index: 10087;
            right: 8px;
            top: -18px;
        }
        .shin-scroll-bar::-webkit-scrollbar-track {
            -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
            border-radius: 10px;
            background-color: #fff;
        }
        .shin-scroll-bar::-webkit-scrollbar {
            width: 5px;
            height: 5px;
            background-color: #fff;
        }
        .shin-scroll-bar::-webkit-scrollbar-thumb {
            border-radius: 10px;
            -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
            background-color: #555;
        }
        .img-list {
            position: fixed;
            bottom: 0;
            height: 180px;
            display: flex;
            overflow: auto;
            white-space: nowrap;
            background: #fff;
            padding: 4px;
            width:100%;
        }
        .img-item-content {
            flex: 1;
            text-align: center;
            border: 1px solid transparent;
        }
        .img-item-content:hover .img-item {
            border-color: blue;
        }
        .img-item {
            width: 200px;
            height: 120px;
            display: inline-block;
            cursor: pointer;
            border: 1px solid transparent;
            border-radius: 4px;
            box-shadow: 12px 17px 51px rgba(0, 0, 0, 0.22);
            transition: .3s all ease-in-out;
        }
        .img-item-content:hover .img-item {
            border-color: #000;
            transform: scale(1.05);
            box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.22);
        }
        .img-preview {
            position: relative;
            margin: 0 55px;
            height: calc(100% - 226px);
        }
        .img-title-wrapper {
            background: #fff;
            padding: 8px 16px;
            width: 100%;
            overflow: hidden;
            box-shadow: 0px 1px 2px #1e1f21;
            position: relative;
        }
        #img-title {
            font-size: 20px;
            color: rgba(0,0,0,0.9);
        }
        #shin-img-size {
            border: 1px solid green;
            background: green;
            padding: 2px 4px;
            color: #fff;
            border-radius: 4px;
        }
        #shin-img-preview {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            max-height: calc(100% - 180px);
            cursor: pointer;
        }
        .shin-before,
        .shin-after{
            color: #000;
            opacity: 0.1;
            font-size: 50px;
            font-weight: 100;
            margin: auto 0;
            cursor: pointer;
            padding-bottom: 50px;
            transform: translateX(0px);
            transition: all .1s;
        }
        .shin-before:hover,
        .shin-after:hover {
            opacity: 0.8;
        }
        .shin-before:hover {
            transform: translateX(-3px);
        }
        .shin-after:hover {
            transform: translateX(3px);
        }
        .shin-selete-display-folder {
            position: absolute;
            top: 50%;
            right: 180px;
            transform: translateY(-50%);
        }
        .img-list-content {
            flex: 1;
            display: flex;
            padding: 4px 0;
        }
        .shin-toast {
            border: 1px solid #ebeef5;
            border-radius: 4px;
            padding: 8px 10px;
            z-index: 1080;
            color: #fff;
            box-sizing: border-box;
            min-width: 380px;
            position: fixed;
            left: 50%;
            top: 20px;
            transform: translateX(-50%);
            background-color: #edf2fc;
            transition: opacity 0.3s, transform .4s, top 0.4s;
            overflow: hidden;
            padding: 15px 15px 15px 20px;
            display: flex;
            align-items: center;
        }
        .shin-toast p {
            padding-right: 16px;
            margin: 0;
            color: #909399;
        }
        .img-list-wrap {
            display: flex;
            flex-wrap: wrap;
            padding: 24px;
            height: calc(100vh - 26px);
            overflow: auto;
            scrollbar-width: none; // firefox
            -ms-overflow-style: none; // ie10+
        }
        .img-list-wrap::-webkit-scrollbar {
            display: none; // chrome safari
        }
        .singe-line {
            max-width: 200px;
            text-overflow: ellipsis;
            overflow: hidden;
            word-break: break-all;
            white-space: nowrap;
        }
        .img-display-mode {
            position: absolute;
            top: 50%;
            right: 80px;
            transform: translateY(-50%);
            display: flex;
            font-size: 14px;
            border: 2px solid #8b8b8b;
            border-radius: 4px;


        }
        .img-display-mode-item {
            flex: 1;
            padding: 2px 8px;
            cursor: pointer;
            color: #666666;
        }
        .img-display-mode > .is-active {
            color: #ffaa00;
            transform: scale(1.05);
            background: #01499c;
        }
    `

    document.body.append(styleEl)
}
const sizeLevel = ['b', 'kb', 'mb']
function calcSize(size, level = 0) {
    if( size < 1024 || level >= 2) {
        return size.toFixed(2) + sizeLevel[level]
    }else{
        return calcSize(size/1024, level + 1)
    }
}

async function setTitle(str) {
    const imgTitle= document.querySelector('#img-title')
    const imgSize = document.querySelector('#shin-img-size')
    imgTitle.innerText = str
    imgSize.innerText = '0'

    try {
        const { size } = await (await fetch(`http://git.rdapp.com/visulization/project-web-components-store/blob/publish/dist/${curDisplayPath}/${str}?format=json&viewer=none`)).json()
        imgSize.innerText = calcSize(size)
        imgSize.style.background = size <= 200 * 1024 ? 'green' : '#ffaa00';
    } catch(e) {
        imgSize.innerText = '加载失败'
    }
}

async function showImg() {
    // mask
    const maskEl = document.createElement('div')
    maskEl.className = 'modal-backdrop fade show shin-modal white-background'

    // dialog
    const wrapEl = document.createElement('div')
    wrapEl.className = 'modal fade show'
    wrapEl.style = 'display: block;'
    wrapEl.id = "shin-script-wrapper"

    let arr = getImgList()

    const browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    const useWidth = browserWidth - 33.5 * 2
    const imgWidth = 200 + 4 // width + margin

    const maxImgCount = Math.floor(useWidth / imgWidth)

    // 页码配置
    let curPage = 0
    let totalPage = Math.ceil(arr.length / maxImgCount)

    const getSinglePageImg = () => {
        const a = arr.slice(curPage * maxImgCount, (curPage + 1) * maxImgCount)
        a.push(...new Array(maxImgCount - a.length))
        return a
    }

    // 展示状态
    let displayStatus = curDisplayStyle || 'preview' // 'list'

    const imgListEl = getSinglePageImg().reduce((pre, cur) => pre + getImgItem(cur), '')

    function replaceImgList (index = 0) {
        const cil = getSinglePageImg()
        const ilEL = cil.reduce((pre, cur) => pre + getImgItem(cur), '')
        document.querySelector('.img-list-content').innerHTML = ilEL

        // 替换大图
        const imgPreview = document.querySelector('#shin-img-preview')
        imgPreview.setAttribute('src', gitPreUrl + curDisplayPath + '/' + cil.at(index))

        // 替换标题
        setTitle(cil.at(index))
    }

    function addPage () {
        curPage = curPage >= (totalPage - 1) ? 0 : ++curPage
    }

    function subPage() {
        curPage = curPage <= 0 ? totalPage - 1 : --curPage
    }

    function setCurPageByIndex(index) {
        curPage = Math.floor(index / maxImgCount)
    }

    function changeMode (type) {
        if(!type) return

        const elList = document.querySelector('#img-display-mode').children

        // 修改高亮
        for(let i = 0; i < elList.length; i++) {
            const el = elList[i]
            if(el.classList.contains('is-active')) {
                el.classList.remove('is-active')
            }

            if(el.dataset.type === type) {
                el.classList.add('is-active')
            }
        }

        const wrapEl = document.querySelector('#shin-script-wrapper')

        curDisplayStyle = type

        // 展示状态
        switch(type) {
            case 'preview': {
                wrapEl.querySelector('.img-preview').style.display = 'block'
                wrapEl.querySelector('.img-list').style.display = 'flex'
                wrapEl.querySelector('.img-list-wrap').style.display = 'none'
                break;
            }
            case 'list': {
                wrapEl.querySelector('.img-preview').style.display = 'none'
                wrapEl.querySelector('.img-list').style.display = 'none'
                wrapEl.querySelector('.img-list-wrap').style.display = 'flex'
                break;
            }
        }

    }

    function setPage (arr) {
        const dialogStr = `
        <div class="img-title-wrapper">
            <span id="img-title" class="singe-line">
                ${arr.at(0)}
            </span>

            <span id="shin-img-size" style="background: green;">
                0
            </span>

            <select class="shin-selete-display-folder">
                ${getfolderList().reduce((pre, cur) => pre + `<option value=${cur}>${replacePreCustomStr(cur)}</option>`, '')}
            </select>

            <div id="img-display-mode" class="img-display-mode">
                <div class="img-display-mode-item" data-type="list">
                    列表
                </div>
                <div class="img-display-mode-item is-active" data-type="preview">
                    相册
                </div>
            </div>
            <button aria-label="关闭" type="button" data-dismiss="modal" class="close js-modal-close-action shin-close">
                <span aria-hidden="true">×</span>
            </button>
        </div>

        <div class="img-preview" style="display: block;">
            <img id="shin-img-preview" src="${gitPreUrl}${curDisplayPath}/${arr.at(0)}" alt="加载失败,请稍后再试" />
        </div>

        <div class="img-list" style="display: flex;">
            <span class="shin-before"><</span>
            <div class="img-list-content" >
                ${imgListEl}
            </div>
            <span class="shin-after">></span>
        </div>

        <div class="img-list-wrap" style="display: none;">
            ${[...arr, ...new Array(10)].reduce((pre, cur) => pre + getImgItem(cur), '')}
        </div>
        `
         wrapEl.innerHTML = dialogStr
    }

    setPage(getImgList())

    function close () {
        maskEl.remove()
        wrapEl.remove()
    }

    function changeDisplayState (state) {
        displayStatus = state
        switch(displayStatus) {
            case 'preview': {
                wrapEl.querySelector('.img-preview').style.display = 'block'
                wrapEl.querySelector('.img-list').style.display = 'flex'
                wrapEl.querySelector('.img-list-wrap').style.display = 'none'
                break;
            }
            case 'list': {
                wrapEl.querySelector('.img-preview').style.display = 'none'
                wrapEl.querySelector('.img-list').style.display = 'none'
                wrapEl.querySelector('.img-list-wrap').style.display = 'flex'
                break;
            }
        }
    }

    function setImgListDom () {
        wrapEl.querySelector('.img-list-wrap').innerHTML = [...arr, ...new Array(10)].reduce((pre, cur) => pre + getImgItem(cur), '')
    }

    wrapEl.querySelector('.img-display-mode').onclick = e=> changeMode(e.target.dataset.type)

    wrapEl.querySelector('.js-modal-close-action').onclick = close

    wrapEl.querySelector('.img-preview').onclick = function (e) {
        if(e.target.className === 'img-preview') {
            close()
        }
        if(e.target.id) {
            const imgName = e.target.innerText || e.target.src.split('/').pop()
            copy(preUrl+ curDisplayPath + '/' + imgName)
        }
        e.stopPropagation()
        e.preventDefault()
    }

    wrapEl.querySelector('.shin-before').onclick = function() {
        // 上一页
        subPage()
        replaceImgList()
    }
    wrapEl.querySelector('.shin-after').onclick = function() {
        // 下一页
        addPage()
        replaceImgList()
    }

    wrapEl.querySelector('.img-list').onclick = function(e) {
        // 点击图片
        if(e.target.nodeName === 'IMG') {
            // 替换大图
            const imgPreview = document.querySelector('#shin-img-preview')
            imgPreview.setAttribute('src', '#')
            imgPreview.setAttribute('src', e.target.currentSrc)

            // 替换标题
            setTitle(e.target.currentSrc.split('/').pop())
        }

        if(e.target.classList.contains('copy-name')) {
            // 复制链接
            const imgName = e.target.innerText
            copy(preUrl + curDisplayPath + '/' + imgName)
        }

        e.stopPropagation()
        e.preventDefault()
    }

    const folderEl = wrapEl.querySelector('.shin-selete-display-folder')
    folderEl.onchange = function(e) {
        curDisplayPath = folderEl.value
        arr = getImgList()
        curPage = 0
        totalPage = Math.ceil(arr.length / maxImgCount)
        replaceImgList()
        setImgListDom()
    }
    folderEl.value = curDisplayPath

    wrapEl.querySelector('.img-list-wrap').onclick = function(e) {
        // 点击图片
        if(e.target.nodeName === 'IMG') {
            // 查询、修改页码
            const imgName = e.target.currentSrc.split('/').pop()
            const imgIndex = arr.indexOf(imgName)
            setCurPageByIndex(imgIndex)
            replaceImgList(imgIndex % maxImgCount)

            changeMode('preview')
        }

        if(e.target.classList.contains('copy-name')) {
            // 复制链接
            const imgName = e.target.innerText
            copy(preUrl + curDisplayPath + '/' + imgName)
        }

        e.stopPropagation()
        e.preventDefault()
    }

    document.body.append(maskEl)
    document.body.append(wrapEl)

    setTitle(arr.at(0))

    setStyle()

    // 恢复预览状态
    changeMode(curDisplayStyle)
}



async function showImgeList() {
    const imgList = getImgList() || []
    showImg(imgList)
}

function getfolderList() {
    return jsonData.reduce((pre, cur) => {
        const path = cur.split('/').slice(1,-1).join('/')
        if(!pre.includes(path)) {
            pre.push(path)
        }
        return pre
    }, [])
}

function createEl () {
    const ul = document.getElementsByClassName('navbar-sub-nav')[0];

    const uploadBtn = document.createElement('li')

    uploadBtn.innerHTML = `
        <button data-toggle="dropdown" type="button">
            上传图片
    <svg class="caret-down"><use xlink:href="/assets/icons-24aaa921aa9e411162e6913688816c79861d0de4bee876cf6fc4c794be34ee91.svg#angle-down"></use></svg>
        </button>

    <div class="dropdown-menu frequent-items-dropdown-menu">
        <div class="frequent-items-list-container">
            <ul class="list-unstyled">
                <li class="frequent-items-list-item-container">
                    <a href="/visulization/visualization-display-platform" class="clearfix">
                    </a>
                </li>
            </ul>
        </div>
    </div>
    `

    uploadBtn.className = 'home dropdown header-projects qa-projects-dropdown'

    uploadBtn.querySelector('button').onclick = () => {
        const str = getfolderList().reduce((pre, cur) => {
            return `
                ${pre}
    <li class="frequent-items-list-item-container">
        <a class="clearfix">
            ${replacePreCustomStr(cur)}
    </a>
    </li>
    `
        }, '')

        uploadBtn.querySelector('.list-unstyled').innerHTML = str
    }

    uploadBtn.querySelector('.list-unstyled').onclick = (e) => {
        curUploadPath = e.target.innerText
        uploadImge()
    }

    ul.append(uploadBtn)

    const imgDisplayBtn = document.createElement('li')
    imgDisplayBtn.className = 'd-none d-sm-block'

    const imgEl = document.createElement('a')

    imgEl.text = '显示封面图'
    imgEl.onclick = showImgeList
    imgDisplayBtn.append(imgEl)
    ul.append(imgDisplayBtn)
}

(function() {
    'use strict';

    createEl()
})();