diff --git a/nkebao/.env.development b/nkebao/.env.development
index 9ac98215..d130c9ed 100644
--- a/nkebao/.env.development
+++ b/nkebao/.env.development
@@ -1,4 +1,4 @@
# 基础环境变量示例
-# VITE_API_BASE_URL=http://www.yishi.com
-VITE_API_BASE_URL=https://ckbapi.quwanzhi.com
+VITE_API_BASE_URL=http://www.yishi.com
+# VITE_API_BASE_URL=https://ckbapi.quwanzhi.com
VITE_APP_TITLE=Nkebao Base
diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx
index bfee5515..fa7a4541 100644
--- a/nkebao/src/App.tsx
+++ b/nkebao/src/App.tsx
@@ -1,11 +1,8 @@
import React from "react";
import AppRouter from "@/router";
+
function App() {
- return (
- <>
-
- >
- );
+ return ;
}
export default App;
diff --git a/nkebao/src/components/DeviceGuard/index.tsx b/nkebao/src/components/DeviceGuard/index.tsx
new file mode 100644
index 00000000..9335ddfe
--- /dev/null
+++ b/nkebao/src/components/DeviceGuard/index.tsx
@@ -0,0 +1,90 @@
+import React, { useEffect, useState, useMemo } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+import { useDeviceStore } from "@/store/module/device";
+import { useUserStore } from "@/store/module/user";
+import { updateDeviceCount } from "@/utils/device";
+
+interface DeviceGuardProps {
+ children: React.ReactNode;
+}
+
+const DeviceGuard: React.FC = ({ children }) => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { isLoggedIn } = useUserStore();
+ const { setDeviceCount } = useDeviceStore();
+ const [isChecking, setIsChecking] = useState(true);
+
+ // 不需要设备检查的路径
+ const EXEMPT_PATHS = useMemo(
+ () => ["/login", "/guide", "/register", "/forgot-password"],
+ [],
+ );
+
+ useEffect(() => {
+ const checkDeviceStatus = async () => {
+ // 如果用户未登录,不需要检查设备状态
+ if (!isLoggedIn) {
+ setIsChecking(false);
+ return;
+ }
+
+ // 如果当前路径是豁免路径,不需要检查设备状态
+ if (EXEMPT_PATHS.includes(location.pathname)) {
+ setIsChecking(false);
+ return;
+ }
+
+ try {
+ // 从API获取最新的设备数量并更新到store
+ const currentDeviceCount = await updateDeviceCount(setDeviceCount);
+
+ // 如果设备数量为0且不在guide页面,跳转到guide页面
+ if (currentDeviceCount === 0 && location.pathname !== "/guide") {
+ navigate("/guide");
+ return;
+ }
+
+ // 如果设备数量大于0且在guide页面,跳转到首页
+ if (currentDeviceCount > 0 && location.pathname === "/guide") {
+ navigate("/");
+ return;
+ }
+ } catch (error) {
+ console.error("检查设备状态失败:", error);
+ // 如果检查失败,默认跳转到guide页面
+ if (location.pathname !== "/guide") {
+ navigate("/guide");
+ return;
+ }
+ } finally {
+ setIsChecking(false);
+ }
+ };
+
+ checkDeviceStatus();
+ }, [isLoggedIn, location.pathname, navigate, setDeviceCount, EXEMPT_PATHS]);
+
+ // 如果正在检查,显示加载状态
+ if (isChecking) {
+ return (
+
+ );
+ }
+
+ return <>{children}>;
+};
+
+export default DeviceGuard;
diff --git a/nkebao/src/pages/guide/api.ts b/nkebao/src/pages/guide/api.ts
new file mode 100644
index 00000000..d86d80ce
--- /dev/null
+++ b/nkebao/src/pages/guide/api.ts
@@ -0,0 +1,13 @@
+import request from "@/api/request";
+
+// 获取设备二维码
+export const fetchDeviceQRCode = (accountId: string) =>
+ request("/v1/api/device/add", { accountId }, "POST");
+
+// 通过IMEI添加设备
+export const addDeviceByImei = (imei: string, name: string) =>
+ request("/v1/api/device/add-by-imei", { imei, name }, "POST");
+
+// 获取设备列表
+export const fetchDeviceList = (params: { accountId?: string }) =>
+ request("/v1/devices/add-results", params, "GET");
diff --git a/nkebao/src/pages/guide/index.tsx b/nkebao/src/pages/guide/index.tsx
index 9dda790a..27c8aed8 100644
--- a/nkebao/src/pages/guide/index.tsx
+++ b/nkebao/src/pages/guide/index.tsx
@@ -8,16 +8,15 @@ import {
QrcodeOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
-import { getDashboard } from "@/pages/mobile/home/api";
-import { fetchDeviceQRCode, addDeviceByImei } from "@/api/devices";
-import { useUserStore } from "@/store/module/user";
+import { fetchDeviceQRCode, addDeviceByImei, fetchDeviceList } from "./api";
+import { useUserStore, useDeviceStore } from "@/store";
import styles from "./index.module.scss";
const Guide: React.FC = () => {
const navigate = useNavigate();
const { user } = useUserStore();
+ const { deviceCount, setDeviceCount } = useDeviceStore();
const [loading, setLoading] = useState(true);
- const [deviceCount, setDeviceCount] = useState(0);
// 添加设备弹窗状态
const [addVisible, setAddVisible] = useState(false);
@@ -37,8 +36,10 @@ const Guide: React.FC = () => {
const checkDeviceStatus = useCallback(async () => {
try {
setLoading(true);
- const dashboardData = await getDashboard();
- const deviceNum = dashboardData?.deviceNum || 0;
+ const dashboardData = await fetchDeviceList({
+ accountId: user.s2_accountId,
+ });
+ const deviceNum = dashboardData.added ? 1 : 0;
setDeviceCount(deviceNum);
@@ -48,7 +49,6 @@ const Guide: React.FC = () => {
return;
}
} catch (error) {
- console.error("检查设备状态失败:", error);
Toast.show({
content: "检查设备状态失败,请重试",
position: "top",
@@ -56,7 +56,7 @@ const Guide: React.FC = () => {
} finally {
setLoading(false);
}
- }, []);
+ }, [navigate, setDeviceCount]);
useEffect(() => {
checkDeviceStatus();
@@ -71,7 +71,9 @@ const Guide: React.FC = () => {
const pollDeviceStatus = async () => {
try {
- const dashboardData = await getDashboard();
+ const dashboardData = await fetchDeviceList({
+ accountId: user.s2_accountId,
+ });
const currentDeviceCount = dashboardData?.deviceNum || 0;
// 如果设备数量增加了,说明有新设备添加成功
@@ -95,7 +97,7 @@ const Guide: React.FC = () => {
// 每3秒检查一次设备状态
pollingRef.current = setInterval(pollDeviceStatus, 3000);
- }, [isPolling, deviceCount]);
+ }, [isPolling, deviceCount, setDeviceCount]);
// 停止轮询
const stopPolling = useCallback(() => {
diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx
index 225b8ef7..5a91a67c 100644
--- a/nkebao/src/pages/login/login.tsx
+++ b/nkebao/src/pages/login/login.tsx
@@ -6,9 +6,9 @@ import {
EyeOutline,
UserOutline,
} from "antd-mobile-icons";
-import { useUserStore } from "@/store/module/user";
+import { useUserStore, useDeviceStore } from "@/store";
import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api";
-import { getDashboard } from "@/pages/mobile/home/api";
+import { updateDeviceCount } from "@/utils/device";
import style from "./login.module.scss";
const Login: React.FC = () => {
@@ -22,6 +22,7 @@ const Login: React.FC = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { login } = useUserStore();
+ const { setDeviceCount } = useDeviceStore();
// 倒计时效果
useEffect(() => {
@@ -43,33 +44,17 @@ const Login: React.FC = () => {
// 发送验证码
const handleSendVerificationCode = async () => {
- const account = form.getFieldValue("account");
-
- if (!account) {
- Toast.show({ content: "请输入手机号", position: "top" });
- return;
- }
-
- // 手机号格式验证
- const phoneRegex = /^1[3-9]\d{9}$/;
- if (!phoneRegex.test(account)) {
- Toast.show({ content: "请输入正确的11位手机号", position: "top" });
- return;
- }
-
try {
- setLoading(true);
- await sendVerificationCode({
- mobile: account,
- type: "login",
- });
-
- Toast.show({ content: "验证码已发送", position: "top" });
+ const phone = form.getFieldValue("phone");
+ if (!phone) {
+ Toast.show({ content: "请输入手机号", position: "top" });
+ return;
+ }
+ await sendVerificationCode(phone);
setCountdown(60);
- } catch (error) {
- // 错误已在request中处理,这里不需要额外处理
- } finally {
- setLoading(false);
+ Toast.show({ content: "验证码已发送", position: "top" });
+ } catch (error: any) {
+ // 错误已在request中处理
}
};
@@ -101,11 +86,11 @@ const Login: React.FC = () => {
Toast.show({ content: "登录成功", position: "top" });
- // 检查设备绑定状态
+ // 检查设备绑定状态并更新到store
try {
- const dashboardData = await getDashboard();
- const deviceNum = dashboardData?.deviceNum || 0;
+ const deviceNum = await updateDeviceCount(setDeviceCount);
console.log(deviceNum, "deviceNum");
+
// 如果没有绑定设备,跳转到引导页面
if (deviceNum === 0) {
navigate("/guide");
@@ -113,7 +98,10 @@ const Login: React.FC = () => {
}
} catch (error) {
console.error("检查设备状态失败:", error);
- // 如果检查失败,默认跳转到首页
+ // 如果检查失败,设置设备数量为0并跳转到guide页面
+ setDeviceCount(0);
+ navigate("/guide");
+ return;
}
// 跳转到首页或重定向URL
diff --git a/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx b/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx
index 0b9fadc3..944d72b2 100644
--- a/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx
+++ b/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx
@@ -3,13 +3,12 @@ import { useParams, useNavigate } from "react-router-dom";
import { NavBar, Tabs, Switch, Toast, SpinLoading, Button } from "antd-mobile";
import { SettingOutlined, RedoOutlined } from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
-import MeauMobile from "@/components/MeauMobile/MeauMoible";
import {
fetchDeviceDetail,
fetchDeviceRelatedAccounts,
fetchDeviceHandleLogs,
updateDeviceTaskConfig,
-} from "@/api/devices";
+} from "./api";
import type { Device, WechatAccount, HandleLog } from "@/types/device";
const DeviceDetail: React.FC = () => {
diff --git a/nkebao/src/api/devices.ts b/nkebao/src/pages/mobile/mine/devices/api.ts
similarity index 97%
rename from nkebao/src/api/devices.ts
rename to nkebao/src/pages/mobile/mine/devices/api.ts
index 1a4534ab..c8e91198 100644
--- a/nkebao/src/api/devices.ts
+++ b/nkebao/src/pages/mobile/mine/devices/api.ts
@@ -1,4 +1,4 @@
-import request from "./request";
+import request from "@/api/request";
// 获取设备列表
export const fetchDeviceList = (params: {
diff --git a/nkebao/src/pages/mobile/mine/devices/index.tsx b/nkebao/src/pages/mobile/mine/devices/index.tsx
index 8fe74b53..444cfea3 100644
--- a/nkebao/src/pages/mobile/mine/devices/index.tsx
+++ b/nkebao/src/pages/mobile/mine/devices/index.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState, useCallback } from "react";
-import { NavBar, Popup, Tabs, Toast, SpinLoading, Dialog } from "antd-mobile";
+import { Popup, Tabs, Toast, SpinLoading } from "antd-mobile";
import { Button, Input, Pagination, Checkbox } from "antd";
import { useNavigate } from "react-router-dom";
import { AddOutline, DeleteOutline } from "antd-mobile-icons";
@@ -7,7 +7,6 @@ import {
ReloadOutlined,
SearchOutlined,
QrcodeOutlined,
- ArrowLeftOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
import {
@@ -15,7 +14,7 @@ import {
fetchDeviceQRCode,
addDeviceByImei,
deleteDevice,
-} from "@/api/devices";
+} from "./api";
import type { Device } from "@/types/device";
import { comfirm } from "@/utils/common";
import { useUserStore } from "@/store/module/user";
diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/list/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/list/api.ts
index b5b9bc36..d8bc9af4 100644
--- a/nkebao/src/pages/mobile/mine/traffic-pool/list/api.ts
+++ b/nkebao/src/pages/mobile/mine/traffic-pool/list/api.ts
@@ -1,6 +1,6 @@
import request from "@/api/request";
import type { TrafficPoolListResponse, DeviceOption } from "./data";
-import { fetchDeviceList } from "@/api/devices";
+import { fetchDeviceList } from "@/pages/guide/api";
// 获取流量池列表
export function fetchTrafficPoolList(params: {
diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx
index 117681a7..7ff448cc 100644
--- a/nkebao/src/router/index.tsx
+++ b/nkebao/src/router/index.tsx
@@ -1,6 +1,7 @@
import React from "react";
import { BrowserRouter, useRoutes, RouteObject } from "react-router-dom";
import PermissionRoute from "./permissionRoute";
+import DeviceGuard from "@/components/DeviceGuard";
// 动态导入所有 module 下的 ts/tsx 路由模块
const modules = import.meta.glob("./module/*.{ts,tsx}", { eager: true });
@@ -42,7 +43,9 @@ const AppRouter: React.FC = () => (
v7_relativeSplatPath: true,
}}
>
-
+
+
+
);
diff --git a/nkebao/src/store/index.ts b/nkebao/src/store/index.ts
index 4eca24e3..c74f8ad2 100644
--- a/nkebao/src/store/index.ts
+++ b/nkebao/src/store/index.ts
@@ -1,2 +1,3 @@
export * from "./module/user";
+export * from "./module/device";
// 未来可继续合并其他模块
diff --git a/nkebao/src/store/module/device.ts b/nkebao/src/store/module/device.ts
new file mode 100644
index 00000000..53477263
--- /dev/null
+++ b/nkebao/src/store/module/device.ts
@@ -0,0 +1,30 @@
+import { createPersistStore } from "@/store/createPersistStore";
+
+export interface DeviceState {
+ deviceCount: number;
+ setDeviceCount: (count: number) => void;
+ updateDeviceCount: () => Promise;
+ resetDeviceCount: () => void;
+}
+
+export const useDeviceStore = createPersistStore(
+ (set, get) => ({
+ deviceCount: 0,
+ setDeviceCount: (count: number) => set({ deviceCount: count }),
+ updateDeviceCount: async () => {
+ try {
+ // 这里需要导入getDashboard,但为了避免循环依赖,我们通过参数传入
+ // 实际使用时会在组件中调用并传入API函数
+ set({ deviceCount: 0 }); // 默认设置为0,实际值由调用方设置
+ } catch (error) {
+ console.error("更新设备数量失败:", error);
+ set({ deviceCount: 0 });
+ }
+ },
+ resetDeviceCount: () => set({ deviceCount: 0 }),
+ }),
+ "device-store",
+ state => ({
+ deviceCount: state.deviceCount,
+ }),
+);
diff --git a/nkebao/src/utils/device.ts b/nkebao/src/utils/device.ts
new file mode 100644
index 00000000..8b5c5b8c
--- /dev/null
+++ b/nkebao/src/utils/device.ts
@@ -0,0 +1,30 @@
+import { getDashboard } from "@/pages/mobile/home/api";
+
+/**
+ * 更新设备数量到store
+ * @param setDeviceCount store中的setDeviceCount函数
+ * @returns 更新后的设备数量
+ */
+export const updateDeviceCount = async (
+ setDeviceCount: (count: number) => void,
+): Promise => {
+ try {
+ const dashboardData = await getDashboard();
+ const deviceCount = dashboardData?.deviceNum || 0;
+ setDeviceCount(deviceCount);
+ return deviceCount;
+ } catch (error) {
+ console.error("更新设备数量失败:", error);
+ setDeviceCount(0);
+ return 0;
+ }
+};
+
+/**
+ * 检查是否需要设备绑定
+ * @param deviceCount 设备数量
+ * @returns 是否需要设备绑定
+ */
+export const needsDeviceBinding = (deviceCount: number): boolean => {
+ return deviceCount === 0;
+};