新增用戶信息統計功能,更新主頁面以顯示用戶相關數據,並調整狀態管理邏輯以提升性能。
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