// ==UserScript== // @name 平台用户批量导入助手 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 解析CSV并自动填写新增用户表单,支持可视化操作,面板可拖动 // @author cc // @match *://*/user-manage-index* // @grant none // ==/UserScript== (function() { 'use strict'; // 状态管理 let csvData = []; let currentIndex = 0; let isRunning = false; // 创建可视化界面 const panelHTML = `
批量导入用户助手
尚未加载数据
`; document.body.insertAdjacentHTML('beforeend', panelHTML); // 元素引用 const panel = document.getElementById('batch-import-panel'); const header = document.getElementById('bi-header'); const toggleBtn = document.getElementById('bi-toggle'); const content = document.getElementById('bi-content'); const csvInput = document.getElementById('bi-csv-input'); const uploadBtn = document.getElementById('bi-upload-btn'); const info = document.getElementById('bi-info'); const preview = document.getElementById('bi-preview'); const startBtn = document.getElementById('bi-start-btn'); const stopBtn = document.getElementById('bi-stop-btn'); const logDiv = document.getElementById('bi-log'); // --- 拖动功能实现 --- let isDragging = false; let startX, startY, initialLeft, initialTop; // 初始化位置(从 right/bottom 转换为 left/top) function initPosition() { const rect = panel.getBoundingClientRect(); panel.style.left = rect.left + 'px'; panel.style.top = rect.top + 'px'; panel.style.right = 'auto'; panel.style.bottom = 'auto'; } header.addEventListener('mousedown', (e) => { // 如果点击的是折叠按钮,不触发拖动 if (e.target === toggleBtn) return; initPosition(); // 确保使用 left/top 定位 isDragging = true; startX = e.clientX; startY = e.clientY; initialLeft = panel.offsetLeft; initialTop = panel.offsetTop; panel.style.transition = 'none'; // 拖动时去掉过渡动画,防止卡顿 }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; e.preventDefault(); // 防止拖动时选中页面文字 const dx = e.clientX - startX; const dy = e.clientY - startY; let newLeft = initialLeft + dx; let newTop = initialTop + dy; // 边界限制,防止拖出屏幕 const maxLeft = window.innerWidth - panel.offsetWidth; const maxTop = window.innerHeight - panel.offsetHeight; newLeft = Math.max(0, Math.min(newLeft, maxLeft)); newTop = Math.max(0, Math.min(newTop, maxTop)); panel.style.left = newLeft + 'px'; panel.style.top = newTop + 'px'; }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; panel.style.transition = ''; // 恢复过渡动画 } }); // --- 拖动功能结束 --- // 界面交互:折叠/展开 toggleBtn.addEventListener('click', () => { const isHidden = content.style.display === 'none'; content.style.display = isHidden ? 'block' : 'none'; toggleBtn.textContent = isHidden ? '—' : '+'; }); // 上传按钮 uploadBtn.addEventListener('click', () => csvInput.click()); // 解析CSV csvInput.addEventListener('change', (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(evt) { const text = evt.target.result; const lines = text.split(/\r?\n/).filter(line => line.trim() !== ''); if (lines.length < 2) { alert('CSV文件至少需要包含表头和一行数据'); return; } csvData = []; // 跳过第一行表头 for (let i = 1; i < lines.length; i++) { const cols = lines[i].split(','); if (cols.length >= 3) { csvData.push({ account: cols[0].trim(), password: cols[1].trim(), nickname: cols[2].trim() }); } } if (csvData.length > 0) { info.textContent = `成功加载 ${csvData.length} 条用户数据`; info.style.color = '#67c23a'; preview.value = csvData.map((u, i) => `${i+1}. 账号:${u.account} 昵称:${u.nickname}`).join('\n'); startBtn.disabled = false; currentIndex = 0; } else { alert('未解析到有效数据,请检查CSV格式'); } }; reader.readAsText(file, 'UTF-8'); }); // 核心:模拟Vue输入(解决Vue双向绑定问题) function simulateVueInput(element, value) { const event = new Event('input', { bubbles: true }); element.value = value; element.dispatchEvent(event); } // 核心:点击页面上的"新增用户"按钮 function clickAddButton() { const btn = document.querySelector('button.el-button--primary.button_primary'); if (btn && btn.textContent.includes('新增用户')) { btn.click(); return true; } return false; } // 核心:填写表单 function fillForm(user) { return new Promise((resolve) => { setTimeout(() => { // 获取输入框(通过placeholder定位更准确) const inputs = document.querySelectorAll('.el-dialog .el-form .el-input__inner'); const accountInput = Array.from(inputs).find(i => i.placeholder.includes('请输入用户账号')); const passwordInput = Array.from(inputs).find(i => i.placeholder.includes('请输不少于8位')); const nicknameInput = Array.from(inputs).find(i => i.placeholder.includes('请输入用户昵称')); if (accountInput) simulateVueInput(accountInput, user.account); if (passwordInput) simulateVueInput(passwordInput, user.password); if (nicknameInput) simulateVueInput(nicknameInput, user.nickname); // 点击确认按钮 setTimeout(() => { const confirmBtn = Array.from(document.querySelectorAll('.el-dialog__footer .el-button--primary')).find(b => b.textContent.includes('确 认')); if (confirmBtn) confirmBtn.click(); // 等待弹窗关闭和接口响应 setTimeout(() => { resolve(); }, 1500); // 1.5秒等待接口响应和弹窗关闭 }, 500); // 0.5秒等待表单渲染和校验 }, 800); // 0.8秒等待弹窗打开动画 }); } // 日志输出 function addLog(msg) { logDiv.innerHTML += `
${new Date().toLocaleTimeString()}: ${msg}
`; logDiv.scrollTop = logDiv.scrollHeight; } // 开始自动录入 startBtn.addEventListener('click', async () => { if (csvData.length === 0) return; isRunning = true; startBtn.style.display = 'none'; stopBtn.style.display = 'block'; addLog('开始执行自动录入...'); for (currentIndex = 0; currentIndex < csvData.length; currentIndex++) { if (!isRunning) { addLog('用户手动停止了录入'); break; } const user = csvData[currentIndex]; addLog(`正在录入第 ${currentIndex + 1} 个用户: ${user.account}`); // 1. 点击新增用户按钮 if (!clickAddButton()) { addLog('未找到[新增用户]按钮,请确认页面状态'); continue; } // 2. 填写表单并确认 await fillForm(user); addLog(`第 ${currentIndex + 1} 个用户处理完毕`); } isRunning = false; startBtn.style.display = 'block'; stopBtn.style.display = 'none'; startBtn.disabled = true; info.textContent = '录入流程结束'; addLog('全部录入流程结束'); }); // 停止录入 stopBtn.addEventListener('click', () => { isRunning = false; }); })();