| traditionalApp/pages/settings/settings.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traditionalApp/pages/webview/webview.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traditionalApp/utils/webview-cache.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
traditionalApp/pages/settings/settings.vue
@@ -96,6 +96,7 @@ import { bindSettingsWechat, cancelSettingsAccount, getSettingsProfile, logoutSettingsAccount } from './service.js' import { cacheSettingsProfile, clearSettingsLoginSession, isSuccessResponse, maskPhone, normalizeSettingsProfile } from './utils.js' import { normalizeOptionalText } from '@/utils/utils.js' import { clearWebViewData } from '@/utils/webview-cache.js' interface WechatLoginResult { code?: string @@ -291,105 +292,34 @@ } /** * 清除 WebView 缓存和 localStorage。 * 退出登录时清除本地存储与 WebView 缓存。 * WebView 部分复用进入直播间前的彻底清理逻辑(含原生 Cookie/存储 API),保证登录态不残留。 */ function clearWebViewCache() { console.log('[clearWebViewCache] 函数被调用') let logMessages = [] logMessages.push('开始清除缓存') try { // 清除 uni.storage(所有环境通用) try { uni.clearStorageSync() console.log('[clearWebViewCache] uni.storage 已清除') logMessages.push('uni.storage 已清除') } catch (e) { console.error('[clearWebViewCache] 清除 uni.storage 失败:', e) logMessages.push('uni.storage 清除失败') } // #ifdef APP-PLUS console.log('[clearWebViewCache] APP-PLUS 环境,开始清除 WebView 缓存') logMessages.push('APP-PLUS 环境检测到') // 清除 plus.storage if (plus.storage) { plus.storage.clear() console.log('[clearWebViewCache] plus.storage 已清除') logMessages.push('plus.storage 已清除') } // 清除 WebView 缓存 const currentWebview = plus.webview.currentWebview() if (currentWebview && typeof currentWebview.clearCache === 'function') { currentWebview.clearCache() console.log('[clearWebViewCache] 当前 WebView 缓存已清除') logMessages.push('当前 WebView 缓存已清除') } // 清除所有 WebView 的缓存 const allWebviews = plus.webview.all() if (allWebviews && allWebviews.length > 0) { allWebviews.forEach((webview) => { try { // 清除缓存 if (webview && typeof webview.clearCache === 'function') { webview.clearCache() } // 清除 Cookie 和存储 if (webview && typeof webview.evalJS === 'function') { webview.evalJS(` try { // 清除所有 Cookie document.cookie.split(";").forEach(function(c) { document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); }); // 清除存储 if (window.localStorage) localStorage.clear(); if (window.sessionStorage) sessionStorage.clear(); console.log('[WebView.evalJS] Cookie 和存储已清除'); } catch (e) { console.error('[WebView.evalJS] 清除失败:', e); } `) } } catch (e) { console.error('[clearWebViewCache] 处理 WebView 失败:', e) } }) console.log('[clearWebViewCache] 所有 WebView 缓存和 Cookie 已清除,共 ' + allWebviews.length + ' 个') logMessages.push(`所有 WebView 已清除 (${allWebviews.length}个)`) try { if (plus.storage) { plus.storage.clear() } } catch (e) { console.error('[clearWebViewCache] 清除 plus.storage 失败:', e) } // #endif // #ifndef APP-PLUS console.log('[clearWebViewCache] 非 APP-PLUS 环境,已清除 uni.storage') logMessages.push('非 APP-PLUS 环境') // #endif // 显示清除结果 uni.showToast({ title: '缓存已清除', icon: 'success', duration: 2000 }) // 复用进入直播间前的彻底清理逻辑(Android CookieManager/WebStorage、iOS NSHTTPCookieStorage/WKWebsiteDataStore) clearWebViewData() } catch (error) { console.error('[clearWebViewCache] 清除缓存失败:', error) logMessages.push('异常: ' + error.message) uni.showToast({ title: '缓存清除失败', icon: 'none', duration: 2000 }) } // 开发调试:显示详细日志 console.log('[clearWebViewCache] 执行完成,日志:', logMessages.join(' | ')) } /** traditionalApp/pages/webview/webview.vue
@@ -10,6 +10,7 @@ <script setup lang="ts"> import { ref } from 'vue' import { onLoad, onUnload } from '@dcloudio/uni-app' import { clearWebViewData } from '@/utils/webview-cache.js' const webViewUrl = ref('') let currentWebviewInstance: any = null @@ -44,139 +45,11 @@ }) /** * 页面卸载时清除 WebView 缓存和 Cookie * 页面卸载时不再清理缓存:清理集中在进入直播间前与退出登录时执行。 */ onUnload(() => { console.log('[WebView] 页面卸载,开始清除缓存') clearWebViewData() console.log('[WebView] 页面卸载') }) /** * 清除所有 WebView 的缓存、Cookie 和存储数据。 * 原生 API 可清掉 HttpOnly cookie 和按域名隔离的 localStorage; * evalJS 仅作为兜底,清掉非 HttpOnly cookie 和当前页 storage。 */ function clearWebViewData() { try { // #ifdef APP-PLUS // 1. 原生 API 清理(核心:能清掉 HttpOnly cookie 和所有域名的 localStorage) const platform = String(uni.getSystemInfoSync().platform || '').toLowerCase() if (platform === 'android') { clearAndroidWebViewData() } else if (platform === 'ios') { clearIOSWebViewData() } // 2. plus.webview API 兜底:清 HTTP 资源缓存,并注入 JS 清当前页 cookie/storage try { const allWebviews = plus.webview.all() if (allWebviews && allWebviews.length > 0) { allWebviews.forEach((webview) => { try { if (webview && typeof webview.clearCache === 'function') { webview.clearCache() } if (webview && typeof webview.evalJS === 'function') { webview.evalJS(` try { document.cookie.split(";").forEach(function(c) { document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); }); if (window.localStorage) localStorage.clear(); if (window.sessionStorage) sessionStorage.clear(); } catch (e) {} `) } } catch (e) {} }) console.log('[WebView] 所有 WebView HTTP 缓存已清除,共 ' + allWebviews.length + ' 个') } } catch (e) {} // #endif } catch (error) { console.error('[WebView] 清除数据失败:', error) } } /** * Android 原生清理:清所有域名 Cookie(含 HttpOnly)+ WebStorage + 表单数据。 */ function clearAndroidWebViewData() { try { // 清所有域名 Cookie(包括 HttpOnly),CookieManager 是全局按域名共享的 const CookieManager = plus.android.importClass('android.webkit.CookieManager') if (CookieManager) { const cm = CookieManager.getInstance() try { cm.removeAllCookie() } catch (e) {} // 老版本 API(API 21 以下) try { cm.flush() } catch (e) {} // 立即写入磁盘 console.log('[WebView][Android] CookieManager 已清所有 Cookie') } // 清所有域名的 localStorage / sessionStorage(WebStorage 是全局按域名共享的) const WebStorage = plus.android.importClass('android.webkit.WebStorage') if (WebStorage) { const ws = WebStorage.getInstance() try { ws.deleteAllData() } catch (e) {} console.log('[WebView][Android] WebStorage 已清所有 localStorage') } // 清表单数据和密码(可选) try { const WebViewDatabase = plus.android.importClass('android.webkit.WebViewDatabase') const context = plus.android.runtime.appContext if (WebViewDatabase && context) { const db = WebViewDatabase.getInstance(context) if (db && typeof db.clearFormData === 'function') { db.clearFormData() } } } catch (e) {} } catch (e) { console.error('[WebView][Android] 原生清理失败:', e) } } /** * iOS 原生清理:清 NSHTTPCookieStorage 所有 Cookie;并用 WKWebsiteDataStore 清 WKWebView 数据。 */ function clearIOSWebViewData() { // 1. NSHTTPCookieStorage.deleteAllCookies(UIWebView 与部分 WKWebView 共享) try { const NSHTTPCookieStorage = plus.ios.importClass('NSHTTPCookieStorage') if (NSHTTPCookieStorage) { const storage = NSHTTPCookieStorage.sharedHTTPCookieStorage() if (storage) { try { plus.ios.invoke(storage, 'deleteAllCookies') } catch (e) {} console.log('[WebView][iOS] NSHTTPCookieStorage 已清所有 Cookie') } } } catch (e) { console.error('[WebView][iOS] NSHTTPCookieStorage 清理失败:', e) } // 2. WKWebsiteDataStore.removeDataOfTypes(iOS 9+,清 WKWebView 的 cookie/localStorage/cache) // block 部分用空 block 代替,调用 plus.ios.invoke try { const WKWebsiteDataStore = plus.ios.importClass('WKWebsiteDataStore') if (!WKWebsiteDataStore) { return } const allTypes = WKWebsiteDataStore.allWebsiteDataTypes() const defaultStore = WKWebsiteDataStore.defaultDataStore() const NSDate = plus.ios.importClass('NSDate') const distantPast = NSDate ? NSDate.distantPast() : null // completionHandler 传一个空 block,避免 native 崩溃 const noopHandler = function () {} try { plus.ios.invoke(defaultStore, 'removeDataOfTypes:modifiedAt:completionHandler:', allTypes, distantPast, noopHandler) console.log('[WebView][iOS] WKWebsiteDataStore 已清所有数据') } catch (e) { console.error('[WebView][iOS] WKWebsiteDataStore 调用失败:', e) } } catch (e) { console.error('[WebView][iOS] WKWebsiteDataStore 初始化失败:', e) } } /** * 解码路由参数,异常时回退为空字符串。 traditionalApp/utils/webview-cache.js
New file @@ -0,0 +1,128 @@ /** * 清除所有 WebView 的缓存、Cookie 和存储数据。 * 原生 API 可清掉 HttpOnly cookie 和按域名隔离的 localStorage; * evalJS 仅作为兜底,清掉非 HttpOnly cookie 和当前页 storage。 * * 进入微赞直播间前、退出登录时统一调用此函数,保证清理逻辑一致。 */ export function clearWebViewData() { try { // #ifdef APP-PLUS // 1. 原生 API 清理(核心:能清掉 HttpOnly cookie 和所有域名的 localStorage) const platform = String(uni.getSystemInfoSync().platform || '').toLowerCase() if (platform === 'android') { clearAndroidWebViewData() } else if (platform === 'ios') { clearIOSWebViewData() } // 2. plus.webview API 兜底:清 HTTP 资源缓存,并注入 JS 清当前页 cookie/storage try { const allWebviews = plus.webview.all() if (allWebviews && allWebviews.length > 0) { allWebviews.forEach((webview) => { try { if (webview && typeof webview.clearCache === 'function') { webview.clearCache() } if (webview && typeof webview.evalJS === 'function') { webview.evalJS(` try { document.cookie.split(";").forEach(function(c) { document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); }); if (window.localStorage) localStorage.clear(); if (window.sessionStorage) sessionStorage.clear(); } catch (e) {} `) } } catch (e) {} }) console.log('[WebView] 所有 WebView HTTP 缓存已清除,共 ' + allWebviews.length + ' 个') } } catch (e) {} // #endif } catch (error) { console.error('[WebView] 清除数据失败:', error) } } /** * Android 原生清理:清所有域名 Cookie(含 HttpOnly)+ WebStorage + 表单数据。 */ function clearAndroidWebViewData() { try { // 清所有域名 Cookie(包括 HttpOnly),CookieManager 是全局按域名共享的 const CookieManager = plus.android.importClass('android.webkit.CookieManager') if (CookieManager) { const cm = CookieManager.getInstance() try { cm.removeAllCookie() } catch (e) {} // 老版本 API(API 21 以下) try { cm.flush() } catch (e) {} // 立即写入磁盘 console.log('[WebView][Android] CookieManager 已清所有 Cookie') } // 清所有域名的 localStorage / sessionStorage(WebStorage 是全局按域名共享的) const WebStorage = plus.android.importClass('android.webkit.WebStorage') if (WebStorage) { const ws = WebStorage.getInstance() try { ws.deleteAllData() } catch (e) {} console.log('[WebView][Android] WebStorage 已清所有 localStorage') } // 清表单数据和密码(可选) try { const WebViewDatabase = plus.android.importClass('android.webkit.WebViewDatabase') const context = plus.android.runtime.appContext if (WebViewDatabase && context) { const db = WebViewDatabase.getInstance(context) if (db && typeof db.clearFormData === 'function') { db.clearFormData() } } } catch (e) {} } catch (e) { console.error('[WebView][Android] 原生清理失败:', e) } } /** * iOS 原生清理:清 NSHTTPCookieStorage 所有 Cookie;并用 WKWebsiteDataStore 清 WKWebView 数据。 */ function clearIOSWebViewData() { // 1. NSHTTPCookieStorage.deleteAllCookies(UIWebView 与部分 WKWebView 共享) try { const NSHTTPCookieStorage = plus.ios.importClass('NSHTTPCookieStorage') if (NSHTTPCookieStorage) { const storage = NSHTTPCookieStorage.sharedHTTPCookieStorage() if (storage) { try { plus.ios.invoke(storage, 'deleteAllCookies') } catch (e) {} console.log('[WebView][iOS] NSHTTPCookieStorage 已清所有 Cookie') } } } catch (e) { console.error('[WebView][iOS] NSHTTPCookieStorage 清理失败:', e) } // 2. WKWebsiteDataStore.removeDataOfTypes(iOS 9+,清 WKWebView 的 cookie/localStorage/cache) // block 部分用空 block 代替,调用 plus.ios.invoke try { const WKWebsiteDataStore = plus.ios.importClass('WKWebsiteDataStore') if (!WKWebsiteDataStore) { return } const allTypes = WKWebsiteDataStore.allWebsiteDataTypes() const defaultStore = WKWebsiteDataStore.defaultDataStore() const NSDate = plus.ios.importClass('NSDate') const distantPast = NSDate ? NSDate.distantPast() : null // completionHandler 传一个空 block,避免 native 崩溃 const noopHandler = function () {} try { plus.ios.invoke(defaultStore, 'removeDataOfTypes:modifiedAt:completionHandler:', allTypes, distantPast, noopHandler) console.log('[WebView][iOS] WKWebsiteDataStore 已清所有数据') } catch (e) { console.error('[WebView][iOS] WKWebsiteDataStore 调用失败:', e) } } catch (e) { console.error('[WebView][iOS] WKWebsiteDataStore 初始化失败:', e) } }