// ==UserScript== // @name 问卷星速填通自动化神器 // @namespace https://docs.scriptcat.org/ // @version 0.1.3 // @description 最人性化的问卷星自动化填写神器:一行代码不改!可视化界面设定比例,点击即可开始刷问卷!会安装就会使用! // @author zemelee // @match *://*.wjx.* // @match https://www.wjx.cn/* // @match https://w.wjx.com/* // @match https://v.wjx.cn/* // @grant none // ==/UserScript== (function () { 'use strict'; function checkAndRedirect() { const currentUrl = window.location.href; const activityIdMatch = currentUrl.match(/[?&]activityid=([^&]+)/i); if (activityIdMatch) { const activityId = activityIdMatch[1]; const targetUrl = `https://v.wjx.cn/vm/${activityId}.aspx`; window.location.href = targetUrl; } } // 执行检测 checkAndRedirect(); const style = document.createElement('style'); style.textContent = ` .control-panel { position: fixed; top: 20px; left: 20px; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.15); z-index: 9999; max-width: 400px; max-height: 80vh; overflow-y: auto; transition: all 0.3s ease; } .control-panel.dragging { transition: none; opacity: 0.9; } .panel-header { cursor: move; user-select: none; padding-top: 40px; border-bottom: 1px solid #eee; color: #666; font-size: 12px; display: flex; align-items: center; gap: 5px; } .panel-header:hover { color: #409eff; background-color: #f5f7fa; } .drag-handle { font-size: 14px; } .control-panel.collapsed { max-height: 50px; overflow: hidden; } .control-panel h3 { margin: 0 0 10px 0; font-size: 16px; color: #333; } .control-panel button { display: block; width: 100%; margin: 5px 0; padding: 8px 12px; background-color: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; } .control-panel button:hover { background-color: #66b1ff; } .control-panel button.secondary { background-color: #67c23a; } .control-panel button.secondary:hover { background-color: #85ce61; } .control-panel button.danger { background-color: #f56c6c; } .control-panel button.danger:hover { background-color: #f78989; } .control-panel button.warning { background-color: #e6a23c; } .control-panel button.warning:hover { background-color: #ebb563; } .control-panel .loop-input-group { display: flex; gap: 8px; margin: 8px 0; } .control-panel .loop-input-group input { flex: 1; padding: 8px 12px; border: 1px solid #dcdfe6; border-radius: 4px; font-size: 13px; outline: none; } .control-panel .loop-input-group input:focus { border-color: #409eff; box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); } .control-panel .loop-status { background: #f0f9ff; padding: 8px; border-radius: 4px; font-size: 12px; color: #409eff; margin: 8px 0; text-align: center; } .data-output { background: #f5f7fa; padding: 10px; border-radius: 4px; font-size: 11px; max-height: 300px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; margin-top: 10px; } .close-btn { position: absolute; top: 10px; right: 20px; background: none; border: none; font-size: 18px; cursor: pointer; color: #999; padding: 0; width: auto; } .close-btn:hover { color: #333; } .toggle-btn { position: absolute; top: 10px; right: 0px; background: none; border: none; font-size: 16px; cursor: pointer; color: #409eff; padding: 0; transition: transform 0.3s ease; } .toggle-btn:hover { color: #66b1ff; } .toggle-btn.collapsed { transform: rotate(180deg); } .panel-content { transition: opacity 0.3s ease; } .control-panel.collapsed .panel-content { opacity: 0; pointer-events: none; } .ad-banner { color: white; padding: 12px; border-radius: 6px; margin-top: 15px; text-align: center; animation: gradientShift 8s ease infinite; background-size: 200% 200%; } .ad-banner.style-1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .ad-banner.style-2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); } .ad-banner.style-3 { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); } .ad-banner.style-4 { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); } .ad-banner.style-5 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); } .ad-banner.style-6 { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); color: #333; } @keyframes gradientShift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .ad-banner a { color: inherit; text-decoration: none; font-weight: bold; font-size: 14px; display: block; margin-bottom: 8px; } .ad-banner a:hover { text-decoration: underline; } .ad-desc { font-size: 11px; line-height: 1.5; opacity: 0.9; } .ad-desc span { display: block; margin: 2px 0; } .radio-input, .matrix-input { padding: 4px 8px; margin-right: 8px !important; margin-left: 4px; border: 1px solid #dcdfe6; border-radius: 4px; height: 28px; line-height: 28px; font-size: 12px; width: 60px; box-sizing: border-box; outline: none; } .radio-input:focus { border-color: #409eff; box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); } .radio-input[type="text"] { width: 120px; } .matrix-input{ width: 40px; } .percent-display { display: inline-block; margin-left: 8px; padding: 2px 6px; background-color: #f0f9ff; border: 1px solid #409eff; border-radius: 3px; color: #409eff; font-size: 11px; font-weight: bold; min-width: 45px; text-align: center; } .text-input { padding: 8px 10px; margin-right: 8px !important; margin-left: 4px; border: 1px solid #dcdfe6; border-radius: 4px; font-size: 12px; width: 300px; min-height: 100px; box-sizing: border-box; outline: none; line-height: 1.5; resize: vertical; } .text-input:focus { border-color: #409eff; box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); } .control-panel button.info { background-color: #909399; } .control-panel button.info:hover { background-color: #a6a9ad; } .help-dialog { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 10000; display: flex; align-items: center; justify-content: center; } .help-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); } .help-content { position: relative; background: white; border-radius: 12px; max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); animation: slideIn 0.3s ease; } @keyframes slideIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } .help-header { padding: 5px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; background: white; z-index: 1; } .help-header h3 { margin: 0; font-size: 18px; color: #333; } .help-close { background: none; border: none; font-size: 24px; cursor: pointer; color: #999; padding: 0; width: auto; line-height: 1; } .help-close:hover { color: #333; } .help-body { padding: 15px; } .help-section { padding-bottom: 5px; border-bottom: 1px solid #f0f0f0; } .help-section:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } .help-section h4 { margin: 0 0 5px 0; font-size: 15px; color: #333; } .help-section p { margin: 4px 0; font-size: 13px; color: #666; line-height: 1.6; } .help-example { background: #f0f9ff; padding: 5px; border-radius: 6px; border-left: 3px solid #409eff; font-size: 12px !important; margin: 5px 0 !important; } .help-tip { background: #fff7e6; padding: 5px; border-radius: 6px; border-left: 3px solid #faad14; font-size: 12px !important; } .help-code { background: #2d2d2d; color: #f8f8f2; padding: 10px; border-radius: 6px; font-family: 'Courier New', monospace; font-size: 12px; margin: 10px 0; white-space: pre-wrap; } .help-footer { padding: 10px; border-top: 1px solid #eee; text-align: center; position: sticky; bottom: 0; background: white; } .help-btn { background-color: #409eff; color: white; border: none; padding: 10px 30px; border-radius: 20px; cursor: pointer; font-size: 14px; transition: background-color 0.3s ease; } .help-btn:hover { background-color: #66b1ff; } `; // 将样式表插入到页面头部 document.head.appendChild(style); function randint(a, b) { return Math.floor(Math.random() * (b - a + 1) + a); } // ========== 百分比计算函数(普通题型) ========== function handlePercentInput(event) { const input = event.target; const topic = input.dataset.topic; // 获取同一题目下的所有输入框 const inputs = document.querySelectorAll(`input[data-topic="${topic}"].radio-input`); const percentDisplays = document.querySelectorAll(`input[data-topic="${topic}"].radio-input + span.percent-display`); // 计算总和 let total = 0; inputs.forEach(inp => { const value = parseFloat(inp.value) || 0; total += value; }); // 计算并更新每个选项的百分比 inputs.forEach((inp, index) => { const value = parseFloat(inp.value) || 0; let percent = 0; if (total > 0) { percent = (value / total) * 100; } if (percentDisplays[index]) { percentDisplays[index].textContent = percent.toFixed(0) + '%'; } }); // 实时保存数据 window.wjxSaveData(); } // ========== 矩阵题百分比计算函数 ========== function handleMatrixPercentInput(event) { const input = event.target; const topic = input.dataset.topic; const row = input.dataset.row; // 获取同一题目同一行下的所有输入框 const inputs = document.querySelectorAll(`input[data-topic="${topic}"][data-row="${row}"].matrix-input`); const percentDisplays = document.querySelectorAll(`input[data-topic="${topic}"][data-row="${row}"].matrix-input + span.percent-display`); // 计算该行的总和 let total = 0; inputs.forEach(inp => { const value = parseFloat(inp.value) || 0; total += value; }); // 计算并更新该行每个选项的百分比 inputs.forEach((inp, index) => { const value = parseFloat(inp.value) || 0; let percent = 0; if (total > 0) { percent = (value / total) * 100; } if (percentDisplays[index]) { percentDisplays[index].textContent = percent.toFixed(0) + '%'; } }); // 实时保存数据 window.wjxSaveData(); } // ========== localStorage 相关函数 ========== const STORAGE_KEY = 'wjx_percent_data'; const LOOP_KEY = 'wjx_loop_count'; const LOOP_CURRENT_KEY = 'wjx_loop_current'; let savedData = null; // 缓存加载的数据 // 实时保存数据到 localStorage window.wjxSaveData = function () { const data = collectAllData(); localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } // 从 localStorage 重新加载数据 window.wjxLoadData = function () { const saved = localStorage.getItem(STORAGE_KEY); const data = JSON.parse(saved); savedData = data; if(data){ applyData(data); } } // 获取保存的数据中指定题目的值 function getSavedValue(topic, row, index, text = 0) { if (!savedData || !savedData[topic]) return text ? '' : 1; // 默认值:文本题返回空字符串,其他返回1 const topicData = savedData[topic]; // 文本题直接返回整个数组 if (text) { if (Array.isArray(topicData)) { return topicData.join('\n'); } return ''; } if (row) { // 矩阵题 if (topicData && topicData[row] && topicData[row][index] !== undefined) { return topicData[row][index]; } } else { // 普通题型 if (topicData && topicData[index] !== undefined) { return topicData[index]; } } return 1; // 默认值1 } // 清除 localStorage 数据 window.wjxClearData = function () { localStorage.removeItem(STORAGE_KEY); alert('缓存已清除'); } // 收集所有题目数据 function collectAllData() { const result = {}; const processedTopics = new Set(); // 普通题型数据收集(包括 .radio-input 和 .matrix-input) const allInputs = document.querySelectorAll('.radio-input[data-topic], .matrix-input[data-topic]'); allInputs.forEach(input => { const topic = input.dataset.topic; const row = input.dataset.row; if (processedTopics.has(`${topic}-${row || 'default'}`)) { return; } processedTopics.add(`${topic}-${row || 'default'}`); if (row) { // 矩阵题 if (!result[topic]) { result[topic] = []; // 二维数组 } // 一行的输入框 const rowInputs = document.querySelectorAll(`.radio-input[data-topic="${topic}"][data-row="${row}"], .matrix-input[data-topic="${topic}"][data-row="${row}"]`); const values = []; // 一行的比例 rowInputs.forEach(inp => { values.push(parseFloat(inp.value) || 0); }); result[topic].push(values); // 一行的比例 } else { // 普通题型 const topicInputs = document.querySelectorAll(`.radio-input[data-topic="${topic}"]:not([data-row])`); const values = []; topicInputs.forEach(inp => { values.push(parseFloat(inp.value) || 0); }); result[topic] = values; } }); // 文本题型数据收集 const textInputs = document.querySelectorAll('.text-input'); textInputs.forEach((input) => { const topic = input.dataset.topic const valueArray = input.value.split('\n').filter(item => item.trim() !== ''); result[`${topic}`] = valueArray; }); return result; } // 应用数据到输入框 function applyData(data) { // 应用普通题型和矩阵题数据 Object.keys(data).forEach((topic) => { const topicData = data[topic]; // 矩阵题:二维数组 if (topicData && topicData.length > 0 && Array.isArray(topicData[0])) { Object.keys(topicData).forEach(row => { const rowInputs = document.querySelectorAll(`.radio-input[data-topic="${topic}"][data-row="${row}"], .matrix-input[data-topic="${topic}"][data-row="${row}"]`); topicData[row].forEach((value, index) => { if (rowInputs[index]) { rowInputs[index].value = value; rowInputs[index].dispatchEvent(new Event('input')); } }); }); } else if (topicData && topicData.length > 0 && typeof topicData[0] === 'number') { // 普通题型:数字数组 const inputs = document.querySelectorAll(`.radio-input[data-topic="${topic}"]:not([data-row])`); topicData.forEach((value, idx) => { if (inputs[idx]) { inputs[idx].value = value; inputs[idx].dispatchEvent(new Event('input')); } }); } else if (topicData && topicData.length > 0 && typeof topicData[0] === 'string') { // 文本题:字符串数组 const textInputs = document.querySelectorAll(`.text-input[data-topic="${topic}"]:not([data-row])`); if (textInputs.length > 0) { textInputs[0].value = topicData.join('\n'); } } }); } // 开始循环提交 window.wjxStartLoop = async function (totalCount) { if (!totalCount || totalCount < 1) { alert('请输入有效的循环次数'); return; } localStorage.setItem(LOOP_KEY, totalCount); localStorage.setItem(LOOP_CURRENT_KEY, 0); await wjxAutoFill(); } // 停止循环 window.wjxStopLoop = function () { localStorage.removeItem(LOOP_KEY); localStorage.removeItem(LOOP_CURRENT_KEY); alert('循环已停止'); location.reload(); } // 获取当前循环状态 function getLoopStatus() { const totalCount = localStorage.getItem(LOOP_KEY); const currentCount = localStorage.getItem(LOOP_CURRENT_KEY); return { totalCount: totalCount ? parseInt(totalCount) : 0, currentCount: currentCount ? parseInt(currentCount) : 0 }; } // 更新循环状态显示 function updateLoopStatus() { const status = getLoopStatus(); const statusEl = document.querySelector('.loop-status'); if (statusEl) { if (status.totalCount > 0) { statusEl.textContent = `循环进度: ${status.currentCount} / ${status.totalCount}`; } else { statusEl.textContent = '未开始循环'; } } } // 根据比例自动填写问卷 window.wjxAutoFill = async function () { const data = collectAllData(); // 填写单选题和矩阵题 Object.keys(data).forEach(topic => { const topicData = data[topic]; const type = document.querySelector(`#div${topic}`).getAttribute("type") // 填写矩阵题:二维数组 if (topicData && topicData.length > 0 && Array.isArray(topicData[0])) { Object.keys(topicData).forEach(row => { // 二维数组,row表示当前行的比例 const values = topicData[row]; const total = values.reduce((sum, val) => sum + val, 0); // 一行和 if (total > 0) { const probabilities = values.map(val => val / total); const random = Math.random(); let cumulative = 0; let selectedIndex = 0; for (let i = 0; i < probabilities.length; i++) { cumulative += probabilities[i]; if (random <= cumulative) { selectedIndex = i; break; } } const tds = document.querySelectorAll(`#divRefTab${topic} tr[rowindex="${row}"] td`); const tdElements = Array.from(tds).slice(1); if (tdElements[selectedIndex]) { tdElements[selectedIndex].click(); } } }); } else if (topicData && topicData.length > 0 && typeof topicData[0] === "number") { // 填写普通题型:数字数组 const total = topicData.reduce((sum, val) => sum + val, 0); if (total > 0) { // 计算每个选项的概率 const probabilities = topicData.map(val => val / total); // 生成随机数选择选项 const random = Math.random(); let cumulative = 0; let selectedIndex = 0; for (let i = 0; i < probabilities.length; i++) { cumulative += probabilities[i]; if (random <= cumulative) { selectedIndex = i; break; } } if (type == "3") { const opt = document.querySelector(`#div${topic} > div.ui-controlgroup > div:nth-child(${selectedIndex + 1})`); if (opt) { opt.click(); } } else if (type == "4") { const opts = document.querySelectorAll(`#div${topic} > div.ui-controlgroup > div`); let scaledTopicData = topicData; if (total<100){ const scaleRatio = 100 / total; scaledTopicData = topicData.map(num => { const scaledNum = num * scaleRatio; return Number(scaledNum.toFixed(2)); }); } let isAnySelected = false; // 标记是否已有选中项 const selectedIndexes = []; // 存储选中的索引 scaledTopicData.forEach((prob, idx) => { if (prob <= 0) return; const random = Math.random() * 100; if (random <= prob) { selectedIndexes.push(idx); isAnySelected = true; } }); // 如果没有选中任何项,随机选一个有效选项 if (!isAnySelected) { const validIndexes = scaledTopicData.map((p, i) => i).filter(i => scaledTopicData[i] > 0); const randomValidIdx = validIndexes[Math.floor(Math.random() * validIndexes.length)]; selectedIndexes.push(randomValidIdx); } selectedIndexes.forEach(idx => { if (opts[idx]) { // 防止索引越界 opts[idx].click(); } }); } else if (type == "5") { const opt = document.querySelector(`#div${topic} > div.scale-div.defaultScale > div > ul > li:nth-child(${selectedIndex + 1})`) if (opt) { opt.click(); } } else if (type == "8") { const textInput = document.querySelector(`#q${topic}`) textInput.value = randint(topicData[0], topicData[1]) } } } else if (topicData && (type == "1" || type == "2")) { let idx = randint(0, topicData.length - 1); // 随机选一个答案 document.querySelector(`#q${topic}`).value = topicData[idx] } }); await submit() } async function submit() { const status = getLoopStatus(); await new Promise((resolve) => { setTimeout(() => { //点击提交按钮 const nextBtn = document.querySelector("#ctlNext") if (nextBtn) { nextBtn.click(); resolve(); } }, 500); }); // 延迟 2 秒后点击验证按钮 await new Promise((resolve) => { setTimeout(() => { let verify = document.querySelector("#layui-layer1 > div.layui-layer-btn.layui-layer-btn- > a") if(verify){ verify.click(); } resolve(); }, 2000); }); // 如果正在循环,更新计数并刷新 if (status.totalCount > 0) { const newCount = status.currentCount + 1; if (newCount < status.totalCount) { localStorage.setItem(LOOP_CURRENT_KEY, newCount); // 延迟3秒后刷新页面继续 setTimeout(() => { location.reload(); }, 3000); } else { // 循环完成 localStorage.removeItem(LOOP_KEY); localStorage.removeItem(LOOP_CURRENT_KEY); } } } // 创建使用说明弹窗 window.showHelpDialog = function() { // 移除已存在的弹窗 const existingDialog = document.querySelector('.help-dialog'); if (existingDialog) { existingDialog.remove(); return; } const dialog = document.createElement('div'); dialog.className = 'help-dialog'; dialog.innerHTML = `
单选题会根据比例随机选择一个选项,每次只选一个。
所有选项比例相加无需等于100,脚本会自动计算百分比。
多选题每个选项独立判断,按百分比概率决定是否选中。
例如:选项A填50,则A有50%概率被选中;选项B填80,则B有80%概率被选中。可能同时选中多个选项。
建议总和在100左右,避免选中过多或过少。
在文本框中每行填写一个答案,系统会随机选择一行填入。如:
张三 李四 王五
系统会随机选择"张三"、"李四"或"王五"中的一个填入。
矩阵题每一行独立设置比例,每行会根据比例随机选择一个选项;不同行的比例设置互不影响。
设置最小值和最大值,系统会在该范围内随机选择一个数值。
最小值填1,最大值填10,则会在1-10之间随机选择一个整数。