您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
支持账号管理、切换、删除、确认切号、切号后刷新页面。优化版UI更美观,交互更流畅。
// ==UserScript== // @name UPhone 切号工具优化版(账号管理+删除+刷新) // @namespace http://tampermonkey.net/ // @license MIT // @version 3.3 // @description 支持账号管理、切换、删除、确认切号、切号后刷新页面。优化版UI更美观,交互更流畅。 // @author GPT // @match https://uphone.wo-adv.cn/cloudphone/* // @grant none // ==/UserScript== (function () { 'use strict'; // 等待页面加载完成 window.addEventListener('load', () => { const hash = location.hash; if (!['#/personal', '#/discover', '#/home'].includes(hash)) return; // 存储相关函数 const storageKey = '__uphone_token_accounts__'; const getAccounts = () => JSON.parse(localStorage.getItem(storageKey) || '{}'); const saveAccounts = (obj) => localStorage.setItem(storageKey, JSON.stringify(obj)); // 状态变量 let isExpanded = false; let isDragging = false; let dragOffsetX = 0, dragOffsetY = 0; // 创建主容器 const container = document.createElement('div'); Object.assign(container.style, { position: 'fixed', top: '150px', right: '0', width: '50px', height: '50px', zIndex: '99999', cursor: 'pointer', transition: 'all 0.3s ease', userSelect: 'none' }); // 创建浮动按钮 const floatBtn = document.createElement('div'); let isHovering = false; Object.assign(floatBtn.style, { width: '100%', height: '100%', backgroundColor: '#409EFF', color: '#fff', fontSize: '24px', textAlign: 'center', lineHeight: '50px', borderRadius: '50%', // 保持圆形 border: '2px solid rgba(255,255,255,0.3)', boxShadow: '0 4px 12px rgba(64,158,255,0.4)', transition: 'all 0.3s ease', display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'absolute', right: '0' // 初始位置在容器最右侧 }); // 修改鼠标事件 floatBtn.addEventListener('mouseenter', () => { container.style.right = '0'; // 完全显示 floatBtn.style.paddingLeft = '0'; floatBtn.style.marginLeft = '0'; floatBtn.style.borderRadius = '50%'; // 恢复圆形 // floatBtn.style.border = '2px solid rgba(255,255,255,0.3)'; }); floatBtn.addEventListener('mouseleave', () => { if (isExpanded) return; // 如果面板展开则不隐藏 container.style.right = '-60px'; // 容器向右隐藏一半 floatBtn.style.paddingLeft = '25px'; floatBtn.style.marginLeft = '-25px'; floatBtn.style.borderRadius = '10%'; // floatBtn.style.borderRadius = '25px 0 0 25px'; floatBtn.style.border = '2px solid rgba(255,255,255,0.3)'; floatBtn.style.borderRight = 'none'; }); floatBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12" y2="18"></line></svg>'; // 创建控制面板 const panel = document.createElement('div'); Object.assign(panel.style, { width: '280px', backgroundColor: '#fff', border: 'none', borderRadius: '12px', boxShadow: '0 8px 24px rgba(0,0,0,0.15)', padding: '16px', position: 'absolute', right: '60px', top: '0', opacity: '0', transform: 'translateX(20px)', pointerEvents: 'none', transition: 'all 0.3s ease', fontFamily: '"PingFang SC", "Microsoft YaHei", sans-serif' }); // 面板标题 const title = document.createElement('div'); Object.assign(title.style, { fontSize: '16px', fontWeight: '600', color: '#333', marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }); title.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> 账号管理'; // 账号列表容器 const accountList = document.createElement('div'); accountList.style.maxHeight = '300px'; accountList.style.overflowY = 'auto'; accountList.style.marginBottom = '16px'; // 刷新账号列表函数 const refreshAccountList = () => { accountList.innerHTML = ''; const accounts = getAccounts(); if (Object.keys(accounts).length === 0) { const emptyState = document.createElement('div'); Object.assign(emptyState.style, { color: '#999', textAlign: 'center', padding: '16px', fontSize: '14px' }); emptyState.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg><div>暂无保存的账号</div>'; accountList.appendChild(emptyState); return; } Object.entries(accounts).forEach(([name, token]) => { const accountItem = document.createElement('div'); Object.assign(accountItem.style, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 12px', marginBottom: '8px', backgroundColor: '#f8f9fa', borderRadius: '8px', transition: 'all 0.2s ease' }); // 账号名称 const accountName = document.createElement('div'); accountName.textContent = name; accountName.style.flex = '1'; accountName.style.fontSize = '14px'; accountName.style.fontWeight = '500'; accountName.style.overflow = 'hidden'; accountName.style.textOverflow = 'ellipsis'; accountName.style.whiteSpace = 'nowrap'; // 操作按钮容器 const actionButtons = document.createElement('div'); actionButtons.style.display = 'flex'; actionButtons.style.gap = '6px'; // 切换按钮 const switchBtn = document.createElement('button'); Object.assign(switchBtn.style, { padding: '4px 8px', fontSize: '12px', backgroundColor: '#409EFF', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', transition: 'all 0.2s ease' }); switchBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" y1="3" x2="14" y2="10"></line><line x1="3" y1="21" x2="10" y2="14"></line></svg>'; switchBtn.title = '切换到此账号'; switchBtn.onmouseenter = () => switchBtn.style.backgroundColor = '#337ecc'; switchBtn.onmouseleave = () => switchBtn.style.backgroundColor = '#409EFF'; switchBtn.onclick = () => { if (confirm(`确定切换到账号「${name}」吗?`)) { const baseInfoStr = localStorage.getItem('baseInfo'); if (!baseInfoStr) return alert('账号信息不存在'); try { // 自定义位置逻辑 const baseInfo = JSON.parse(baseInfoStr); baseInfo.data.token = token; baseInfo.data.userInfo = {}; localStorage.setItem('baseInfo', JSON.stringify(baseInfo)); showLoading('切换中...'); setTimeout(() => location.reload(), 800); } catch (e) { alert('切换失败:账号信息解析错误'); console.error(e); } } }; // 删除按钮 const deleteBtn = document.createElement('button'); Object.assign(deleteBtn.style, { padding: '4px 8px', fontSize: '12px', backgroundColor: '#ff4d4f', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', transition: 'all 0.2s ease' }); deleteBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>'; deleteBtn.title = '删除此账号'; deleteBtn.onmouseenter = () => deleteBtn.style.backgroundColor = '#d9363e'; deleteBtn.onmouseleave = () => deleteBtn.style.backgroundColor = '#ff4d4f'; deleteBtn.onclick = () => { if (confirm(`确定删除账号「${name}」?此操作不可恢复!`)) { const accounts = getAccounts(); delete accounts[name]; saveAccounts(accounts); refreshAccountList(); } }; // 组装元素 actionButtons.appendChild(switchBtn); actionButtons.appendChild(deleteBtn); accountItem.appendChild(accountName); accountItem.appendChild(actionButtons); accountList.appendChild(accountItem); // 悬停效果 accountItem.onmouseenter = () => { accountItem.style.backgroundColor = '#ebf3ff'; }; accountItem.onmouseleave = () => { accountItem.style.backgroundColor = '#f8f9fa'; }; }); }; // 按钮组容器 const buttonGroup = document.createElement('div'); buttonGroup.style.display = 'flex'; buttonGroup.style.gap = '10px'; buttonGroup.style.marginTop = '16px'; // 添加账号按钮 const addBtn = document.createElement('button'); Object.assign(addBtn.style, { flex: '1', padding: '8px 12px', fontSize: '13px', fontWeight: '500', backgroundColor: '#f0f2f5', color: '#409EFF', border: 'none', borderRadius: '6px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '6px', transition: 'all 0.2s ease' }); addBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg> 添加账号'; addBtn.onmouseenter = () => { addBtn.style.backgroundColor = '#e6f1ff'; }; addBtn.onmouseleave = () => { addBtn.style.backgroundColor = '#f0f2f5'; }; addBtn.onclick = () => { const name = prompt('请输入账号名称:'); if (!name) return; const token = prompt('请输入该账号的 token:'); if (!token) return; const accounts = getAccounts(); if (accounts[name]) { if (!confirm(`账号「${name}」已存在,是否覆盖?`)) return; } accounts[name] = token; saveAccounts(accounts); refreshAccountList(); }; // 退出登录按钮 const logoutBtn = document.createElement('button'); Object.assign(logoutBtn.style, { flex: '1', padding: '8px 12px', fontSize: '13px', fontWeight: '500', backgroundColor: '#fff0f0', color: '#ff4d4f', border: '1px solid #ffd6d6', borderRadius: '6px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '6px', transition: 'all 0.2s ease' }); logoutBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg> 退出登录'; logoutBtn.onmouseenter = () => { logoutBtn.style.backgroundColor = '#ffebeb'; }; logoutBtn.onmouseleave = () => { logoutBtn.style.backgroundColor = '#fff0f0'; }; logoutBtn.onclick = () => { if (confirm('确定要退出当前账号吗?')) { const baseInfoStr = localStorage.getItem('baseInfo'); if (!baseInfoStr) return alert('账号信息不存在'); try { const baseInfo = JSON.parse(baseInfoStr); baseInfo.data.token = ""; baseInfo.data.userInfo = {}; localStorage.setItem('baseInfo', JSON.stringify(baseInfo)); showLoading('退出中...'); setTimeout(() => location.reload(), 800); } catch (e) { alert('退出失败:账号信息解析错误'); console.error(e); } } }; // 加载状态提示 const showLoading = (text) => { const loading = document.createElement('div'); Object.assign(loading.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', backgroundColor: 'rgba(0,0,0,0.5)', zIndex: '100000', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: '16px', gap: '16px' }); loading.innerHTML = ` <div class="spinner" style="width: 40px; height: 40px; border: 4px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: #fff; animation: spin 1s ease-in-out infinite;"></div> <div>${text}</div> `; document.body.appendChild(loading); // 添加动画 const style = document.createElement('style'); style.textContent = ` @keyframes spin { to { transform: rotate(360deg); } } `; document.head.appendChild(style); // 3秒后自动移除 setTimeout(() => { if (loading.parentNode) { loading.parentNode.removeChild(loading); } if (style.parentNode) { style.parentNode.removeChild(style); } }, 3000); }; // 组装面板 buttonGroup.appendChild(addBtn); buttonGroup.appendChild(logoutBtn); panel.appendChild(title); panel.appendChild(accountList); panel.appendChild(buttonGroup); // 主容器组装 container.appendChild(floatBtn); container.appendChild(panel); // 添加到页面 document.body.appendChild(container); // 修改togglePanel函数(确保展开时完全显示) const togglePanel = () => { isExpanded = !isExpanded; if (isExpanded) { refreshAccountList(); panel.style.opacity = '1'; panel.style.transform = 'translateX(0)'; panel.style.pointerEvents = 'auto'; floatBtn.style.transform = 'rotate(90deg)'; floatBtn.style.opacity = '1'; // 展开时强制不透明 floatBtn.style.backgroundColor = '#409EFF'; } else { panel.style.opacity = '0'; panel.style.transform = 'translateX(20px)'; panel.style.pointerEvents = 'none'; floatBtn.style.transform = 'rotate(0)'; if (!isHovering) { // 收起后如果鼠标不在按钮上则恢复半透明 floatBtn.style.opacity = '1'; // floatBtn.style.backgroundColor = 'rgba(64,158,255,0.2)'; } } }; // 点击事件 floatBtn.addEventListener('click', togglePanel); setTimeout(() => { if (!isExpanded && !isHovering) { container.style.right = '-40px'; // 执行隐藏 floatBtn.style.borderRadius = '10%'; } }, 2000); }); })();