// ==UserScript==
// @name 常用邮箱手机号一键填写助手(可隐藏+导入导出)
// @namespace https://example.com
// @version 0.6.0
// @description 注册表单快速填写常用邮箱/手机号,支持隐藏面板、导入导出
// @author You
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_clipboard
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
// ─────────────── 数据 ───────────────
let data = GM_getValue('fastfill_data', {
emails: ["example@gmail.com", "test@outlook.com"],
phones: ["13812345678", "13900002222"]
});
let panelPos = GM_getValue('fastfill_panel_pos', { bottom: 24, right: 24 });
let panelVisible = GM_getValue('fastfill_panel_visible', true);
let panel = null;
// ─────────────── CSS ───────────────
GM_addStyle(`
#fastfill-panel {
position: fixed;
z-index: 999998;
width: 240px;
max-height: 80vh;
overflow-y: auto;
background: rgba(30, 30, 38, 0.94);
backdrop-filter: blur(10px);
color: #e0e0ff;
border-radius: 16px;
box-shadow: 0 10px 40px rgba(0,0,0,0.6);
font-family: system-ui, -apple-system, sans-serif;
font-size: 14px;
padding: 12px 14px;
border: 1px solid rgba(120,120,180,0.3);
user-select: none;
transition: all 0.18s ease, opacity 0.3s, transform 0.3s;
}
#fastfill-panel.hidden {
opacity: 0;
transform: scale(0.85);
pointer-events: none;
}
#fastfill-header {
font-weight: 600;
color: #a5b4fc;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
}
.fastfill-section-title {
color: #c7d2fe;
font-size: 13px;
font-weight: 600;
margin: 12px 0 6px;
}
.fastfill-item {
padding: 8px 10px;
margin: 4px 0;
background: rgba(50,50,70,0.6);
border-radius: 8px;
cursor: pointer;
transition: all 0.13s;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fastfill-item:hover {
background: rgba(80,80,120,0.8);
transform: translateX(2px);
}
.fastfill-item.email { color: #a5b4fc; }
.fastfill-item.phone { color: #86efac; }
#fastfill-btn-area {
margin-top: 16px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.fastfill-btn {
flex: 1;
min-width: 80px;
padding: 8px 10px;
border-radius: 8px;
text-align: center;
cursor: pointer;
font-size: 13px;
background: #4b5563;
color: #e0e0ff;
transition: background 0.15s;
}
.fastfill-btn:hover { background: #6366f1; color: white; }
.fastfill-btn.primary { background: linear-gradient(135deg, #6366f1, #8b5cf6); }
#fastfill-manage-btn { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; }
`);
// ─────────────── 核心函数 ───────────────
function saveData() {
GM_setValue('fastfill_data', data);
}
function fillEmail(value) {
fillInputs(value, el => /email|mail|邮箱|account|user/i.test((el.name||'') + (el.id||'') + (el.placeholder||'')));
}
function fillPhone(value) {
fillInputs(value, el => /phone|mobile|tel|手机|电话/i.test((el.name||'') + (el.id||'') + (el.placeholder||'')));
}
function fillInputs(value, matcher) {
document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"])').forEach(el => {
if (matcher(el) && el.offsetParent !== null) { // 只填可见的
if (el.value && el.value !== value) {
if (!confirm(`覆盖已有值?\n原: ${el.value}\n新: ${value}`)) return;
}
el.value = value;
el.dispatchEvent(new Event('input', {bubbles: true}));
el.dispatchEvent(new Event('change', {bubbles: true}));
}
});
}
// ─────────────── 面板创建与渲染 ───────────────
function createOrUpdatePanel() {
if (panel) panel.remove();
if (!panelVisible) return;
panel = document.createElement('div');
panel.id = 'fastfill-panel';
panel.style.bottom = panelPos.bottom + 'px';
panel.style.right = panelPos.right + 'px';
panel.innerHTML = `
邮箱
手机号
`;
document.body.appendChild(panel);
// 渲染列表
const emailList = panel.querySelector('#email-list');
const phoneList = panel.querySelector('#phone-list');
emailList.innerHTML = data.emails.map(v => `${v}
`).join('');
phoneList.innerHTML = data.phones.map(v => `${v}
`).join('');
// 点击填充
panel.querySelectorAll('.fastfill-item').forEach(item => {
item.onclick = () => {
const val = item.textContent.trim();
if (item.classList.contains('email')) fillEmail(val);
else fillPhone(val);
};
});
// 关闭(隐藏)
panel.querySelector('#fastfill-close').onclick = () => {
panelVisible = false;
GM_setValue('fastfill_panel_visible', false);
panel.classList.add('hidden');
setTimeout(() => panel.remove(), 350);
panel = null;
};
// 导出
panel.querySelector('#fastfill-export').onclick = () => {
const json = JSON.stringify(data, null, 2);
GM_setValue('fastfill_last_export', json); // 备用
GM_clipboard(json).then(() => {
alert("已复制到剪贴板!\n可保存到记事本等地方");
}).catch(() => {
prompt("复制失败,请手动复制:", json);
});
};
// 导入
panel.querySelector('#fastfill-import').onclick = () => {
const text = prompt("请粘贴之前导出的 JSON 数据:", "");
if (!text) return;
try {
const imported = JSON.parse(text);
if (!imported || typeof imported !== 'object') throw new Error();
if (Array.isArray(imported.emails)) data.emails = imported.emails.filter(v => typeof v === 'string');
if (Array.isArray(imported.phones)) data.phones = imported.phones.filter(v => typeof v === 'string');
saveData();
alert("导入成功!");
createOrUpdatePanel(); // 刷新
} catch (e) {
alert("格式错误,请检查是否为正确的 JSON");
}
};
// 管理
panel.querySelector('#fastfill-manage').onclick = showManageModal;
// 拖拽
let isDragging = false, startX, startY, startRight, startBottom;
const header = panel.querySelector('#fastfill-header');
header.onmousedown = e => {
if (e.target.id === 'fastfill-close') return;
isDragging = true;
startX = e.clientX;
startY = e.clientY;
startRight = parseFloat(panel.style.right || 24);
startBottom = parseFloat(panel.style.bottom || 24);
e.preventDefault();
};
document.onmousemove = e => {
if (!isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
panelPos.right = Math.max(12, startRight - dx);
panelPos.bottom = Math.max(12, startBottom - dy);
panel.style.right = panelPos.right + 'px';
panel.style.bottom = panelPos.bottom + 'px';
};
document.onmouseup = () => {
if (isDragging) {
isDragging = false;
GM_setValue('fastfill_panel_pos', panelPos);
}
};
}
// ─────────────── 管理弹窗(保持不变,略微精简) ───────────────
function showManageModal() {
let modal = document.getElementById('fastfill-modal');
if (modal) modal.remove();
modal = document.createElement('div');
modal.id = 'fastfill-modal';
modal.innerHTML = `
`;
document.body.appendChild(modal);
function renderList(type) {
const container = document.getElementById(type + '-edit-list');
const arr = type === 'email' ? data.emails : data.phones;
container.innerHTML = arr.map((v,i)=>`
`).join('');
}
renderList('email');
renderList('phone');
// tab
modal.querySelectorAll('[data-tab]').forEach(el=>{
el.onclick = ()=>{
modal.querySelectorAll('[data-tab]').forEach(e=>e.classList.remove('active'));
modal.querySelectorAll('#tab-email,#tab-phone').forEach(e=>e.style.display='none');
el.classList.add('active');
document.getElementById('tab-'+el.dataset.tab).style.display='block';
};
});
// 添加
modal.querySelectorAll('button:not(.save):not(.del)').forEach(btn=>{
btn.onclick = ()=>{
const input = btn.previousElementSibling.previousElementSibling || btn.previousElementSibling;
const val = input.value.trim();
if(!val) return;
const type = btn.closest('#tab-email') ? 'email' : 'phone';
const arr = type==='email'?data.emails:data.phones;
if(arr.includes(val)) return alert('已存在');
arr.push(val);
saveData();
input.value='';
renderList(type);
};
});
// 保存/删除
modal.addEventListener('click', e=>{
if(!e.target.matches('.save,.del')) return;
const row = e.target.closest('div');
const input = row.querySelector('input');
const i = +input.dataset.i;
const type = input.dataset.t;
const arr = type==='email'?data.emails:data.phones;
if(e.target.classList.contains('save')){
const val = input.value.trim();
if(val && !arr.includes(val)){
arr[i] = val;
saveData();
renderList(type);
}
} else if(e.target.classList.contains('del')){
if(!confirm('确定删除?')) return;
arr.splice(i,1);
saveData();
renderList(type);
}
});
// 关闭
modal.querySelector('button:last-child').onclick = ()=>{
modal.remove();
createOrUpdatePanel();
};
}
// ─────────────── 显示/隐藏控制 ───────────────
GM_registerMenuCommand("显示/隐藏 账号助手面板", () => {
panelVisible = !panelVisible;
GM_setValue('fastfill_panel_visible', panelVisible);
if (panelVisible) {
createOrUpdatePanel();
} else if (panel) {
panel.classList.add('hidden');
setTimeout(() => { panel.remove(); panel = null; }, 350);
}
alert(panelVisible ? "面板已显示" : "面板已隐藏\n可通过菜单重新显示");
});
// ─────────────── 自动出现逻辑 ───────────────
function shouldShow() {
return panelVisible && !!document.querySelector(`
input[type="email" i],
input[type="tel" i],
input[name*="email" i],
input[name*="mail" i],
input[name*="phone" i],
input[name*="mobile" i],
input[placeholder*="邮箱" i],
input[placeholder*="手机" i],
input[placeholder*="电话" i]
`);
}
function checkAndShow() {
if (shouldShow() && !panel) {
createOrUpdatePanel();
} else if (!shouldShow() && panel) {
panel.remove();
panel = null;
}
}
setTimeout(checkAndShow, 800);
const observer = new MutationObserver(checkAndShow);
observer.observe(document.body, { childList: true, subtree: true });
// 菜单快速添加当前填写的值
GM_registerMenuCommand("添加当前页面填写的邮箱/手机号", () => {
const emailEl = document.querySelector('input[type="email" i], input[name*="email" i], input[name*="mail" i]');
const phoneEl = document.querySelector('input[type="tel" i], input[name*="phone" i], input[name*="mobile" i]');
let added = false;
if (emailEl?.value?.trim() && !data.emails.includes(emailEl.value.trim())) {
data.emails.push(emailEl.value.trim());
added = true;
}
if (phoneEl?.value?.trim() && !data.phones.includes(phoneEl.value.trim())) {
data.phones.push(phoneEl.value.trim());
added = true;
}
if (added) {
saveData();
alert("已添加!");
if (panel) createOrUpdatePanel();
}
});
// 初始显示
if (panelVisible) checkAndShow();
})();