// ==UserScript==
// @name SHEIN登录管理器5.1
// @namespace http://tampermonkey.net/
// @version 5.0
// @description 按钮横向排布+导入导出不乱码+只检测login+排序开关+【Vue真实输入修复】
// @match https://sso.geiwohuo.com/*
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @require https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js
// ==/UserScript==
(function() {
'use strict';
const SELECTOR = {
username: '#container > div > div.style__login_form_container--vs4UNdFH > div:nth-child(3) > form > div:nth-child(1) > div.c-dqgsow.soui-form-item-control > div > div > input',
password: '#container > div > div.style__login_form_container--vs4UNdFH > div:nth-child(3) > form > div:nth-child(2) > div.c-dqgsow.soui-form-item-control > div > div > input',
loginBtn: '#container > div > div.style__login_form_container--vs4UNdFH > div:nth-child(3) > form > button'
};
GM_addStyle(`
#accountManager{position:fixed;top:50%;left:50%;transform: translate(-120%, -50%);background:#fff;border-radius:14px;box-shadow:0 8px 30px rgba(0,0,0,.12);z-index:99999;padding:20px;font-size:14px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;border:1px solid #f0f0f0}
.manager-header{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap}
.manager-header button{padding:8px 14px;border:none;border-radius:8px;background:#3370ff;color:#fff;cursor:pointer;font-weight:500}
.manager-header button.success{background:#00c48c}
.manager-header button.sort-mode{background:#722ED1}
.account-table{width:100%;border-collapse:collapse;border-radius:10px;overflow:hidden}
.account-table th{background:#f7f8fa;padding:10px;text-align:center;font-weight:600}
.account-table td{padding:10px;text-align:center;border-bottom:1px solid #f2f2f2;min-width:90px}
.account-table td:nth-child(2){padding:10px;text-align:center;border-bottom:1px solid #f2f2f2;min-width:120px}
.account-table tr:hover{background:#fafbfc}
.btn-group{display:flex;gap:4px;justify-content:center;flex-wrap:nowrap}
.table-btn{padding:4px 6px;border:none;border-radius:6px;color:#fff;cursor:pointer;font-size:12px;white-space:nowrap}
.login-btn{background:#3370ff}
.edit-btn{background:#ffab00}
.del-btn{background:#ff4d4f}
.sort-btn{display:none;background:#13C2C2}
.show-sort .sort-btn{display:inline-block}
.modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;padding:24px;border-radius:14px;box-shadow:0 10px 40px rgba(0,0,0,.15);z-index:100000;width:340px}
.modal input{width:100%;margin:8px 0;padding:10px 12px;border:1px solid #e5e6eb;border-radius:8px;box-sizing:border-box}
.modal-btns{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}
.modal-btns button{padding:7px 14px;border-radius:8px;border:none;cursor:pointer}
.modal-btns button:nth-child(1){background:#f2f3f5;color:#333}
.modal-btns button:nth-child(2){background:#3370ff;color:#fff}
`);
// 排序模式开关
let sortMode = false;
const STORAGE_KEY = 'geiwohuo_account_list';
function getAccounts() { return GM_getValue(STORAGE_KEY, []); }
function saveAccounts(list) { GM_setValue(STORAGE_KEY, list); renderTable(); }
// 渲染表格
function renderTable() {
const list = getAccounts();
const tbody = document.querySelector('#accountTable tbody');
const table = document.querySelector('#accountTable');
if (!tbody) return;
if (sortMode) {
table.classList.add('show-sort');
} else {
table.classList.remove('show-sort');
}
tbody.innerHTML = '';
list.forEach((item, index) => {
const tr = document.createElement('tr');
tr.innerHTML = `
${item.type || '-'} |
${item.shop || '-'} |
${item.account} |
|
`;
tbody.appendChild(tr);
});
}
// ==============================================
// 【终极修复】完全照搬你提供的 Vue 真实输入方法
// 100% 解决假输入 / 回弹 / 不触发事件
// ==============================================
function triggerEvent(el, type) {
if (!el) return;
const e = new Event(type, { bubbles: true, cancelable: true });
el.dispatchEvent(e);
}
function setVueInputValue(input, value) {
if (!input) return;
input.focus();
input.value = '';
triggerEvent(input, 'input');
triggerEvent(input, 'change');
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
if (nativeInputValueSetter) {
nativeInputValueSetter.call(input, value);
} else {
input.value = value;
}
triggerEvent(input, 'input');
triggerEvent(input, 'change');
triggerEvent(input, 'blur');
}
// 登录逻辑(使用真实Vue输入)
function doLogin(index) {
const list = getAccounts();
const data = list[index];
if (!data) return;
const userInput = document.querySelector(SELECTOR.username);
const pwdInput = document.querySelector(SELECTOR.password);
const loginBtn = document.querySelector(SELECTOR.loginBtn);
// 真实填入,不会假输入!
setVueInputValue(userInput, data.account);
setVueInputValue(pwdInput, data.password);
setTimeout(() => {
loginBtn?.click();
}, 200);
}
// 上移
function moveUp(index) {
if (index <= 0) return;
const list = getAccounts();
[list[index], list[index - 1]] = [list[index - 1], list[index]];
saveAccounts(list);
}
// 下移
function moveDown(index) {
const list = getAccounts();
if (index >= list.length - 1) return;
[list[index], list[index + 1]] = [list[index + 1], list[index]];
saveAccounts(list);
}
// 弹窗
function showModal(editIndex = -1) {
const list = getAccounts();
const isEdit = editIndex >= 0;
const data = isEdit ? list[editIndex] : { type: '', shop: '', account: '', password: '' };
const modal = document.createElement('div');
modal.className = 'modal';
modal.innerHTML = `
${isEdit ? '修改账号' : '添加账号'}
`;
document.body.appendChild(modal);
modal.querySelector('#m_cancel').onclick = () => modal.remove();
modal.querySelector('#m_save').onclick = () => {
const type = modal.querySelector('#m_type').value.trim();
const shop = modal.querySelector('#m_shop').value.trim();
const account = modal.querySelector('#m_account').value.trim();
const password = modal.querySelector('#m_pwd').value.trim();
if (!account || !password) { alert('账号密码必填'); return; }
const newData = { type, shop, account, password };
if (isEdit) list[editIndex] = newData; else list.push(newData);
saveAccounts(list);
modal.remove();
};
}
// 导出
function exportCSV() {
const list = getAccounts();
if (list.length === 0) { alert('无账号可导出'); return; }
const header = '类型,店名,账号,密码\n';
const rows = list.map(i => `${i.type},${i.shop},${i.account},${i.password}`).join('\n');
const BOM = new Uint8Array([0xEF, 0xBB, 0xBF]);
const blob = new Blob([BOM, header + rows], { type: 'text/csv;charset=utf-8' });
saveAs(blob, `给我火账号_${Date.now()}.csv`);
}
// 导入
function importCSV() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.csv';
input.onchange = e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = evt => {
try {
let buffer = evt.target.result;
let decoder = new TextDecoder('utf-8');
let str = decoder.decode(buffer).replace(/^\ufeff/, '');
if (/[\u4e00-\u9fa5]/.test(str) === false && /[�]/.test(str)) {
decoder = new TextDecoder('gbk');
str = decoder.decode(buffer).replace(/^\ufeff/, '');
}
const lines = str.split(/\r?\n/).filter(i => i.trim());
lines.shift();
const accounts = lines.map(line => {
const [type = '', shop = '', account = '', password = ''] = line.split(',');
return {
type: type.trim(),
shop: shop.trim(),
account: account.trim(),
password: password.trim()
};
}).filter(i => i.account && i.password);
if (accounts.length === 0) { alert('未读取到有效账号'); return; }
if (confirm(`导入 ${accounts.length} 个账号,是否覆盖本地?`)) {
saveAccounts(accounts);
}
} catch (err) {
alert('导入失败:格式错误');
}
};
reader.readAsArrayBuffer(file);
};
input.click();
}
// 绑定事件
function bindTableEvents() {
document.querySelector('#accountTable').onclick = e => {
const index = parseInt(e.target.dataset.index);
if (e.target.classList.contains('login-btn')) doLogin(index);
else if (e.target.classList.contains('edit-btn')) showModal(index);
else if (e.target.classList.contains('up-btn')) moveUp(index);
else if (e.target.classList.contains('down-btn')) moveDown(index);
else if (e.target.classList.contains('del-btn')) {
if (confirm('确定删除?')) {
const list = getAccounts();
list.splice(index, 1);
saveAccounts(list);
}
}
};
}
let managerInstance = null;
function initUI() {
if (managerInstance) return;
const ui = document.createElement('div');
ui.id = 'accountManager';
ui.innerHTML = `
`;
document.body.appendChild(ui);
managerInstance = ui;
document.querySelector('#addAccount').onclick = () => showModal();
document.querySelector('#exportCSV').onclick = exportCSV;
document.querySelector('#importCSV').onclick = importCSV;
// 排序模式开关
document.querySelector('#sortBtn').onclick = () => {
sortMode = !sortMode;
document.querySelector('#sortBtn').textContent = sortMode ? '✅ 退出排序' : '🔼 排序模式';
renderTable();
};
renderTable();
bindTableEvents();
}
function destroyUI() {
if (managerInstance) { managerInstance.remove(); managerInstance = null; }
}
function checkUrl() {
return location.href.includes('login') || location.hash.includes('login');
}
function start() {
if (checkUrl()) initUI();
else destroyUI();
}
window.addEventListener('hashchange', start);
start();
})();