// ==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;
});
})();