新增用戶信息統計功能,更新主頁面以顯示用戶相關數據,並調整狀態管理邏輯以提升性能。
This commit is contained in:
352
Cunkebao/src/android-polyfills.ts
Normal file
352
Cunkebao/src/android-polyfills.ts
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
// Android 专用 polyfill - 解决Android 7等低版本系统的兼容性问题
|
||||||
|
|
||||||
|
// 检测是否为Android设备
|
||||||
|
const isAndroid = () => {
|
||||||
|
return /Android/i.test(navigator.userAgent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检测Android版本
|
||||||
|
const getAndroidVersion = () => {
|
||||||
|
const match = navigator.userAgent.match(/Android\s+(\d+)/);
|
||||||
|
return match ? parseInt(match[1]) : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检测是否为低版本Android
|
||||||
|
const isLowVersionAndroid = () => {
|
||||||
|
const version = getAndroidVersion();
|
||||||
|
return version <= 7; // Android 7及以下版本
|
||||||
|
};
|
||||||
|
|
||||||
|
// 只在Android设备上执行polyfill
|
||||||
|
if (isAndroid() && isLowVersionAndroid()) {
|
||||||
|
console.log("检测到低版本Android系统,启用兼容性polyfill");
|
||||||
|
|
||||||
|
// 修复Array.prototype.includes在Android WebView中的问题
|
||||||
|
if (!Array.prototype.includes) {
|
||||||
|
Array.prototype.includes = function (searchElement, fromIndex) {
|
||||||
|
if (this == null) {
|
||||||
|
throw new TypeError('"this" is null or not defined');
|
||||||
|
}
|
||||||
|
var o = Object(this);
|
||||||
|
var len = o.length >>> 0;
|
||||||
|
if (len === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var n = fromIndex | 0;
|
||||||
|
var k = Math.max(n >= 0 ? n : len + n, 0);
|
||||||
|
while (k < len) {
|
||||||
|
if (o[k] === searchElement) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复String.prototype.includes在Android WebView中的问题
|
||||||
|
if (!String.prototype.includes) {
|
||||||
|
String.prototype.includes = function (search, start) {
|
||||||
|
if (typeof start !== "number") {
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
if (start + search.length > this.length) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return this.indexOf(search, start) !== -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复String.prototype.startsWith在Android WebView中的问题
|
||||||
|
if (!String.prototype.startsWith) {
|
||||||
|
String.prototype.startsWith = function (searchString, position) {
|
||||||
|
position = position || 0;
|
||||||
|
return this.substr(position, searchString.length) === searchString;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复String.prototype.endsWith在Android WebView中的问题
|
||||||
|
if (!String.prototype.endsWith) {
|
||||||
|
String.prototype.endsWith = function (searchString, length) {
|
||||||
|
if (length === undefined || length > this.length) {
|
||||||
|
length = this.length;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
this.substring(length - searchString.length, length) === searchString
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复Array.prototype.find在Android WebView中的问题
|
||||||
|
if (!Array.prototype.find) {
|
||||||
|
Array.prototype.find = function (predicate) {
|
||||||
|
if (this == null) {
|
||||||
|
throw new TypeError("Array.prototype.find called on null or undefined");
|
||||||
|
}
|
||||||
|
if (typeof predicate !== "function") {
|
||||||
|
throw new TypeError("predicate must be a function");
|
||||||
|
}
|
||||||
|
var list = Object(this);
|
||||||
|
var length = parseInt(list.length) || 0;
|
||||||
|
var thisArg = arguments[1];
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var element = list[i];
|
||||||
|
if (predicate.call(thisArg, element, i, list)) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复Array.prototype.findIndex在Android WebView中的问题
|
||||||
|
if (!Array.prototype.findIndex) {
|
||||||
|
Array.prototype.findIndex = function (predicate) {
|
||||||
|
if (this == null) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Array.prototype.findIndex called on null or undefined",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (typeof predicate !== "function") {
|
||||||
|
throw new TypeError("predicate must be a function");
|
||||||
|
}
|
||||||
|
var list = Object(this);
|
||||||
|
var length = parseInt(list.length) || 0;
|
||||||
|
var thisArg = arguments[1];
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var element = list[i];
|
||||||
|
if (predicate.call(thisArg, element, i, list)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复Object.assign在Android WebView中的问题
|
||||||
|
if (typeof Object.assign !== "function") {
|
||||||
|
Object.assign = function (target) {
|
||||||
|
if (target == null) {
|
||||||
|
throw new TypeError("Cannot convert undefined or null to object");
|
||||||
|
}
|
||||||
|
var to = Object(target);
|
||||||
|
for (var index = 1; index < arguments.length; index++) {
|
||||||
|
var nextSource = arguments[index];
|
||||||
|
if (nextSource != null) {
|
||||||
|
for (var nextKey in nextSource) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||||
|
to[nextKey] = nextSource[nextKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复Array.from在Android WebView中的问题
|
||||||
|
if (!Array.from) {
|
||||||
|
Array.from = (function () {
|
||||||
|
var toStr = Object.prototype.toString;
|
||||||
|
var isCallable = function (fn) {
|
||||||
|
return (
|
||||||
|
typeof fn === "function" || toStr.call(fn) === "[object Function]"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
var toInteger = function (value) {
|
||||||
|
var number = Number(value);
|
||||||
|
if (isNaN(number)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (number === 0 || !isFinite(number)) {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||||
|
};
|
||||||
|
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||||
|
var toLength = function (value) {
|
||||||
|
var len = toInteger(value);
|
||||||
|
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||||
|
};
|
||||||
|
return function from(arrayLike) {
|
||||||
|
var C = this;
|
||||||
|
var items = Object(arrayLike);
|
||||||
|
if (arrayLike == null) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Array.from requires an array-like object - not null or undefined",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var mapFunction = arguments.length > 1 ? arguments[1] : void undefined;
|
||||||
|
var T;
|
||||||
|
if (typeof mapFunction !== "undefined") {
|
||||||
|
if (typeof mapFunction !== "function") {
|
||||||
|
throw new TypeError(
|
||||||
|
"Array.from: when provided, the second argument must be a function",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (arguments.length > 2) {
|
||||||
|
T = arguments[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var len = toLength(items.length);
|
||||||
|
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||||
|
var k = 0;
|
||||||
|
var kValue;
|
||||||
|
while (k < len) {
|
||||||
|
kValue = items[k];
|
||||||
|
if (mapFunction) {
|
||||||
|
A[k] =
|
||||||
|
typeof T === "undefined"
|
||||||
|
? mapFunction(kValue, k)
|
||||||
|
: mapFunction.call(T, kValue, k);
|
||||||
|
} else {
|
||||||
|
A[k] = kValue;
|
||||||
|
}
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
A.length = len;
|
||||||
|
return A;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复requestAnimationFrame在Android WebView中的问题
|
||||||
|
if (!window.requestAnimationFrame) {
|
||||||
|
window.requestAnimationFrame = function (callback) {
|
||||||
|
return setTimeout(function () {
|
||||||
|
callback(Date.now());
|
||||||
|
}, 1000 / 60);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.cancelAnimationFrame) {
|
||||||
|
window.cancelAnimationFrame = function (id) {
|
||||||
|
clearTimeout(id);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复IntersectionObserver在Android WebView中的问题
|
||||||
|
if (!window.IntersectionObserver) {
|
||||||
|
window.IntersectionObserver = function (callback, options) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.options = options || {};
|
||||||
|
this.observers = [];
|
||||||
|
|
||||||
|
this.observe = function (element) {
|
||||||
|
this.observers.push(element);
|
||||||
|
// 简单的实现,实际项目中可能需要更复杂的逻辑
|
||||||
|
setTimeout(() => {
|
||||||
|
this.callback([
|
||||||
|
{
|
||||||
|
target: element,
|
||||||
|
isIntersecting: true,
|
||||||
|
intersectionRatio: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unobserve = function (element) {
|
||||||
|
var index = this.observers.indexOf(element);
|
||||||
|
if (index > -1) {
|
||||||
|
this.observers.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.disconnect = function () {
|
||||||
|
this.observers = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复ResizeObserver在Android WebView中的问题
|
||||||
|
if (!window.ResizeObserver) {
|
||||||
|
window.ResizeObserver = function (callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.observers = [];
|
||||||
|
|
||||||
|
this.observe = function (element) {
|
||||||
|
this.observers.push(element);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unobserve = function (element) {
|
||||||
|
var index = this.observers.indexOf(element);
|
||||||
|
if (index > -1) {
|
||||||
|
this.observers.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.disconnect = function () {
|
||||||
|
this.observers = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复URLSearchParams在Android WebView中的问题
|
||||||
|
if (!window.URLSearchParams) {
|
||||||
|
window.URLSearchParams = function (init) {
|
||||||
|
this.params = {};
|
||||||
|
|
||||||
|
if (init) {
|
||||||
|
if (typeof init === "string") {
|
||||||
|
if (init.charAt(0) === "?") {
|
||||||
|
init = init.slice(1);
|
||||||
|
}
|
||||||
|
var pairs = init.split("&");
|
||||||
|
for (var i = 0; i < pairs.length; i++) {
|
||||||
|
var pair = pairs[i].split("=");
|
||||||
|
var key = decodeURIComponent(pair[0]);
|
||||||
|
var value = decodeURIComponent(pair[1] || "");
|
||||||
|
this.append(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.append = function (name, value) {
|
||||||
|
if (!this.params[name]) {
|
||||||
|
this.params[name] = [];
|
||||||
|
}
|
||||||
|
this.params[name].push(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.get = function (name) {
|
||||||
|
return this.params[name] ? this.params[name][0] : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getAll = function (name) {
|
||||||
|
return this.params[name] || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.has = function (name) {
|
||||||
|
return !!this.params[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.set = function (name, value) {
|
||||||
|
this.params[name] = [value];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.delete = function (name) {
|
||||||
|
delete this.params[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toString = function () {
|
||||||
|
var pairs = [];
|
||||||
|
for (var key in this.params) {
|
||||||
|
if (this.params.hasOwnProperty(key)) {
|
||||||
|
for (var i = 0; i < this.params[key].length; i++) {
|
||||||
|
pairs.push(
|
||||||
|
encodeURIComponent(key) +
|
||||||
|
"=" +
|
||||||
|
encodeURIComponent(this.params[key][i]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pairs.join("&");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Android兼容性polyfill已加载完成");
|
||||||
|
}
|
||||||
228
Cunkebao/src/components/AndroidCompatibilityCheck.tsx
Normal file
228
Cunkebao/src/components/AndroidCompatibilityCheck.tsx
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface AndroidCompatibilityInfo {
|
||||||
|
isAndroid: boolean;
|
||||||
|
androidVersion: number;
|
||||||
|
chromeVersion: number;
|
||||||
|
webViewVersion: number;
|
||||||
|
issues: string[];
|
||||||
|
suggestions: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const AndroidCompatibilityCheck: React.FC = () => {
|
||||||
|
const [compatibility, setCompatibility] = useState<AndroidCompatibilityInfo>({
|
||||||
|
isAndroid: false,
|
||||||
|
androidVersion: 0,
|
||||||
|
chromeVersion: 0,
|
||||||
|
webViewVersion: 0,
|
||||||
|
issues: [],
|
||||||
|
suggestions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkAndroidCompatibility = () => {
|
||||||
|
const ua = navigator.userAgent;
|
||||||
|
const issues: string[] = [];
|
||||||
|
const suggestions: string[] = [];
|
||||||
|
let isAndroid = false;
|
||||||
|
let androidVersion = 0;
|
||||||
|
let chromeVersion = 0;
|
||||||
|
let webViewVersion = 0;
|
||||||
|
|
||||||
|
// 检测Android系统
|
||||||
|
if (ua.indexOf("Android") > -1) {
|
||||||
|
isAndroid = true;
|
||||||
|
const androidMatch = ua.match(/Android\s+(\d+)/);
|
||||||
|
if (androidMatch) {
|
||||||
|
androidVersion = parseInt(androidMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测Chrome版本
|
||||||
|
const chromeMatch = ua.match(/Chrome\/(\d+)/);
|
||||||
|
if (chromeMatch) {
|
||||||
|
chromeVersion = parseInt(chromeMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测WebView版本
|
||||||
|
const webViewMatch = ua.match(/Version\/\d+\.\d+/);
|
||||||
|
if (webViewMatch) {
|
||||||
|
const versionMatch = webViewMatch[0].match(/\d+/);
|
||||||
|
if (versionMatch) {
|
||||||
|
webViewVersion = parseInt(versionMatch[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android 7 (API 24) 兼容性检查
|
||||||
|
if (androidVersion === 7) {
|
||||||
|
issues.push("Android 7 系统对ES6+特性支持不完整");
|
||||||
|
suggestions.push("建议升级到Android 8+或使用最新版Chrome");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android 6 (API 23) 兼容性检查
|
||||||
|
if (androidVersion === 6) {
|
||||||
|
issues.push("Android 6 系统对现代Web特性支持有限");
|
||||||
|
suggestions.push("强烈建议升级系统或使用最新版Chrome");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chrome版本检查
|
||||||
|
if (chromeVersion > 0 && chromeVersion < 50) {
|
||||||
|
issues.push(`Chrome版本过低 (${chromeVersion}),建议升级到50+`);
|
||||||
|
suggestions.push("请在Google Play商店更新Chrome浏览器");
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebView版本检查
|
||||||
|
if (webViewVersion > 0 && webViewVersion < 50) {
|
||||||
|
issues.push(`WebView版本过低 (${webViewVersion}),可能影响应用功能`);
|
||||||
|
suggestions.push("建议使用Chrome浏览器或更新系统WebView");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测特定问题
|
||||||
|
const features = {
|
||||||
|
Promise: typeof Promise !== "undefined",
|
||||||
|
fetch: typeof fetch !== "undefined",
|
||||||
|
"Array.from": typeof Array.from !== "undefined",
|
||||||
|
"Object.assign": typeof Object.assign !== "undefined",
|
||||||
|
"String.includes": typeof String.prototype.includes !== "undefined",
|
||||||
|
"Array.includes": typeof Array.prototype.includes !== "undefined",
|
||||||
|
requestAnimationFrame: typeof requestAnimationFrame !== "undefined",
|
||||||
|
IntersectionObserver: typeof IntersectionObserver !== "undefined",
|
||||||
|
ResizeObserver: typeof ResizeObserver !== "undefined",
|
||||||
|
URLSearchParams: typeof URLSearchParams !== "undefined",
|
||||||
|
TextEncoder: typeof TextEncoder !== "undefined",
|
||||||
|
AbortController: typeof AbortController !== "undefined",
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(features).forEach(([feature, supported]) => {
|
||||||
|
if (!supported) {
|
||||||
|
issues.push(`${feature} 特性不支持`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 微信内置浏览器检测
|
||||||
|
if (ua.indexOf("MicroMessenger") > -1) {
|
||||||
|
issues.push("微信内置浏览器对某些Web特性支持有限");
|
||||||
|
suggestions.push("建议在系统浏览器中打开以获得最佳体验");
|
||||||
|
}
|
||||||
|
|
||||||
|
// QQ内置浏览器检测
|
||||||
|
if (ua.indexOf("QQ/") > -1) {
|
||||||
|
issues.push("QQ内置浏览器对某些Web特性支持有限");
|
||||||
|
suggestions.push("建议在系统浏览器中打开以获得最佳体验");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCompatibility({
|
||||||
|
isAndroid,
|
||||||
|
androidVersion,
|
||||||
|
chromeVersion,
|
||||||
|
webViewVersion,
|
||||||
|
issues,
|
||||||
|
suggestions,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAndroidCompatibility();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!compatibility.isAndroid || compatibility.issues.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: "#fff3cd",
|
||||||
|
border: "1px solid #ffeaa7",
|
||||||
|
padding: "15px",
|
||||||
|
zIndex: 9999,
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "14px",
|
||||||
|
maxHeight: "50vh",
|
||||||
|
overflowY: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{ fontWeight: "bold", marginBottom: "10px", color: "#856404" }}
|
||||||
|
>
|
||||||
|
🚨 Android 兼容性警告
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: "8px", fontSize: "12px" }}>
|
||||||
|
系统版本: Android {compatibility.androidVersion}
|
||||||
|
{compatibility.chromeVersion > 0 &&
|
||||||
|
` | Chrome: ${compatibility.chromeVersion}`}
|
||||||
|
{compatibility.webViewVersion > 0 &&
|
||||||
|
` | WebView: ${compatibility.webViewVersion}`}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: "10px" }}>
|
||||||
|
<div
|
||||||
|
style={{ fontWeight: "bold", marginBottom: "5px", color: "#856404" }}
|
||||||
|
>
|
||||||
|
检测到的问题:
|
||||||
|
</div>
|
||||||
|
<div style={{ color: "#856404", fontSize: "12px" }}>
|
||||||
|
{compatibility.issues.map((issue, index) => (
|
||||||
|
<div key={index} style={{ marginBottom: "3px" }}>
|
||||||
|
• {issue}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{compatibility.suggestions.length > 0 && (
|
||||||
|
<div style={{ marginBottom: "10px" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
marginBottom: "5px",
|
||||||
|
color: "#155724",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
建议解决方案:
|
||||||
|
</div>
|
||||||
|
<div style={{ color: "#155724", fontSize: "12px" }}>
|
||||||
|
{compatibility.suggestions.map((suggestion, index) => (
|
||||||
|
<div key={index} style={{ marginBottom: "3px" }}>
|
||||||
|
• {suggestion}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={{ fontSize: "11px", color: "#6c757d", marginTop: "10px" }}>
|
||||||
|
💡 应用已启用兼容模式,但建议升级系统以获得最佳体验
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const element = document.querySelector(
|
||||||
|
'[style*="position: fixed"][style*="top: 0"]',
|
||||||
|
) as HTMLElement;
|
||||||
|
if (element) {
|
||||||
|
element.style.display = "none";
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "5px",
|
||||||
|
right: "10px",
|
||||||
|
background: "none",
|
||||||
|
border: "none",
|
||||||
|
fontSize: "18px",
|
||||||
|
cursor: "pointer",
|
||||||
|
color: "#856404",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AndroidCompatibilityCheck;
|
||||||
125
Cunkebao/src/components/CompatibilityCheck.tsx
Normal file
125
Cunkebao/src/components/CompatibilityCheck.tsx
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface CompatibilityInfo {
|
||||||
|
isCompatible: boolean;
|
||||||
|
browser: string;
|
||||||
|
version: string;
|
||||||
|
issues: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompatibilityCheck: React.FC = () => {
|
||||||
|
const [compatibility, setCompatibility] = useState<CompatibilityInfo>({
|
||||||
|
isCompatible: true,
|
||||||
|
browser: "",
|
||||||
|
version: "",
|
||||||
|
issues: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkCompatibility = () => {
|
||||||
|
const ua = navigator.userAgent;
|
||||||
|
const issues: string[] = [];
|
||||||
|
let browser = "Unknown";
|
||||||
|
let version = "Unknown";
|
||||||
|
|
||||||
|
// 检测浏览器类型和版本
|
||||||
|
if (ua.indexOf("Chrome") > -1) {
|
||||||
|
browser = "Chrome";
|
||||||
|
const match = ua.match(/Chrome\/(\d+)/);
|
||||||
|
version = match ? match[1] : "Unknown";
|
||||||
|
if (parseInt(version) < 50) {
|
||||||
|
issues.push("Chrome版本过低,建议升级到50+");
|
||||||
|
}
|
||||||
|
} else if (ua.indexOf("Firefox") > -1) {
|
||||||
|
browser = "Firefox";
|
||||||
|
const match = ua.match(/Firefox\/(\d+)/);
|
||||||
|
version = match ? match[1] : "Unknown";
|
||||||
|
if (parseInt(version) < 50) {
|
||||||
|
issues.push("Firefox版本过低,建议升级到50+");
|
||||||
|
}
|
||||||
|
} else if (ua.indexOf("Safari") > -1 && ua.indexOf("Chrome") === -1) {
|
||||||
|
browser = "Safari";
|
||||||
|
const match = ua.match(/Version\/(\d+)/);
|
||||||
|
version = match ? match[1] : "Unknown";
|
||||||
|
if (parseInt(version) < 10) {
|
||||||
|
issues.push("Safari版本过低,建议升级到10+");
|
||||||
|
}
|
||||||
|
} else if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) {
|
||||||
|
browser = "Internet Explorer";
|
||||||
|
const match = ua.match(/(?:MSIE |rv:)(\d+)/);
|
||||||
|
version = match ? match[1] : "Unknown";
|
||||||
|
issues.push("Internet Explorer不受支持,建议使用现代浏览器");
|
||||||
|
} else if (ua.indexOf("Edge") > -1) {
|
||||||
|
browser = "Edge";
|
||||||
|
const match = ua.match(/Edge\/(\d+)/);
|
||||||
|
version = match ? match[1] : "Unknown";
|
||||||
|
if (parseInt(version) < 12) {
|
||||||
|
issues.push("Edge版本过低,建议升级到12+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测ES6+特性支持
|
||||||
|
const features = {
|
||||||
|
Promise: typeof Promise !== "undefined",
|
||||||
|
fetch: typeof fetch !== "undefined",
|
||||||
|
"Array.from": typeof Array.from !== "undefined",
|
||||||
|
"Object.assign": typeof Object.assign !== "undefined",
|
||||||
|
"String.includes": typeof String.prototype.includes !== "undefined",
|
||||||
|
"Array.includes": typeof Array.prototype.includes !== "undefined",
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(features).forEach(([feature, supported]) => {
|
||||||
|
if (!supported) {
|
||||||
|
issues.push(`${feature} 特性不支持`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setCompatibility({
|
||||||
|
isCompatible: issues.length === 0,
|
||||||
|
browser,
|
||||||
|
version,
|
||||||
|
issues,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkCompatibility();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (compatibility.isCompatible) {
|
||||||
|
return null; // 兼容时不需要显示
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: "#fff3cd",
|
||||||
|
border: "1px solid #ffeaa7",
|
||||||
|
padding: "10px",
|
||||||
|
zIndex: 9999,
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ fontWeight: "bold", marginBottom: "5px" }}>
|
||||||
|
浏览器兼容性警告
|
||||||
|
</div>
|
||||||
|
<div style={{ marginBottom: "5px" }}>
|
||||||
|
当前浏览器: {compatibility.browser} {compatibility.version}
|
||||||
|
</div>
|
||||||
|
<div style={{ color: "#856404" }}>
|
||||||
|
{compatibility.issues.map((issue, index) => (
|
||||||
|
<div key={index}>{issue}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: "10px", fontSize: "12px" }}>
|
||||||
|
建议使用 Chrome 50+、Firefox 50+、Safari 10+ 或 Edge 12+
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CompatibilityCheck;
|
||||||
176
Cunkebao/src/polyfills.ts
Normal file
176
Cunkebao/src/polyfills.ts
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
// ES5兼容性polyfill - 确保在低版本浏览器中正常运行
|
||||||
|
// 特别针对Android 7等低版本内核优化
|
||||||
|
|
||||||
|
// 基础polyfill
|
||||||
|
import "core-js/stable";
|
||||||
|
import "regenerator-runtime/runtime";
|
||||||
|
|
||||||
|
// Promise支持
|
||||||
|
import "core-js/features/promise";
|
||||||
|
|
||||||
|
// Array方法支持
|
||||||
|
import "core-js/features/array/from";
|
||||||
|
import "core-js/features/array/find";
|
||||||
|
import "core-js/features/array/includes";
|
||||||
|
import "core-js/features/array/find-index";
|
||||||
|
import "core-js/features/array/fill";
|
||||||
|
import "core-js/features/array/copy-within";
|
||||||
|
|
||||||
|
// Object方法支持
|
||||||
|
import "core-js/features/object/assign";
|
||||||
|
import "core-js/features/object/entries";
|
||||||
|
import "core-js/features/object/values";
|
||||||
|
import "core-js/features/object/keys";
|
||||||
|
|
||||||
|
// String方法支持
|
||||||
|
import "core-js/features/string/includes";
|
||||||
|
import "core-js/features/string/starts-with";
|
||||||
|
import "core-js/features/string/ends-with";
|
||||||
|
import "core-js/features/string/pad-start";
|
||||||
|
import "core-js/features/string/pad-end";
|
||||||
|
import "core-js/features/string/trim-start";
|
||||||
|
import "core-js/features/string/trim-end";
|
||||||
|
import "core-js/features/string/repeat";
|
||||||
|
|
||||||
|
// Number方法支持
|
||||||
|
import "core-js/features/number/is-finite";
|
||||||
|
import "core-js/features/number/is-integer";
|
||||||
|
import "core-js/features/number/is-nan";
|
||||||
|
import "core-js/features/number/is-safe-integer";
|
||||||
|
|
||||||
|
// Math方法支持
|
||||||
|
import "core-js/features/math/sign";
|
||||||
|
import "core-js/features/math/trunc";
|
||||||
|
import "core-js/features/math/cbrt";
|
||||||
|
import "core-js/features/math/clz32";
|
||||||
|
import "core-js/features/math/imul";
|
||||||
|
import "core-js/features/math/fround";
|
||||||
|
import "core-js/features/math/hypot";
|
||||||
|
|
||||||
|
// Map和Set支持
|
||||||
|
import "core-js/features/map";
|
||||||
|
import "core-js/features/set";
|
||||||
|
import "core-js/features/weak-map";
|
||||||
|
import "core-js/features/weak-set";
|
||||||
|
|
||||||
|
// Symbol支持
|
||||||
|
import "core-js/features/symbol";
|
||||||
|
import "core-js/features/symbol/for";
|
||||||
|
import "core-js/features/symbol/key-for";
|
||||||
|
|
||||||
|
// 正则表达式支持
|
||||||
|
import "core-js/features/regexp/flags";
|
||||||
|
import "core-js/features/regexp/sticky";
|
||||||
|
|
||||||
|
// 函数支持
|
||||||
|
import "core-js/features/function/name";
|
||||||
|
import "core-js/features/function/has-instance";
|
||||||
|
|
||||||
|
// 全局对象支持
|
||||||
|
import "core-js/features/global-this";
|
||||||
|
|
||||||
|
// 确保全局对象可用
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
// 确保Promise在全局可用
|
||||||
|
if (!window.Promise) {
|
||||||
|
window.Promise = require("core-js/features/promise");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保fetch在全局可用
|
||||||
|
if (!window.fetch) {
|
||||||
|
window.fetch = require("whatwg-fetch");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保requestAnimationFrame在全局可用
|
||||||
|
if (!window.requestAnimationFrame) {
|
||||||
|
window.requestAnimationFrame = function (callback) {
|
||||||
|
return setTimeout(callback, 1000 / 60);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.cancelAnimationFrame) {
|
||||||
|
window.cancelAnimationFrame = function (id) {
|
||||||
|
clearTimeout(id);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保IntersectionObserver在全局可用
|
||||||
|
if (!window.IntersectionObserver) {
|
||||||
|
window.IntersectionObserver = function (callback, options) {
|
||||||
|
return {
|
||||||
|
observe: function () {},
|
||||||
|
unobserve: function () {},
|
||||||
|
disconnect: function () {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保ResizeObserver在全局可用
|
||||||
|
if (!window.ResizeObserver) {
|
||||||
|
window.ResizeObserver = function (callback) {
|
||||||
|
return {
|
||||||
|
observe: function () {},
|
||||||
|
unobserve: function () {},
|
||||||
|
disconnect: function () {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保MutationObserver在全局可用
|
||||||
|
if (!window.MutationObserver) {
|
||||||
|
window.MutationObserver = function (callback) {
|
||||||
|
return {
|
||||||
|
observe: function () {},
|
||||||
|
disconnect: function () {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保Performance API在全局可用
|
||||||
|
if (!window.performance) {
|
||||||
|
window.performance = {
|
||||||
|
now: function () {
|
||||||
|
return Date.now();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保URLSearchParams在全局可用
|
||||||
|
if (!window.URLSearchParams) {
|
||||||
|
window.URLSearchParams = require("core-js/features/url-search-params");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保URL在全局可用
|
||||||
|
if (!window.URL) {
|
||||||
|
window.URL = require("core-js/features/url");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保AbortController在全局可用
|
||||||
|
if (!window.AbortController) {
|
||||||
|
window.AbortController = function () {
|
||||||
|
return {
|
||||||
|
signal: {
|
||||||
|
aborted: false,
|
||||||
|
addEventListener: function () {},
|
||||||
|
removeEventListener: function () {},
|
||||||
|
},
|
||||||
|
abort: function () {
|
||||||
|
this.signal.aborted = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保AbortSignal在全局可用
|
||||||
|
if (!window.AbortSignal) {
|
||||||
|
window.AbortSignal = {
|
||||||
|
abort: function () {
|
||||||
|
return {
|
||||||
|
aborted: true,
|
||||||
|
addEventListener: function () {},
|
||||||
|
removeEventListener: function () {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
177
Cunkebao/兼容性说明.md
Normal file
177
Cunkebao/兼容性说明.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# 存客宝项目 - 浏览器兼容性说明
|
||||||
|
|
||||||
|
## 🎯 **兼容性目标**
|
||||||
|
|
||||||
|
本项目已配置为支持以下浏览器版本:
|
||||||
|
|
||||||
|
- **Chrome**: 50+
|
||||||
|
- **Firefox**: 50+
|
||||||
|
- **Safari**: 10+
|
||||||
|
- **Edge**: 12+
|
||||||
|
- **Internet Explorer**: 11+ (部分功能受限)
|
||||||
|
- **Android**: 4.4+ (特别优化Android 7)
|
||||||
|
- **iOS**: 9+
|
||||||
|
|
||||||
|
## 🔧 **兼容性配置**
|
||||||
|
|
||||||
|
### 1. **Polyfill 支持**
|
||||||
|
|
||||||
|
项目已集成以下 polyfill 来确保低版本浏览器兼容性:
|
||||||
|
|
||||||
|
- **core-js**: ES6+ 特性支持
|
||||||
|
- **regenerator-runtime**: async/await 支持
|
||||||
|
- **whatwg-fetch**: fetch API 支持
|
||||||
|
- **Android专用polyfill**: 针对Android 7等低版本系统优化
|
||||||
|
|
||||||
|
### 2. **构建配置**
|
||||||
|
|
||||||
|
- 使用 **terser** 进行代码压缩
|
||||||
|
- 配置了 **browserslist** 目标浏览器
|
||||||
|
- 添加了兼容性检测组件
|
||||||
|
- 特别针对Android设备优化
|
||||||
|
|
||||||
|
### 3. **特性支持**
|
||||||
|
|
||||||
|
项目通过 polyfill 支持以下 ES6+ 特性:
|
||||||
|
|
||||||
|
- ✅ Promise
|
||||||
|
- ✅ fetch API
|
||||||
|
- ✅ Array.from, Array.find, Array.includes, Array.findIndex
|
||||||
|
- ✅ Object.assign, Object.entries, Object.values, Object.keys
|
||||||
|
- ✅ String.includes, String.startsWith, String.endsWith
|
||||||
|
- ✅ Map, Set, WeakMap, WeakSet
|
||||||
|
- ✅ Symbol
|
||||||
|
- ✅ requestAnimationFrame
|
||||||
|
- ✅ IntersectionObserver
|
||||||
|
- ✅ ResizeObserver
|
||||||
|
- ✅ URLSearchParams
|
||||||
|
- ✅ AbortController
|
||||||
|
|
||||||
|
## 🚀 **使用方法**
|
||||||
|
|
||||||
|
### 开发环境
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 生产构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 预览构建结果
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm preview
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📱 **Android 特别优化**
|
||||||
|
|
||||||
|
### **Android 7 兼容性**
|
||||||
|
|
||||||
|
Android 7 (API 24) 系统对ES6+特性支持不完整,项目已特别优化:
|
||||||
|
|
||||||
|
#### **问题解决:**
|
||||||
|
|
||||||
|
- ✅ Array.prototype.includes 方法缺失
|
||||||
|
- ✅ String.prototype.includes 方法缺失
|
||||||
|
- ✅ Object.assign 方法缺失
|
||||||
|
- ✅ Array.from 方法缺失
|
||||||
|
- ✅ requestAnimationFrame 缺失
|
||||||
|
- ✅ IntersectionObserver 缺失
|
||||||
|
- ✅ URLSearchParams 缺失
|
||||||
|
|
||||||
|
#### **解决方案:**
|
||||||
|
|
||||||
|
- 使用自定义polyfill补充缺失方法
|
||||||
|
- 提供降级实现确保功能可用
|
||||||
|
- 自动检测Android版本并启用相应polyfill
|
||||||
|
|
||||||
|
### **Android WebView 优化**
|
||||||
|
|
||||||
|
- 针对系统WebView进行特别优化
|
||||||
|
- 支持微信、QQ等内置浏览器
|
||||||
|
- 提供降级方案确保基本功能可用
|
||||||
|
|
||||||
|
## ⚠️ **注意事项**
|
||||||
|
|
||||||
|
1. **Android 7 支持**
|
||||||
|
- 已启用兼容模式,基本功能可用
|
||||||
|
- 建议升级到Android 8+或使用最新版Chrome
|
||||||
|
- 部分高级特性可能受限
|
||||||
|
|
||||||
|
2. **Android 6 及以下**
|
||||||
|
- 支持有限,建议升级系统
|
||||||
|
- 使用最新版Chrome浏览器
|
||||||
|
- 部分功能可能不可用
|
||||||
|
|
||||||
|
3. **移动端兼容性**
|
||||||
|
- iOS Safari 10+
|
||||||
|
- Android Chrome 50+
|
||||||
|
- 微信内置浏览器 (部分功能受限)
|
||||||
|
- QQ内置浏览器 (部分功能受限)
|
||||||
|
|
||||||
|
4. **性能考虑**
|
||||||
|
- polyfill 会增加包体积
|
||||||
|
- 现代浏览器会自动忽略不需要的 polyfill
|
||||||
|
- Android设备上会有额外的兼容性检测
|
||||||
|
|
||||||
|
## 🔍 **兼容性检测**
|
||||||
|
|
||||||
|
项目包含自动兼容性检测功能:
|
||||||
|
|
||||||
|
### **通用检测**
|
||||||
|
|
||||||
|
- 在低版本浏览器中会显示警告提示
|
||||||
|
- 控制台会输出兼容性信息
|
||||||
|
- 建议用户升级浏览器
|
||||||
|
|
||||||
|
### **Android专用检测**
|
||||||
|
|
||||||
|
- 自动检测Android系统版本
|
||||||
|
- 检测Chrome和WebView版本
|
||||||
|
- 识别微信、QQ等内置浏览器
|
||||||
|
- 提供针对性的解决方案建议
|
||||||
|
|
||||||
|
## 📝 **更新日志**
|
||||||
|
|
||||||
|
### v3.0.0
|
||||||
|
|
||||||
|
- ✅ 添加 ES5 兼容性支持
|
||||||
|
- ✅ 集成 core-js polyfill
|
||||||
|
- ✅ 添加兼容性检测组件
|
||||||
|
- ✅ 优化构建配置
|
||||||
|
- ✅ **新增Android 7专用polyfill**
|
||||||
|
- ✅ **新增Android兼容性检测**
|
||||||
|
- ✅ **优化移动端体验**
|
||||||
|
|
||||||
|
## 🛠️ **故障排除**
|
||||||
|
|
||||||
|
如果遇到兼容性问题:
|
||||||
|
|
||||||
|
1. **Android设备问题**
|
||||||
|
- 检查Android系统版本
|
||||||
|
- 确认Chrome浏览器版本
|
||||||
|
- 查看控制台错误信息
|
||||||
|
- 尝试使用系统浏览器而非内置浏览器
|
||||||
|
|
||||||
|
2. **通用问题**
|
||||||
|
- 检查浏览器版本是否在支持范围内
|
||||||
|
- 查看控制台是否有错误信息
|
||||||
|
- 确认 polyfill 是否正确加载
|
||||||
|
- 尝试清除浏览器缓存
|
||||||
|
|
||||||
|
3. **性能问题**
|
||||||
|
- 在低版本设备上可能加载较慢
|
||||||
|
- 建议使用WiFi网络
|
||||||
|
- 关闭不必要的浏览器扩展
|
||||||
|
|
||||||
|
## 📞 **技术支持**
|
||||||
|
|
||||||
|
如有兼容性问题,请联系开发团队。
|
||||||
|
|
||||||
|
### **特别说明**
|
||||||
|
|
||||||
|
本项目已针对Android 7等低版本系统进行了特别优化,通过代码弥补了系统内核对ES6+特性支持不完整的问题。虽然不能完全替代系统升级,但可以确保应用在低版本Android设备上正常运行。
|
||||||
Reference in New Issue
Block a user