// ==UserScript== // @name 联通卡券助手 V11.1 (抽屉交互+ID展示版) // @namespace http://tampermonkey.net/ // @version 11.1 // @description 1.列表内嵌抽屉式展示商品 2.ID改为纯展示 3.保留V9样式布局 4.静默兑换 // @author Analysis // @match https://qy.chinaunicom.cn/* // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; console.log('%c== 联通助手 V11.1 (ID展示版) 已启动 ==', 'color:white; background:#2196F3; font-size:16px;'); let currentCouponContext = null; let currentPage = 1; // ========================================== // 1. 拦截器 // ========================================== const originalFetch = window.fetch; window.fetch = async function(...args) { const response = await originalFetch(...args); const clone = response.clone(); if (response.url && response.url.includes('queryCouponList')) { clone.json().then(data => { if(data && data.data) handleCouponList(data.data); }).catch(e => {}); } return response; }; const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url) { this._url = url; return originalOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(body) { this.addEventListener('readystatechange', function() { if (this.readyState === 4 && this._url && this._url.includes('queryCouponList')) { try { const res = JSON.parse(this.responseText); if(res && res.data) handleCouponList(res.data); } catch (e) {} } }); return originalSend.apply(this, arguments); }; // ========================================== // 2. 辅助功能 // ========================================== function showToast(msg, type = 'info') { const div = document.createElement('div'); div.innerText = msg; div.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.9); color: white; padding: 10px 20px; border-radius: 6px; font-size: 13px; z-index: 1000002; box-shadow: 0 4px 15px rgba(0,0,0,0.5); text-align: center; pointer-events: none; border: 1px solid #444; `; if(type === 'success') div.style.color = '#00e676'; if(type === 'error') div.style.color = '#ff5252'; document.body.appendChild(div); setTimeout(() => div.remove(), 2000); } function enableDrag(element, handle) { let isDragging = false, startX, startY, initialLeft, initialTop; const start = (x, y) => { isDragging = true; startX = x; startY = y; const rect = element.getBoundingClientRect(); initialLeft = rect.left; initialTop = rect.top; element.style.right = 'auto'; element.style.left = initialLeft + 'px'; element.style.top = initialTop + 'px'; }; const move = (x, y) => { if (!isDragging) return; element.style.left = (initialLeft + (x - startX)) + 'px'; element.style.top = (initialTop + (y - startY)) + 'px'; }; const end = () => { isDragging = false; }; (handle||element).addEventListener('mousedown', e => { e.preventDefault(); start(e.clientX, e.clientY); }); document.addEventListener('mousemove', e => move(e.clientX, e.clientY)); document.addEventListener('mouseup', end); (handle||element).addEventListener('touchstart', e => { const t=e.touches[0]; start(t.clientX, t.clientY); }, {passive:false}); document.addEventListener('touchmove', e => { if(isDragging){ e.preventDefault(); const t=e.touches[0]; move(t.clientX, t.clientY); } }, {passive:false}); document.addEventListener('touchend', end); } // ========================================== // 3. 业务逻辑 (抽屉式交互) // ========================================== function handleCouponList(list) { if(list && list.length > 0) list = list.filter(i => !i.consumeWay); const listContainer = document.getElementById('uni-coupon-list'); if(!listContainer) return; listContainer.innerHTML = ''; document.getElementById('uni-status').innerText = `第${currentPage}页: ${list.length}张`; if (list.length === 0) { listContainer.innerHTML = '
当前页无数据
'; return; } list.forEach((item, index) => { // 外层容器 const rowWrapper = document.createElement('div'); rowWrapper.style.cssText = 'border-bottom:1px dashed #444; background:#1e1e1e; margin-bottom:2px; transition: background 0.2s;'; // 卡券信息行 const infoRow = document.createElement('div'); infoRow.style.cssText = 'padding: 8px; font-size:12px; position:relative;'; infoRow.innerHTML = `
${index+1}. ${item.couponName} ${item.source}
ID: ${item.couponId}
`; // 抽屉容器 (默认隐藏) const drawerDiv = document.createElement('div'); drawerDiv.className = 'goods-drawer'; drawerDiv.style.cssText = 'display:none; background:#111; border-top:1px solid #333; padding:5px;'; drawerDiv.innerHTML = '
加载中...
'; rowWrapper.appendChild(infoRow); rowWrapper.appendChild(drawerDiv); listContainer.appendChild(rowWrapper); // 绑定事件 const toggleBtn = infoRow.querySelector('.btn-toggle-drawer'); toggleBtn.onclick = function(e) { e.stopPropagation(); // 切换显示状态 if (drawerDiv.style.display === 'none') { // 展开 drawerDiv.style.display = 'block'; toggleBtn.innerText = '▲ 收起'; toggleBtn.style.background = '#444'; rowWrapper.style.background = '#252526'; // 如果还没有加载过商品,则加载 if(!drawerDiv.hasAttribute('data-loaded')) { fetchActGoods(item, drawerDiv); } } else { // 收起 drawerDiv.style.display = 'none'; toggleBtn.innerText = '▼ 展开兑换选项'; toggleBtn.style.background = '#333'; rowWrapper.style.background = '#1e1e1e'; } }; }); } // 获取商品并渲染到抽屉里 function fetchActGoods(couponItem, targetContainer) { currentCouponContext = couponItem; // 暂存上下文 const actId = couponItem.actId; const url = `/mobile/coupon/getActByCatID?id=${actId}`; fetch(url, { headers: {'X-Requested-With': 'XMLHttpRequest'} }) .then(r => r.json()) .then(res => { let goodsList = []; let relationId = ""; if (res.data && res.data.length > 0) { relationId = res.data[0].actId; if (res.data[0].actGoods) { // 过滤逻辑保持不变 goodsList = res.data[0].actGoods.filter(i => i.goodsName.includes('腾讯视频')); } } renderGoodsToDrawer(goodsList, relationId, targetContainer, couponItem); targetContainer.setAttribute('data-loaded', 'true'); }) .catch(err => { targetContainer.innerHTML = `
查询失败: ${err.message}
`; }); } // 渲染商品列表到抽屉 function renderGoodsToDrawer(goods, relationId, container, couponContext) { container.innerHTML = ''; if (!goods || goods.length === 0) { container.innerHTML = '
无匹配商品 (腾讯视频)
'; return; } goods.forEach(g => { const item = document.createElement('div'); item.style.cssText = 'display:flex; align-items:center; background:#222; margin-bottom:4px; padding:4px; border-radius:4px; border:1px solid #333;'; item.innerHTML = `
${g.goodsName}
`; const btn = item.querySelector('.btn-do-swap'); btn.onclick = function() { // 确保上下文是当前点击的这个券 (防止多开抽屉后上下文错乱) currentCouponContext = couponContext; doWriteOff(g, relationId); }; container.appendChild(item); }); } function doWriteOff(goodItem, relationId) { // 修改点:直接从展示 Span 中获取 ID,不再查找 Input const userIdSpan = document.getElementById('uni-userid-display'); const currentUserId = userIdSpan ? userIdSpan.innerText.trim() : ""; if(!currentUserId || currentUserId.includes("未找到")) { showToast("未找到UserID", "error"); return; } showToast("请求中...", "info"); const params = new URLSearchParams(); params.append('couponId', currentCouponContext.couponId); params.append('createTimeStr', currentCouponContext.createTimeStr || currentCouponContext.createTime); params.append('id', currentCouponContext.id); params.append('actId', currentCouponContext.actId); params.append('goodsIdFrom', goodItem.goodsId); params.append('smsCode', ''); params.append('relationId', relationId); params.append('subPage', window.location.href); const countParamObj = { "pageUrl": window.location.href, "originalPageUrl": window.location.href.split('?')[0], "fPageUrl": "", "operateName": "", "type": "", "countType": 1, "ip": "", "cityName": "", "userId": currentUserId, "channelId": "", "source": "3", "provinceName": "", "operateSystem": "4" }; fetch(`/mobile/coupon/couponWriteOff?countParam=${encodeURIComponent(JSON.stringify(countParamObj))}`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' }, body: params.toString() }) .then(r => r.json()) .then(data => { if (data.responseResult == "1") { showToast("兑换成功! 刷新中...", "success"); setTimeout(() => triggerFetchCoupons(currentPage), 800); } else { showToast(data.errorMsg, "error"); } }) .catch(err => showToast("网络错误", "error")); } function triggerFetchCoupons(page) { const statusSpan = document.getElementById('uni-status'); if(statusSpan) statusSpan.innerText = `加载 P${page}...`; fetch(`/mobile/epom/coupon/queryCouponList?couponCatgId=8&status=2&type=1&pageNo=${page}`, { headers: {'X-Requested-With': 'XMLHttpRequest'} }).then(r=>r.json()).then(d=>{ const pageSpan = document.getElementById('uni-page-num'); if(pageSpan) pageSpan.innerText = page; if(d.data) handleCouponList(d.data); else showToast('该页无数据', 'info'); }); } // ========================================== // 4. UI 构建 (V9 样式 + 悬浮球) // ========================================== function createUI() { // --- 悬浮球 --- const ball = document.createElement('div'); ball.id = 'uni-float-ball'; ball.style.cssText = ` position: fixed; top: 10px; right: 10px; width: 45px; height: 45px; background: rgba(30, 30, 30, 0.95); border: 2px solid #555; border-radius: 50%; box-shadow: 0 4px 10px rgba(0,0,0,0.5); z-index: 999998; cursor: pointer; display: flex; justify-content: center; align-items: center; color: white; font-size: 12px; user-select: none; `; ball.innerText = "助手"; document.body.appendChild(ball); // --- 主面板 --- const div = document.createElement('div'); div.id = 'uni-helper-panel'; div.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 320px; max-height: 90vh; background: rgba(30, 30, 30, 0.95); color: white; border-radius: 8px; z-index: 999999; font-family: 'Segoe UI', sans-serif; font-size: 13px; box-shadow: 0 4px 15px rgba(0,0,0,0.6); display: none; flex-direction: column; border: 1px solid #444; `; // 标题栏 const header = document.createElement('div'); header.style.cssText = ` padding: 10px; border-bottom: 1px solid #555; font-weight: bold; background: #263238; border-top-left-radius: 8px; border-top-right-radius: 8px; cursor: move; user-select: none; display: flex; justify-content: space-between; align-items: center; `; header.innerHTML = ` 卡券助手
就绪
`; div.appendChild(header); // ID 展示区 (修改点:不再是 Input) const configBar = document.createElement('div'); configBar.style.cssText = 'padding: 8px; background: #333; border-bottom: 1px solid #444; font-size:12px; color:#ccc; display:flex; align-items:center;'; // 读取 ID let autoId = localStorage.getItem('qyhm') || localStorage.getItem('mqerDpb') || localStorage.getItem('uni_saved_userid') || '未找到(请登录)'; if(autoId && (autoId.startsWith('{') || autoId.startsWith('"'))) { try { autoId = JSON.parse(autoId); } catch(e){} } configBar.innerHTML = ` User: ${autoId} `; div.appendChild(configBar); // 翻页栏 const actionBar = document.createElement('div'); actionBar.style.cssText = 'padding: 8px; display: flex; gap: 5px; justify-content: center; background: #333; border-bottom: 1px solid #444;'; actionBar.innerHTML = ` 1 `; div.appendChild(actionBar); // 列表 const listDiv = document.createElement('div'); listDiv.id = 'uni-coupon-list'; listDiv.style.cssText = 'flex: 1; overflow-y: auto; min-height: 150px; background: #1e1e1e; padding:5px;'; listDiv.innerHTML = '
请点击刷新
'; div.appendChild(listDiv); document.body.appendChild(div); // --- 交互逻辑 --- // 拖拽 enableDrag(ball); enableDrag(div, header); // 显隐切换 ball.onclick = () => { ball.style.display = 'none'; div.style.display = 'flex'; }; document.getElementById('btn-minimize').onclick = (e) => { e.stopPropagation(); // 防止触发header拖拽等奇葩问题 div.style.display = 'none'; ball.style.display = 'flex'; }; // 翻页 document.getElementById('btn-prev').onclick = () => { if(currentPage>1) { currentPage--; triggerFetchCoupons(currentPage); } }; document.getElementById('btn-next').onclick = () => { currentPage++; triggerFetchCoupons(currentPage); }; document.getElementById('btn-load').onclick = () => triggerFetchCoupons(currentPage); } window.addEventListener('load', () => setTimeout(createUI, 500)); })();