您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
自动填充填写过的产品密码(不是蓝湖账户);查看打开过的项目;查看产品页面窗口改变后帮助侧边栏更新高度
当前为
// ==UserScript== // @name 蓝湖 lanhu // @version 1.5.0 // @description 自动填充填写过的产品密码(不是蓝湖账户);查看打开过的项目;查看产品页面窗口改变后帮助侧边栏更新高度 // @author sakura-flutter // @namespace https://github.com/sakura-flutter/tampermonkey-scripts/commits/master/lanhu/index.js // @license GPL-3.0 // @compatible chrome >= 80 // @compatible firefox >= 75 // @noframes // @match https://lanhuapp.com/web/ // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @grant GM_addStyle // @grant GM_setClipboard // @require http://greasyfork.icu/scripts/411093-toast/code/Toast.js?version=846237 // ==/UserScript== /* global Vue Toast */ (function() { 'use strict' const $ = document.querySelector.bind(document) const marks = new WeakSet() function main() { const app = $('.whole').__vue__ const recorder = createRecorder() fixBarHeight() 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 queryString = location.hash.includes('?') ? location.hash.split('?')[1] : '' if (!queryString) return const pid = new URLSearchParams(queryString).get('pid') // 确认登录按钮 密码框 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() { const ui = new Vue({ template: ` <article id="inject-recorder-ui" @mouseenter="toggle(true)" @mouseleave="toggle(false);toggleMoreActions(false)"> <transition name="inject-slide-fade"> <transition-group v-show="reversed.length && (unhidden || recordsVisible)" :class="{'more-actions': moreActionsVisible}" tag="ul" name="inject-slide-hor-fade"> <li v-for="item in reversed" :key="item.pid"> <a :href="getHref(item)" :title="item.title" target="_blank">{{item.title}}</a> <div class="actions" @mouseenter="toggleMoreActions(true)"> <button title="移除" @click="deleteItem(item)">×</button> <button v-show="moreActionsVisible" title="左击复制链接和密码;右击复制密码" @click="copy('all', item)" @contextmenu.prevent="copy('pwd', item)"> <svg t="1602929080634" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4117" width="10" height="10"><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"></path></svg> </button> </div> </li> </transition-group> </transition> <div style="text-align: center; padding-top: 8px;"> <button class="view-btn">打开最近项目</button> <input style="margin-left: 6px;vertical-align:text-top;" v-model="unhidden" type="checkbox" title="固定显示" @change="unhiddenChange" /> </div> </article> `, data() { return { records: GM_getValue('records', []), recordsVisible: false, moreActionsVisible: false, unhidden: GM_getValue('unhidden', false), } }, computed: { reversed() { return [...this.records].reverse() }, }, created() { GM_addValueChangeListener('records', (name, oldVal, newVal) => { this.records = Object.freeze(newVal) }) GM_addValueChangeListener('unhidden', (name, oldVal, newVal) => { this.unhidden = newVal }) }, methods: { getHref(item) { if (item.href) return item.href // 兼容旧版本 const PATH = 'https://lanhuapp.com/web/' return PATH + item.hash + '?' + item.queryString }, deleteItem(item) { const newRecords = [...this.records] newRecords.find((record, index) => { if (record.pid === item.pid) { newRecords.splice(index, 1) return true } }) GM_setValue('records', newRecords) }, copy(action, item) { let copyString = '' const password = GM_getValue('passwords', {})[item.pid] if (action === 'all') { const href = this.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('复制成功') }, toggle(visible) { this.recordsVisible = visible }, toggleMoreActions(visible) { this.moreActionsVisible = visible }, unhiddenChange() { GM_setValue('unhidden', this.unhidden) }, }, }).$mount() document.body.appendChild(ui.$el) /* 记录函数 */ function record() { const queryString = location.hash.includes('?') ? location.hash.split('?')[1] : '' if (!queryString) return const pid = new URLSearchParams(queryString).get('pid') 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; } |> .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, |> .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, |> .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, } } 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) } } } main() })()