diff --git a/Cunkebao/.env.development b/Cunkebao/.env.development
index c008d630..2ddb67f5 100644
--- a/Cunkebao/.env.development
+++ b/Cunkebao/.env.development
@@ -1,4 +1,6 @@
# 基础环境变量示例
VITE_API_BASE_URL=http://www.yishi.com
+VITE_API_BASE_URL2=https://kf.quwanzhi.com:9991
+VITE_API_WS_URL=wss://kf.quwanzhi.com:9993
# VITE_API_BASE_URL=https://ckbapi.quwanzhi.com
VITE_APP_TITLE=存客宝
diff --git a/Cunkebao/.env.production b/Cunkebao/.env.production
index d71cee1d..838935bb 100644
--- a/Cunkebao/.env.production
+++ b/Cunkebao/.env.production
@@ -1,4 +1,6 @@
# 基础环境变量示例
VITE_API_BASE_URL=https://ckbapi.quwanzhi.com
+VITE_API_BASE_URL2=https://kf.quwanzhi.com:9991
+VITE_API_WS_URL=wss://kf.quwanzhi.com:9993
# VITE_API_BASE_URL=http://www.yishi.com
VITE_APP_TITLE=存客宝
diff --git a/Cunkebao/dist/.vite/manifest.json b/Cunkebao/dist/.vite/manifest.json
index 4f0c258e..1fc771a8 100644
--- a/Cunkebao/dist/.vite/manifest.json
+++ b/Cunkebao/dist/.vite/manifest.json
@@ -1,18 +1,14 @@
{
- "_charts-TuAbbBZ5.js": {
- "file": "assets/charts-TuAbbBZ5.js",
+ "_charts-CLRTJ7Uf.js": {
+ "file": "assets/charts-CLRTJ7Uf.js",
"name": "charts",
"imports": [
- "_ui-D1w-jetn.js",
+ "_ui-BFvqeNzU.js",
"_vendor-2vc8h_ct.js"
]
},
- "_ui-D0C0OGrH.css": {
- "file": "assets/ui-D0C0OGrH.css",
- "src": "_ui-D0C0OGrH.css"
- },
- "_ui-D1w-jetn.js": {
- "file": "assets/ui-D1w-jetn.js",
+ "_ui-BFvqeNzU.js": {
+ "file": "assets/ui-BFvqeNzU.js",
"name": "ui",
"imports": [
"_vendor-2vc8h_ct.js"
@@ -21,6 +17,10 @@
"assets/ui-D0C0OGrH.css"
]
},
+ "_ui-D0C0OGrH.css": {
+ "file": "assets/ui-D0C0OGrH.css",
+ "src": "_ui-D0C0OGrH.css"
+ },
"_utils-6WF66_dS.js": {
"file": "assets/utils-6WF66_dS.js",
"name": "utils",
@@ -33,18 +33,18 @@
"name": "vendor"
},
"index.html": {
- "file": "assets/index-D3HSx5Yt.js",
+ "file": "assets/index-C48GlG01.js",
"name": "index",
"src": "index.html",
"isEntry": true,
"imports": [
"_vendor-2vc8h_ct.js",
- "_ui-D1w-jetn.js",
+ "_ui-BFvqeNzU.js",
"_utils-6WF66_dS.js",
- "_charts-TuAbbBZ5.js"
+ "_charts-CLRTJ7Uf.js"
],
"css": [
- "assets/index-B0SB167P.css"
+ "assets/index-Ta4vyxDJ.css"
]
}
}
\ No newline at end of file
diff --git a/Cunkebao/dist/index.html b/Cunkebao/dist/index.html
index f79f7013..8a58e165 100644
--- a/Cunkebao/dist/index.html
+++ b/Cunkebao/dist/index.html
@@ -10,14 +10,14 @@
}
-
-
+
+
-
+
-
+
-
+
diff --git a/Cunkebao/index.html b/Cunkebao/index.html
index 92ab92a7..16de6c67 100644
--- a/Cunkebao/index.html
+++ b/Cunkebao/index.html
@@ -10,7 +10,7 @@
}
-
+
diff --git a/Cunkebao/src/android-polyfills.ts b/Cunkebao/src/android-polyfills.ts
new file mode 100644
index 00000000..583fd397
--- /dev/null
+++ b/Cunkebao/src/android-polyfills.ts
@@ -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已加载完成");
+}
diff --git a/Cunkebao/src/api/request2.ts b/Cunkebao/src/api/request2.ts
new file mode 100644
index 00000000..cacbdaf7
--- /dev/null
+++ b/Cunkebao/src/api/request2.ts
@@ -0,0 +1,81 @@
+import axios, {
+ AxiosInstance,
+ AxiosRequestConfig,
+ Method,
+ AxiosResponse,
+} from "axios";
+import { Toast } from "antd-mobile";
+import { useUserStore } from "@/store/module/user";
+const DEFAULT_DEBOUNCE_GAP = 1000;
+const debounceMap = new Map();
+
+interface RequestConfig extends AxiosRequestConfig {
+ headers: {
+ Client?: string;
+ "Content-Type"?: string;
+ };
+}
+
+const instance: AxiosInstance = axios.create({
+ baseURL: (import.meta as any).env?.VITE_API_BASE_URL2 || "/api",
+ timeout: 20000,
+ headers: {
+ "Content-Type": "application/json",
+ Client: "kefu-client",
+ },
+});
+
+instance.interceptors.request.use((config: any) => {
+ // 在每次请求时动态获取最新的 token2
+ const { token2 } = useUserStore.getState();
+
+ if (token2) {
+ config.headers = config.headers || {};
+ config.headers["Authorization"] = `bearer ${token2}`;
+ }
+ return config;
+});
+
+instance.interceptors.response.use(
+ (res: AxiosResponse) => {
+ return res.data;
+ },
+ err => {
+ Toast.show({ content: err.message || "网络异常", position: "top" });
+ return Promise.reject(err);
+ },
+);
+
+export function request(
+ url: string,
+ data?: any,
+ method: Method = "GET",
+ config?: RequestConfig,
+ debounceGap?: number,
+): Promise {
+ const gap =
+ typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP;
+ const key = `${method}_${url}_${JSON.stringify(data)}`;
+ const now = Date.now();
+ const last = debounceMap.get(key) || 0;
+ if (gap > 0 && now - last < gap) {
+ // Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' });
+ return Promise.reject("请求过于频繁,请稍后再试");
+ }
+ debounceMap.set(key, now);
+
+ const axiosConfig: RequestConfig = {
+ url,
+ method,
+ ...config,
+ };
+
+ if (method.toUpperCase() === "GET") {
+ axiosConfig.params = data;
+ } else {
+ axiosConfig.data = data;
+ }
+ return instance(axiosConfig);
+}
+
+export default request;
diff --git a/Cunkebao/src/components/AccountSelection/data.ts b/Cunkebao/src/components/AccountSelection/data.ts
index 8c65ee8d..c0ce4343 100644
--- a/Cunkebao/src/components/AccountSelection/data.ts
+++ b/Cunkebao/src/components/AccountSelection/data.ts
@@ -31,4 +31,5 @@ export interface AccountSelectionProps {
showSelectedList?: boolean;
readonly?: boolean;
onConfirm?: (selectedOptions: AccountItem[]) => void;
+ accountGroups?: any[]; // 传递账号组数据
}
diff --git a/Cunkebao/src/components/AndroidCompatibilityCheck.tsx b/Cunkebao/src/components/AndroidCompatibilityCheck.tsx
new file mode 100644
index 00000000..55bc0cc0
--- /dev/null
+++ b/Cunkebao/src/components/AndroidCompatibilityCheck.tsx
@@ -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({
+ 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 (
+
+
+ 🚨 Android 兼容性警告
+
+
+
+ 系统版本: Android {compatibility.androidVersion}
+ {compatibility.chromeVersion > 0 &&
+ ` | Chrome: ${compatibility.chromeVersion}`}
+ {compatibility.webViewVersion > 0 &&
+ ` | WebView: ${compatibility.webViewVersion}`}
+
+
+
+
+ 检测到的问题:
+
+
+ {compatibility.issues.map((issue, index) => (
+
+ • {issue}
+
+ ))}
+
+
+
+ {compatibility.suggestions.length > 0 && (
+
+
+ 建议解决方案:
+
+
+ {compatibility.suggestions.map((suggestion, index) => (
+
+ • {suggestion}
+
+ ))}
+
+
+ )}
+
+
+ 💡 应用已启用兼容模式,但建议升级系统以获得最佳体验
+
+
+
+
+ );
+};
+
+export default AndroidCompatibilityCheck;
diff --git a/Cunkebao/src/components/CompatibilityCheck.tsx b/Cunkebao/src/components/CompatibilityCheck.tsx
new file mode 100644
index 00000000..563d4242
--- /dev/null
+++ b/Cunkebao/src/components/CompatibilityCheck.tsx
@@ -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({
+ 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 (
+
+
+ 浏览器兼容性警告
+
+
+ 当前浏览器: {compatibility.browser} {compatibility.version}
+
+
+ {compatibility.issues.map((issue, index) => (
+
{issue}
+ ))}
+
+
+ 建议使用 Chrome 50+、Firefox 50+、Safari 10+ 或 Edge 12+
+
+
+ );
+};
+
+export default CompatibilityCheck;
diff --git a/Cunkebao/src/components/DeviceSelection/data.ts b/Cunkebao/src/components/DeviceSelection/data.ts
index abc9a214..d002905c 100644
--- a/Cunkebao/src/components/DeviceSelection/data.ts
+++ b/Cunkebao/src/components/DeviceSelection/data.ts
@@ -8,6 +8,8 @@ export interface DeviceSelectionItem {
wxid?: string;
nickname?: string;
usedInPlans?: number;
+ avatar?: string;
+ totalFriend?: number;
}
// 组件属性接口
@@ -23,4 +25,5 @@ export interface DeviceSelectionProps {
showInput?: boolean; // 新增
showSelectedList?: boolean; // 新增
readonly?: boolean; // 新增
+ deviceGroups?: any[]; // 传递设备组数据
}
diff --git a/Cunkebao/src/components/DeviceSelection/index.module.scss b/Cunkebao/src/components/DeviceSelection/index.module.scss
index ea776f81..8d004a48 100644
--- a/Cunkebao/src/components/DeviceSelection/index.module.scss
+++ b/Cunkebao/src/components/DeviceSelection/index.module.scss
@@ -67,60 +67,152 @@
}
.deviceItem {
display: flex;
- align-items: flex-start;
- gap: 12px;
- padding: 16px;
- border-radius: 12px;
- border: 1px solid #f0f0f0;
+ flex-direction: column;
+ padding: 12px;
background: #fff;
- cursor: pointer;
- transition: background 0.2s;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ transition: all 0.2s ease;
+ border: 1px solid #f5f5f5;
+
&:hover {
- background: #f5f6fa;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+ }
+}
+
+.headerRow {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.checkboxContainer {
+ flex-shrink: 0;
+}
+
+.imeiText {
+ font-size: 13px;
+ color: #666;
+ font-family: monospace;
+ flex: 1;
+}
+
+.mainContent {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 8px;
+ transition: background-color 0.2s ease;
+
+ &:hover {
+ background-color: #f8f9fa;
}
}
.deviceCheckbox {
- margin-top: 4px;
+ flex-shrink: 0;
}
.deviceInfo {
flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ gap: 12px;
}
+.deviceAvatar {
+ width: 64px;
+ height: 64px;
+ border-radius: 6px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.25);
+ flex-shrink: 0;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+
+ .avatarText {
+ font-size: 18px;
+ color: #fff;
+ font-weight: 700;
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
+ }
+}
+
+.deviceContent {
+ flex: 1;
+ min-width: 0;
+}
+
.deviceInfoRow {
display: flex;
align-items: center;
- justify-content: space-between;
+ gap: 6px;
+ margin-bottom: 6px;
}
.deviceName {
- font-weight: 500;
font-size: 16px;
- color: #222;
+ font-weight: 600;
+ color: #1a1a1a;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.statusOnline {
- width: 56px;
- height: 24px;
- border-radius: 12px;
- background: #52c41a;
- color: #fff;
- font-size: 13px;
- display: flex;
- align-items: center;
- justify-content: center;
+ font-size: 11px;
+ padding: 1px 6px;
+ border-radius: 8px;
+ color: #52c41a;
+ background: #f6ffed;
+ border: 1px solid #b7eb8f;
+ font-weight: 500;
}
.statusOffline {
- width: 56px;
- height: 24px;
- border-radius: 12px;
- background: #e5e6eb;
- color: #888;
- font-size: 13px;
- display: flex;
- align-items: center;
- justify-content: center;
+ font-size: 11px;
+ padding: 1px 6px;
+ border-radius: 8px;
+ color: #ff4d4f;
+ background: #fff2f0;
+ border: 1px solid #ffccc7;
+ font-weight: 500;
}
.deviceInfoDetail {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.infoItem {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.infoLabel {
font-size: 13px;
- color: #888;
- margin-top: 4px;
+ color: #666;
+ min-width: 50px;
+}
+
+.infoValue {
+ font-size: 13px;
+ color: #333;
+
+ &.imei {
+ font-family: monospace;
+ }
+
+ &.friendCount {
+ font-weight: 500;
+ }
}
.loadingBox {
display: flex;
diff --git a/Cunkebao/src/components/DeviceSelection/index.tsx b/Cunkebao/src/components/DeviceSelection/index.tsx
index 9d31e268..ba6952cd 100644
--- a/Cunkebao/src/components/DeviceSelection/index.tsx
+++ b/Cunkebao/src/components/DeviceSelection/index.tsx
@@ -46,6 +46,12 @@ const DeviceSelection: React.FC = ({
onSelect(selectedOptions.filter(v => v.id !== id));
};
+ // 清除所有已选设备
+ const handleClearAll = () => {
+ if (readonly) return;
+ onSelect([]);
+ };
+
return (
<>
{/* mode=input 显示输入框,mode=dialog不显示 */}
@@ -57,6 +63,7 @@ const DeviceSelection: React.FC = ({
onClick={openPopup}
prefix={}
allowClear={!readonly}
+ onClear={handleClearAll}
size="large"
readOnly={readonly}
disabled={readonly}
@@ -86,11 +93,52 @@ const DeviceSelection: React.FC = ({
style={{
display: "flex",
alignItems: "center",
- padding: "4px 8px",
+ padding: "8px 12px",
borderBottom: "1px solid #f0f0f0",
fontSize: 14,
}}
>
+ {/* 头像 */}
+
+ {device.avatar ? (
+

+ ) : (
+
+ {(device.memo || device.wechatId || "设")[0]}
+
+ )}
+
+
= ({
textOverflow: "ellipsis",
}}
>
- 【 {device.memo}】 - {device.wechatId}
+ {device.memo} - {device.wechatId}
{!readonly && (