Greasy Fork

Greasy Fork is available in English.

蓝湖 lanhu

自动填充填写过的产品密码(不是蓝湖账户);查看打开过的项目;查看产品页面窗口改变后帮助侧边栏更新高度

当前为 2020-10-26 提交的版本,查看 最新版本

// ==UserScript==
// @name         蓝湖 lanhu
// @version      1.6.0
// @description  自动填充填写过的产品密码(不是蓝湖账户);查看打开过的项目;查看产品页面窗口改变后帮助侧边栏更新高度
// @author       sakura-flutter
// @namespace    https://github.com/sakura-flutter/tampermonkey-scripts/commits/master/src/lanhu/index.js
// @license      GPL-3.0
// @compatible   chrome >= Latest
// @compatible   firefox >= Latest
// @noframes
// @match        https://lanhuapp.com/web/
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @require      https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.runtime.global.js
// @require      http://greasyfork.icu/scripts/411093-toast/code/Toast.js?version=862073
// ==/UserScript==

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";

// CONCATENATED MODULE: external "Vue"
const external_Vue_namespaceObject = Vue;
// CONCATENATED MODULE: ./src/utils/index.js
function parseURL(search = location.search) {
  if (!search) {
    // 主要处理对hash的search
    if (location.hash.includes('?')) {
      search = location.hash.split('?')[1];
    }

    if (!search) return {};
  }

  const searchParams = new URLSearchParams(search);
  return [...searchParams.entries()].reduce((acc, [key, value]) => (acc[key] = value, acc), {});
}
function throttle(fn, delay) {
  var t = null;
  var begin = new Date().getTime();
  return function (...args) {
    var _self = this;

    var cur = new Date().getTime();
    clearTimeout(t);

    if (cur - begin >= delay) {
      fn.apply(_self, args);
      begin = cur;
    } else {
      t = setTimeout(function () {
        fn.apply(_self, args);
      }, delay);
    }
  };
}
// CONCATENATED MODULE: ./src/lanhu/index.js


const $ = document.querySelector.bind(document);
const marks = new WeakSet();

function main() {
  fixBarHeight();

  const app = $('.whole').__vue__;

  if (!app) {
    console.warn('蓝湖脚本:获取vue失败');
    return;
  }

  const recorder = createRecorder();
  app.$watch('$route', function (to, from) {
    // 无法知道页面是否渲染完毕,延时处理
    setTimeout(autofillPassword, 500); // 蓝湖title是动态获取的,可能有延时,延时处理

    setTimeout(recorder.record, 500);
  }, {
    immediate: true
  });
}
/* 填充密码 */


function autofillPassword() {
  if (!location.hash.startsWith('#/item/project/door')) return;
  const {
    pid
  } = parseURL(); // 确认登录按钮   密码框

  const [confirmEl, passwordEl] = [$('#project-door .mu-raised-button-wrapper'), $('#project-door .pass input')];
  if (!pid || !confirmEl || !passwordEl) return;
  const pidPassword = GM_getValue('passwords', {})[pid];

  if (pidPassword) {
    passwordEl.value = pidPassword;
    Toast('密码已填写');
    confirmEl.click();
  } // 标记已添加事件的元素


  if (marks.has(confirmEl)) return;
  marks.add(confirmEl); // 点击后保存密码

  confirmEl.addEventListener('mousedown', savePassword); // 回车键保存密码

  passwordEl.addEventListener('keydown', event => {
    if (event.keyCode !== 13) return;
    savePassword();
  });

  function savePassword() {
    const savedPassword = GM_getValue('passwords', {});
    const password = passwordEl.value;
    GM_setValue('passwords', { ...savedPassword,
      [pid]: password
    });
  }
}
/* 更新侧边栏高度 */


function fixBarHeight() {
  window.addEventListener('resize', throttle(function () {
    if (!location.hash.startsWith('#/item/project/product')) return;
    const barEl = $('.flexible-bar');
    const modalEl = $('.flexible-modal');
    if (!barEl || !modalEl) return;
    barEl.dispatchEvent(new MouseEvent('mousedown'));
    modalEl.dispatchEvent(new MouseEvent('mouseup'));
  }, 150));
}
/* 记录看过的产品 */


function createRecorder() {
  // eslint-disable-next-line no-unused-vars
  const {
    Transition,
    TransitionGroup
  } = Vue;
  const app = Vue.createApp({
    render() {
      const {
        reversed,
        recordsVisible,
        unhidden,
        moreActionsVisible,
        toggle,
        toggleMoreActions,
        getHref,
        deleteItem,
        copy,
        onUnhiddenChange
      } = this;
      return (0,external_Vue_namespaceObject.createVNode)("article", {
        "id": "inject-recorder-ui",
        "onMouseenter": () => {
          toggle(true);
        },
        "onMouseleave": () => {
          toggle(false);
          toggleMoreActions(false);
        }
      }, [(0,external_Vue_namespaceObject.createVNode)(Transition, {
        "name": "inject-slide-fade"
      }, {
        default: () => [(0,external_Vue_namespaceObject.withDirectives)((0,external_Vue_namespaceObject.createVNode)("div", null, [(0,external_Vue_namespaceObject.createVNode)(TransitionGroup, {
          "class": {
            'more-actions': moreActionsVisible
          },
          "tag": "ul",
          "name": "inject-slide-hor-fade",
          "appear": true
        }, {
          default: () => [reversed.map(item => (0,external_Vue_namespaceObject.createVNode)("li", {
            "key": item.pid
          }, [(0,external_Vue_namespaceObject.createVNode)("a", {
            "href": getHref(item),
            "title": item.title,
            "target": "_blank"
          }, [item.title]), (0,external_Vue_namespaceObject.createVNode)("div", {
            "class": "actions",
            "onMouseenter": () => {
              toggleMoreActions(true);
            }
          }, [(0,external_Vue_namespaceObject.createVNode)("button", {
            "title": "移除",
            "onClick": () => {
              deleteItem(item);
            }
          }, [(0,external_Vue_namespaceObject.createTextVNode)("\xD7")]), (0,external_Vue_namespaceObject.withDirectives)((0,external_Vue_namespaceObject.createVNode)("button", {
            "title": "左击复制链接和密码;右击复制密码",
            "onClick": () => {
              copy('all', item);
            },
            "onContextmenu": event => {
              event.preventDefault();
              copy('pwd', item);
            }
          }, [(0,external_Vue_namespaceObject.createVNode)("svg", {
            "t": "1602929080634",
            "viewBox": "0 0 1024 1024",
            "version": "1.1",
            "xmlns": "http://www.w3.org/2000/svg",
            "p-id": "4117",
            "width": "10",
            "height": "10"
          }, [(0,external_Vue_namespaceObject.createVNode)("path", {
            "d": "M877.714286 0H265.142857c-5.028571 0-9.142857 4.114286-9.142857 9.142857v64c0 5.028571 4.114286 9.142857 9.142857 9.142857h566.857143v786.285715c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857V36.571429c0-20.228571-16.342857-36.571429-36.571428-36.571429zM731.428571 146.285714H146.285714c-20.228571 0-36.571429 16.342857-36.571428 36.571429v606.514286c0 9.714286 3.885714 18.971429 10.742857 25.828571l198.057143 198.057143c2.514286 2.514286 5.371429 4.571429 8.457143 6.285714v2.171429h4.8c4 1.485714 8.228571 2.285714 12.571428 2.285714H731.428571c20.228571 0 36.571429-16.342857 36.571429-36.571429V182.857143c0-20.228571-16.342857-36.571429-36.571429-36.571429zM326.857143 905.371429L228.457143 806.857143H326.857143v98.514286zM685.714286 941.714286H400V779.428571c0-25.257143-20.457143-45.714286-45.714286-45.714285H192V228.571429h493.714286v713.142857z",
            "p-id": "4118"
          }, null)])]), [[external_Vue_namespaceObject.vShow, moreActionsVisible]])])]))]
        })]), [[external_Vue_namespaceObject.vShow, reversed.length && (unhidden || recordsVisible)]])]
      }), (0,external_Vue_namespaceObject.createVNode)("div", {
        "class": "control"
      }, [(0,external_Vue_namespaceObject.createVNode)("button", {
        "class": "view-btn"
      }, [(0,external_Vue_namespaceObject.createTextVNode)("\u6253\u5F00\u6700\u8FD1\u9879\u76EE")]), (0,external_Vue_namespaceObject.createVNode)("input", {
        "checked": this.unhidden,
        "type": "checkbox",
        "title": "固定显示",
        "onChange": onUnhiddenChange
      }, null)])]);
    },

    setup() {
      const {
        toRefs,
        reactive,
        computed
      } = Vue;
      const state = reactive({
        records: GM_getValue('records', []),
        recordsVisible: false,
        moreActionsVisible: false,
        unhidden: GM_getValue('unhidden', false)
      });
      const reversed = computed(() => [...state.records].reverse());
      GM_addValueChangeListener('records', (name, oldVal, newVal) => {
        state.records = newVal;
      });
      GM_addValueChangeListener('unhidden', (name, oldVal, newVal) => {
        state.unhidden = newVal;
      });

      function getHref(item) {
        if (item.href) return item.href; // 兼容旧版本

        const PATH = 'https://lanhuapp.com/web/';
        return PATH + item.hash + '?' + item.queryString;
      }

      function deleteItem(item) {
        const newRecords = [...state.records];
        newRecords.find((record, index) => {
          if (record.pid === item.pid) {
            newRecords.splice(index, 1);
            return true;
          }
        });
        GM_setValue('records', newRecords);
      }

      function copy(action, item) {
        let copyString = '';
        const password = GM_getValue('passwords', {})[item.pid];

        if (action === 'all') {
          const href = getHref(item);
          copyString += `${item.title}`;
          password && (copyString += ` (密码:${password})`);
          copyString += `\n${href}`;
        } else if (action === 'pwd') {
          if (password) {
            copyString += password;
          } else {
            Toast.warning('没有密码!');
          }
        }

        if (!copyString) return;
        GM_setClipboard(copyString, 'text');
        Toast.success('复制成功');
      }

      function toggle(visible) {
        state.recordsVisible = visible;
      }

      function toggleMoreActions(visible) {
        state.moreActionsVisible = visible;
      }

      function onUnhiddenChange(event) {
        GM_setValue('unhidden', event.target.checked);
      }

      return { ...toRefs(state),
        reversed,
        getHref,
        deleteItem,
        copy,
        toggle,
        toggleMoreActions,
        onUnhiddenChange
      };
    }

  });
  const rootContainer = document.createElement('div');
  app.mount(rootContainer);
  document.body.appendChild(rootContainer);
  /* 记录函数 */

  function record() {
    const {
      pid
    } = parseURL();
    if (!pid) return;
    const records = GM_getValue('records', []);
    let oldTitle = null;
    records.find((item, index) => {
      if (item.pid === pid) {
        oldTitle = item.title;
        records.splice(index, 1);
        return true;
      }
    }); // 优化标题显示:当前是无意义标题且有旧标题时优先使用旧标题

    const title = ['蓝湖', '...'].includes(document.title) && oldTitle ? oldTitle : document.title;
    records.push({
      pid,
      title,
      href: location.href
    });
    GM_setValue('records', records);
  } // 添加样式


  GM_addStyle(`
    |> {
        position: fixed;
        right: 1.5vw;
        bottom: 8vh;
        z-index: 1000;
        width: 240px;
        padding: 30px 30px 10px;
        opacity: .5;
        transition: opacity .1s;
    }
    |>:hover {
        opacity: 1;
    }
    |> ul::-webkit-scrollbar {
        width: 8px;
        height: 8px;
        background: #f2f2f2;
        padding-right: 2px;
    }
    |> ul::-webkit-scrollbar-thumb {
        border-radius: 3px;
        border: 0;
        background: #b4bbc5;
    }
    |> ul.more-actions {
        width: 204px;
    }
    |> ul {
        width: 180px;
        padding: 5px;
        max-height: 40vh;
        overflow-x: hidden;
        background: rgb(251, 251, 251);
        box-shadow: 0 1px 6px rgba(0,0,0,.15);
        transition: width .1s;
    }
    |> li {
        display: flex;
        align-items: center;
        padding: 0 5px;
        transition: all .3s, background 0.1s ease-out;
    }
    |> li:hover {
        background: rgba(220, 237, 251, 0.64);
    }
    |> li a {
        width: 132px;
        flex: none;
        line-height: 30px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    |> li .actions {
        flex: 1 0 auto;
    }
    |> li button {
        width: 20px;
        line-height: 20px;
        border: none;
        border-radius: 50%;
        color: #ababab;
        box-shadow: 0 1px 1px rgba(0,0,0,.15);
        background: #fff;
        cursor: pointer;
    }
    |> li button:nth-of-type(n+2) {
        margin-left: 4px;
    }
    |> .control {
        display: flex;
        justify-content: center;
        align-items: center;
        padding-top: 8px;
    }
    |> .control input {
        margin-left: 6px;
    }
    |> .view-btn {
        padding: 4px 12px;
        color: #fff;
        background: #3385ff;
        box-shadow:0 1px 6px rgba(0,0,0,.2);
        border: none;
        border-radius: 2px;
    }
    |> svg {
        fill: currentColor;
    }

    /* 动画1 */
    |> .inject-slide-fade-enter-active, |> .inject-slide-fade-leave-active {
        transition: all .1s;
    }
    |> .inject-slide-fade-enter-from,
    |> .inject-slide-fade-leave-to {
        transform: translateY(5px);
        opacity: 0;
    }
    /* 动画2 group */
    |> .inject-slide-hor-fade-move {
        transition: all .8s;
    }
    |> .inject-slide-hor-fade-enter-from,
    |> .inject-slide-hor-fade-leave-to {
        opacity: 0;
        transform: translateX(30px);
    }
    |> .inject-slide-hor-fade-active {
        position: absolute;
    }
  `.replace(/\|>/g, '#inject-recorder-ui'));
  return {
    record
  };
}

main();
/******/ })()
;