diff --git a/nkebao/.env.development b/nkebao/.env.development index 9ac982158..d130c9eda 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/public/logo.png b/nkebao/public/logo.png new file mode 100644 index 000000000..40daafb0d Binary files /dev/null and b/nkebao/public/logo.png differ diff --git a/nkebao/src/api/common.ts b/nkebao/src/api/common.ts index ebe966eb5..77a0e7dea 100644 --- a/nkebao/src/api/common.ts +++ b/nkebao/src/api/common.ts @@ -7,7 +7,7 @@ import request from "./request"; */ export async function uploadFile( file: File, - uploadUrl: string = "/v1/attachment/upload" + uploadUrl: string = "/v1/attachment/upload", ): Promise { try { // 创建 FormData 对象用于文件上传 diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts index 18173f8da..2235a5529 100644 --- a/nkebao/src/api/request.ts +++ b/nkebao/src/api/request.ts @@ -47,7 +47,7 @@ instance.interceptors.response.use( err => { Toast.show({ content: err.message || "网络异常", position: "top" }); return Promise.reject(err); - } + }, ); export function request( @@ -55,7 +55,7 @@ export function request( data?: any, method: Method = "GET", config?: AxiosRequestConfig, - debounceGap?: number + debounceGap?: number, ): Promise { const gap = typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP; diff --git a/nkebao/src/components/AccountSelection/index.tsx b/nkebao/src/components/AccountSelection/index.tsx index be8023b89..3f8aace6c 100644 --- a/nkebao/src/components/AccountSelection/index.tsx +++ b/nkebao/src/components/AccountSelection/index.tsx @@ -117,7 +117,7 @@ export default function AccountSelection({ acc => acc.userName.includes(searchQuery) || acc.realName.includes(searchQuery) || - acc.departmentName.includes(searchQuery) + acc.departmentName.includes(searchQuery), ); // 处理账号选择 diff --git a/nkebao/src/components/ContentLibrarySelection/index.tsx b/nkebao/src/components/ContentLibrarySelection/index.tsx index a7e8507c7..b053f1d98 100644 --- a/nkebao/src/components/ContentLibrarySelection/index.tsx +++ b/nkebao/src/components/ContentLibrarySelection/index.tsx @@ -58,7 +58,7 @@ interface ContentLibrarySelectionProps { readonly?: boolean; onConfirm?: ( selectedIds: string[], - selectedItems: ContentLibraryItem[] + selectedItems: ContentLibraryItem[], ) => void; } @@ -86,7 +86,7 @@ export default function ContentLibrarySelection({ // 获取已选内容库详细信息 const selectedLibraryObjs = libraries.filter(item => - selectedLibraries.includes(item.id) + selectedLibraries.includes(item.id), ); // 删除已选内容库 @@ -161,7 +161,7 @@ export default function ContentLibrarySelection({ onSelect(newSelected); if (onSelectDetail) { const selectedObjs = libraries.filter(item => - newSelected.includes(item.id) + newSelected.includes(item.id), ); onSelectDetail(selectedObjs); } diff --git a/nkebao/src/components/DeviceSelection/selectionPopup.tsx b/nkebao/src/components/DeviceSelection/selectionPopup.tsx index d1e002f05..fc3ea3c21 100644 --- a/nkebao/src/components/DeviceSelection/selectionPopup.tsx +++ b/nkebao/src/components/DeviceSelection/selectionPopup.tsx @@ -61,7 +61,7 @@ const SelectionPopup: React.FC = ({ wxid: d.wechatId || "", nickname: d.nickname || "", usedInPlans: d.usedInPlans || 0, - })) + })), ); setTotal(res.total || 0); } @@ -71,7 +71,7 @@ const SelectionPopup: React.FC = ({ setLoading(false); } }, - [] + [], ); // 打开弹窗时获取第一页 diff --git a/nkebao/src/components/FriendSelection/index.tsx b/nkebao/src/components/FriendSelection/index.tsx index e2f37c363..c416d0114 100644 --- a/nkebao/src/components/FriendSelection/index.tsx +++ b/nkebao/src/components/FriendSelection/index.tsx @@ -137,7 +137,7 @@ export default function FriendSelection({ // 如果有 onSelectDetail 回调,传递完整的好友对象 if (onSelectDetail) { const selectedFriendObjs = friends.filter(friend => - newSelectedFriends.includes(friend.id) + newSelectedFriends.includes(friend.id), ); onSelectDetail(selectedFriendObjs); } diff --git a/nkebao/src/components/GroupSelection/index.tsx b/nkebao/src/components/GroupSelection/index.tsx index fe027edfa..423cf142b 100644 --- a/nkebao/src/components/GroupSelection/index.tsx +++ b/nkebao/src/components/GroupSelection/index.tsx @@ -59,7 +59,7 @@ export default function GroupSelection({ // 获取已选群聊详细信息 const selectedGroupObjs = groups.filter(group => - selectedGroups.includes(group.id) + selectedGroups.includes(group.id), ); // 删除已选群聊 @@ -141,7 +141,7 @@ export default function GroupSelection({ // 如果有 onSelectDetail 回调,传递完整的群聊对象 if (onSelectDetail) { const selectedGroupObjs = groups.filter(group => - newSelectedGroups.includes(group.id) + newSelectedGroups.includes(group.id), ); onSelectDetail(selectedGroupObjs); } diff --git a/nkebao/src/components/Layout/Layout.tsx b/nkebao/src/components/Layout/Layout.tsx index 43c51f1fc..160d993e4 100644 --- a/nkebao/src/components/Layout/Layout.tsx +++ b/nkebao/src/components/Layout/Layout.tsx @@ -20,7 +20,7 @@ const Layout: React.FC = ({ const setRealHeight = () => { document.documentElement.style.setProperty( "--real-vh", - `${window.innerHeight * 0.01}px` + `${window.innerHeight * 0.01}px`, ); }; setRealHeight(); diff --git a/nkebao/src/components/Upload/index.tsx b/nkebao/src/components/Upload/index.tsx index 73ec84a92..da1a2a211 100644 --- a/nkebao/src/components/Upload/index.tsx +++ b/nkebao/src/components/Upload/index.tsx @@ -68,7 +68,7 @@ const UploadComponent: React.FC = ({ Authorization: `Bearer ${localStorage.getItem("token")}`, }, body: formData, - } + }, ); if (!response.ok) { diff --git a/nkebao/src/pages/guide/api.ts b/nkebao/src/pages/guide/api.ts new file mode 100644 index 000000000..d86d80cef --- /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.module.scss b/nkebao/src/pages/guide/index.module.scss new file mode 100644 index 000000000..3acb01ac4 --- /dev/null +++ b/nkebao/src/pages/guide/index.module.scss @@ -0,0 +1,341 @@ +.guideContainer { + height: 100vh; + background: var(--primary-color); + padding: 16px; + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,'); + opacity: 0.3; + pointer-events: none; + } +} + +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + background: var(--primary-color); +} + +.loadingText { + color: white; + font-size: 16px; + margin-top: 16px; + font-weight: 500; +} + +.header { + text-align: center; + margin-bottom: 20px; + position: relative; + z-index: 1; +} + +.iconContainer { + width: 60px; + height: 60px; + background: #fff; + border-radius: 50%; + margin: 0 auto 12px; + border: 1px solid rgba(255, 255, 255, 0.3); + overflow: hidden; +} + +.logo { + width: 100%; + height: 100%; + object-fit: contain; +} + +.title { + color: white; + font-size: 22px; + font-weight: 700; + margin-bottom: 8px; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +.subtitle { + color: rgba(255, 255, 255, 0.9); + font-size: 14px; + line-height: 1.4; + max-width: 280px; + margin: 0 auto; +} + +.content { + flex: 1; + position: relative; + z-index: 1; + overflow-y: auto; + padding-right: 4px; +} + +.deviceStatus { + margin-bottom: 16px; +} + +.statusCard { + background: rgba(255, 255, 255, 0.95); + border-radius: 12px; + padding: 12px; + display: flex; + align-items: center; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.statusIcon { + width: 40px; + height: 40px; + background: var(--primary-color); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 12px; + color: white; + font-size: 20px; +} + +.statusInfo { + flex: 1; +} + +.statusTitle { + font-size: 14px; + font-weight: 600; + color: #333; + margin-bottom: 2px; +} + +.statusValue { + font-size: 12px; + color: #666; +} + +.deviceCount { + color: var(--primary-color); + font-weight: 700; + font-size: 16px; +} + +.guideSteps { + margin-bottom: 16px; +} + +.stepsTitle { + color: white; + font-size: 16px; + font-weight: 600; + margin-bottom: 12px; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +.stepList { + display: flex; + flex-direction: column; + gap: 8px; +} + +.stepItem { + background: rgba(255, 255, 255, 0.95); + border-radius: 8px; + padding: 10px; + display: flex; + align-items: flex-start; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + transition: transform 0.2s ease; + + &:hover { + transform: translateY(-1px); + } +} + +.stepNumber { + width: 24px; + height: 24px; + background: var(--primary-color); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: 700; + font-size: 12px; + margin-right: 10px; + flex-shrink: 0; +} + +.stepContent { + flex: 1; +} + +.stepTitle { + font-size: 14px; + font-weight: 600; + color: #333; + margin-bottom: 2px; +} + +.stepDesc { + font-size: 12px; + color: #666; + line-height: 1.3; +} + +.tips { + background: rgba(255, 255, 255, 0.95); + border-radius: 8px; + padding: 12px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.tipsTitle { + display: flex; + align-items: center; + font-size: 14px; + font-weight: 600; + color: #333; + margin-bottom: 8px; +} + +.tipsIcon { + color: #ff6b6b; + margin-right: 6px; + font-size: 16px; +} + +.tipsContent { + p { + font-size: 12px; + color: #666; + line-height: 1.4; + margin-bottom: 4px; + + &:last-child { + margin-bottom: 0; + } + } +} + +.footer { + margin-top: 16px; + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + gap: 8px; +} + +.primaryButton { + background: white; + border: none; + border-radius: 8px; + height: 44px; + font-size: 15px; + font-weight: 600; + color: var(--primary-color); + box-shadow: 0 2px 12px rgba(255, 255, 255, 0.4); + transition: all 0.3s ease; + + &:active { + transform: translateY(1px); + box-shadow: 0 1px 6px rgba(255, 255, 255, 0.4); + } +} + +.buttonIcon { + margin-left: 6px; + font-size: 12px; +} + +.secondaryButton { + border: 2px solid rgba(255, 255, 255, 0.8); + border-radius: 8px; + height: 44px; + font-size: 15px; + font-weight: 600; + color: white; + background: transparent; + backdrop-filter: blur(10px); + + &:active { + background: rgba(255, 255, 255, 0.1); + } +} + +// 响应式设计 +@media (max-width: 480px) { + .guideContainer { + padding: 12px; + } + + .title { + font-size: 20px; + } + + .subtitle { + font-size: 13px; + } + + .statusCard { + padding: 10px; + } + + .stepItem { + padding: 8px; + } + + .tips { + padding: 10px; + } +} + +// 动画效果 +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.header, +.deviceStatus, +.guideSteps, +.tips { + animation: fadeInUp 0.5s ease-out; +} + +.guideSteps { + animation-delay: 0.1s; +} + +.tips { + animation-delay: 0.2s; +} + +.footer { + animation: fadeInUp 0.5s ease-out 0.3s both; +} diff --git a/nkebao/src/pages/guide/index.tsx b/nkebao/src/pages/guide/index.tsx new file mode 100644 index 000000000..da42ed053 --- /dev/null +++ b/nkebao/src/pages/guide/index.tsx @@ -0,0 +1,349 @@ +import React, { useEffect, useState, useCallback, useRef } from "react"; +import { useNavigate } from "react-router-dom"; +import { Button, Toast, Popup, Tabs, Input } from "antd-mobile"; +import { + MobileOutlined, + ExclamationCircleOutlined, + ArrowRightOutlined, + QrcodeOutlined, +} from "@ant-design/icons"; +import Layout from "@/components/Layout/Layout"; +import { fetchDeviceQRCode, addDeviceByImei, fetchDeviceList } from "./api"; +import { useUserStore } from "@/store/module/user"; +import styles from "./index.module.scss"; +const Guide: React.FC = () => { + const navigate = useNavigate(); + const { user } = useUserStore(); + const [loading, setLoading] = useState(false); + const [deviceCount, setDeviceCount] = useState(user?.deviceTotal || 0); + + // 添加设备弹窗状态 + const [addVisible, setAddVisible] = useState(false); + const [addTab, setAddTab] = useState("scan"); + const [qrLoading, setQrLoading] = useState(false); + const [qrCode, setQrCode] = useState(null); + const [imei, setImei] = useState(""); + const [name, setName] = useState(""); + const [addLoading, setAddLoading] = useState(false); + + // 轮询监听相关 + const [isPolling, setIsPolling] = useState(false); + const pollingRef = useRef(null); + const initialDeviceCountRef = useRef(deviceCount); + + // 检查设备绑定状态 + const checkDeviceStatus = useCallback(async () => { + try { + setLoading(true); + // 使用store中的设备数量 + const deviceNum = user?.deviceTotal || 0; + setDeviceCount(deviceNum); + + // 如果已有设备,直接跳转到首页 + if (deviceNum > 0) { + navigate("/"); + return; + } + } catch (error) { + console.error("检查设备状态失败:", error); + Toast.show({ + content: "检查设备状态失败,请重试", + position: "top", + }); + } finally { + setLoading(false); + } + }, [user?.deviceTotal, navigate]); + + useEffect(() => { + checkDeviceStatus(); + }, [checkDeviceStatus]); + + // 开始轮询监听设备状态 + const startPolling = useCallback(() => { + if (isPolling) return; + + setIsPolling(true); + initialDeviceCountRef.current = deviceCount; + + const pollDeviceStatus = async () => { + try { + // 这里可以调用一个简单的设备数量接口来检查是否有新设备 + // 或者使用其他方式检测设备状态变化 + // 暂时使用store中的数量,实际项目中可能需要调用专门的接口 + let currentDeviceCount = user?.deviceTotal || 0; + const res = await fetchDeviceList({ accountId: user?.s2_accountId }); + if (res.added) { + currentDeviceCount = 1; + Toast.show({ content: "设备添加成功!", position: "top" }); + setAddVisible(false); + setDeviceCount(currentDeviceCount); + setIsPolling(false); + if (pollingRef.current) { + clearInterval(pollingRef.current); + pollingRef.current = null; + } + // 可以选择跳转到首页或继续留在当前页面 + navigate("/"); + return; + } + } catch (error) { + console.error("轮询检查设备状态失败:", error); + } + }; + + // 每3秒检查一次设备状态 + pollingRef.current = setInterval(pollDeviceStatus, 3000); + }, [isPolling, user?.s2_accountId]); + + // 停止轮询 + const stopPolling = useCallback(() => { + setIsPolling(false); + if (pollingRef.current) { + clearInterval(pollingRef.current); + pollingRef.current = null; + } + }, []); + + // 组件卸载时清理轮询 + useEffect(() => { + return () => { + if (pollingRef.current) { + clearInterval(pollingRef.current); + } + }; + }, []); + + // 获取二维码 + const handleGetQr = async () => { + setQrLoading(true); + setQrCode(null); + try { + const accountId = user?.s2_accountId; + if (!accountId) throw new Error("未获取到用户信息"); + const res = await fetchDeviceQRCode(accountId); + setQrCode(res.qrCode); + // 获取二维码后开始轮询监听 + startPolling(); + } catch (e: any) { + Toast.show({ content: e.message || "获取二维码失败", position: "top" }); + } finally { + setQrLoading(false); + } + }; + + // 跳转到设备管理页面 + const handleGoToDevices = () => { + handleGetQr(); + setAddVisible(true); + }; + + // 手动添加设备 + const handleAddDevice = async () => { + if (!imei.trim() || !name.trim()) { + Toast.show({ content: "请填写完整信息", position: "top" }); + return; + } + setAddLoading(true); + try { + await addDeviceByImei(imei, name); + Toast.show({ content: "添加成功", position: "top" }); + setAddVisible(false); + setImei(""); + setName(""); + // 重新检查设备状态 + await checkDeviceStatus(); + } catch (e: any) { + Toast.show({ content: e.message || "添加失败", position: "top" }); + } finally { + setAddLoading(false); + } + }; + + // 关闭弹窗时停止轮询 + const handleClosePopup = () => { + setAddVisible(false); + stopPolling(); + setQrCode(null); + }; + + if (loading) { + return ( + +
+
检查设备状态中...
+
+
+ ); + } + + return ( + +
+ {/* 头部区域 */} +
+
+ 存客宝 +
+

欢迎使用存客宝

+

请先绑定设备以获得完整功能体验

+
+ + {/* 内容区域 */} +
+
+
+
+ +
+
+
设备绑定状态
+
+ 已绑定: + {deviceCount} 台 +
+
+
+
+ +
+

绑定步骤

+
+
+
1
+
+
准备设备
+
+ 确保手机已安装存客宝应用 +
+
+
+
+
2
+
+
扫描二维码
+
在设备管理页面扫描绑定
+
+
+
+
3
+
+
开始使用
+
+ 绑定成功后即可使用所有功能 +
+
+
+
+
+ +
+
+ + 温馨提示 +
+
+

• 绑定设备后可享受完整功能体验

+

• 每个账号最多可绑定10台设备

+

• 如需帮助请联系客服

+
+
+
+ + {/* 底部按钮区域 */} +
+ +
+
+ + {/* 添加设备弹窗 */} + +
+ + + + + {addTab === "scan" && ( +
+ + {qrCode && ( +
+ 二维码 +
+ 请用手机扫码添加设备 +
+ {isPolling && ( +
+ 正在监听设备添加状态... +
+ )} +
+ )} +
+ )} + {addTab === "manual" && ( +
+ setName(val)} + clearable + /> + setImei(val)} + clearable + /> + +
+ )} +
+
+
+ ); +}; + +export default Guide; diff --git a/nkebao/src/pages/login/api.ts b/nkebao/src/pages/login/api.ts index 617d0bed9..9c1f6e6c9 100644 --- a/nkebao/src/pages/login/api.ts +++ b/nkebao/src/pages/login/api.ts @@ -11,6 +11,7 @@ export interface LoginResponse { data: { token: string; token_expired: string; + deviceTotal: number; // 设备总数 member: { id: string; name: string; diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index f53ccc6d0..c1b528071 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; -import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile"; +import { Form, Input, Button, Toast, Checkbox } from "antd-mobile"; import { EyeInvisibleOutline, EyeOutline, @@ -95,22 +95,32 @@ const Login: React.FC = () => { } console.log(response, "response"); + // 获取设备总数 + const deviceTotal = response.deviceTotal || 0; + console.log(deviceTotal, "deviceTotal"); + // 更新状态管理(token会自动存储到localStorage,用户信息存储在状态管理中) - login(response.token, response.member); + login(response.token, response.member, deviceTotal); Toast.show({ content: "登录成功", position: "top" }); - // 跳转到首页或重定向URL - const returnUrl = searchParams.get("returnUrl"); - if (returnUrl) { - const decodedUrl = decodeURIComponent(returnUrl); - if (isLoginPage(decodedUrl)) { - navigate("/"); + // 根据设备数量判断跳转 + if (deviceTotal > 0) { + // 有设备,跳转到首页或重定向URL + const returnUrl = searchParams.get("returnUrl"); + if (returnUrl) { + const decodedUrl = decodeURIComponent(returnUrl); + if (isLoginPage(decodedUrl)) { + navigate("/"); + } else { + window.location.href = decodedUrl; + } } else { - window.location.href = decodedUrl; + navigate("/"); } } else { - navigate("/"); + // 没有设备,跳转到引导页面 + navigate("/guide"); } } catch (error: any) { // 错误已在request中处理,这里不需要额外处理 diff --git a/nkebao/src/pages/mobile/content/form/api.ts b/nkebao/src/pages/mobile/content/form/api.ts index 4cce9f260..bc136d885 100644 --- a/nkebao/src/pages/mobile/content/form/api.ts +++ b/nkebao/src/pages/mobile/content/form/api.ts @@ -12,14 +12,14 @@ export function getContentLibraryDetail(id: string): Promise { // 创建内容库 export function createContentLibrary( - params: CreateContentLibraryParams + params: CreateContentLibraryParams, ): Promise { return request("/v1/content/library/create", params, "POST"); } // 更新内容库 export function updateContentLibrary( - params: UpdateContentLibraryParams + params: UpdateContentLibraryParams, ): Promise { const { id, ...data } = params; return request(`/v1/content/library/update`, { id, ...data }, "POST"); diff --git a/nkebao/src/pages/mobile/content/list/api.ts b/nkebao/src/pages/mobile/content/list/api.ts index 7bb985ee2..51821b6f7 100644 --- a/nkebao/src/pages/mobile/content/list/api.ts +++ b/nkebao/src/pages/mobile/content/list/api.ts @@ -22,14 +22,14 @@ export function getContentLibraryDetail(id: string): Promise { // 创建内容库 export function createContentLibrary( - params: CreateContentLibraryParams + params: CreateContentLibraryParams, ): Promise { return request("/v1/content/library/create", params, "POST"); } // 更新内容库 export function updateContentLibrary( - params: UpdateContentLibraryParams + params: UpdateContentLibraryParams, ): Promise { const { id, ...data } = params; return request(`/v1/content/library/update`, { id, ...data }, "POST"); @@ -43,7 +43,7 @@ export function deleteContentLibrary(id: string): Promise { // 切换内容库状态 export function toggleContentLibraryStatus( id: string, - status: number + status: number, ): Promise { return request("/v1/content/library/update-status", { id, status }, "POST"); } diff --git a/nkebao/src/pages/mobile/content/list/index.tsx b/nkebao/src/pages/mobile/content/list/index.tsx index f3b0eb134..a601710cf 100644 --- a/nkebao/src/pages/mobile/content/list/index.tsx +++ b/nkebao/src/pages/mobile/content/list/index.tsx @@ -182,7 +182,7 @@ const ContentLibraryList: React.FC = () => { const filteredLibraries = libraries.filter( library => library.name.toLowerCase().includes(searchQuery.toLowerCase()) || - library.creatorName?.toLowerCase().includes(searchQuery.toLowerCase()) + library.creatorName?.toLowerCase().includes(searchQuery.toLowerCase()), ); return ( diff --git a/nkebao/src/pages/mobile/home/index.tsx b/nkebao/src/pages/mobile/home/index.tsx index 5ff2672a9..25fadb073 100644 --- a/nkebao/src/pages/mobile/home/index.tsx +++ b/nkebao/src/pages/mobile/home/index.tsx @@ -140,6 +140,10 @@ const Home: React.FC = () => { navigate("/wechat-accounts"); }; + const handleAliveWechatClick = () => { + navigate("/wechat-accounts?wechatStatus=1"); + }; + return ( { -
+
在线微信号
{dashboard.aliveWechatNum || 35} @@ -206,8 +213,8 @@ const Home: React.FC = () => { onClick={() => navigate( `/scenarios/list/${scenario.id}/${encodeURIComponent( - scenario.name - )}` + scenario.name, + )}`, ) } > diff --git a/nkebao/src/pages/mobile/mine/consumption-records/api.ts b/nkebao/src/pages/mobile/mine/consumption-records/api.ts new file mode 100644 index 000000000..52f953377 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/api.ts @@ -0,0 +1,17 @@ +import request from "@/api/request"; +import { ConsumptionRecordsResponse, ConsumptionRecordDetail } from "./data"; + +// 获取消费记录列表 +export function getConsumptionRecords(params: { + page: number; + limit: number; +}): Promise { + return request("/v1/consumption-records", params, "GET"); +} + +// 获取消费记录详情 +export function getConsumptionRecordDetail( + id: string, +): Promise { + return request(`/v1/consumption-records/${id}`, {}, "GET"); +} diff --git a/nkebao/src/pages/mobile/mine/consumption-records/data.ts b/nkebao/src/pages/mobile/mine/consumption-records/data.ts new file mode 100644 index 000000000..391b40dda --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/data.ts @@ -0,0 +1,26 @@ +// 消费记录类型定义 +export interface ConsumptionRecord { + id: string; + type: "recharge" | "ai_service" | "version_upgrade"; + amount: number; + description: string; + createTime: string; + status: "success" | "pending" | "failed"; + balance?: number; +} + +// API响应类型 +export interface ConsumptionRecordsResponse { + list: ConsumptionRecord[]; + total: number; + page: number; + limit: number; +} + +// 消费记录详情 +export interface ConsumptionRecordDetail extends ConsumptionRecord { + orderNo?: string; + paymentMethod?: string; + remark?: string; + operator?: string; +} diff --git a/nkebao/src/pages/mobile/mine/consumption-records/index.module.scss b/nkebao/src/pages/mobile/mine/consumption-records/index.module.scss new file mode 100644 index 000000000..f244cccce --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/index.module.scss @@ -0,0 +1,141 @@ +.records-page { + padding: 16px; + background: #f7f8fa; + min-height: 100vh; +} + +.records-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.record-card { + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + background: #fff; +} + +.record-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; +} + +.record-info { + display: flex; + align-items: flex-start; + gap: 12px; + flex: 1; +} + +.type-icon-wrapper { + width: 40px; + height: 40px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.type-icon { + font-size: 20px; + color: #666; +} + +.record-details { + flex: 1; + min-width: 0; +} + +.record-description { + font-size: 16px; + font-weight: 500; + color: #222; + margin-bottom: 4px; + line-height: 1.4; +} + +.record-time { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: #999; +} + +.time-icon { + font-size: 12px; +} + +.record-amount { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 4px; + flex-shrink: 0; +} + +.amount-text { + font-size: 16px; + font-weight: 600; +} + +.status-tag { + font-size: 11px; + padding: 2px 6px; + border-radius: 8px; +} + +.balance-info { + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid #f0f0f0; + font-size: 12px; + color: #666; +} + +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 20px; +} + +.loading-text { + font-size: 14px; + color: #666; +} + +.load-more { + text-align: center; + padding: 16px; + color: var(--primary-color); + font-size: 14px; + font-weight: 500; + cursor: pointer; + background: #fff; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: background-color 0.2s ease; + + &:hover { + background-color: #f8f9fa; + } + + &:active { + background-color: #e9ecef; + } +} + +.empty-state { + margin-top: 60px; +} + +.empty-icon { + font-size: 48px; + color: #ccc; +} diff --git a/nkebao/src/pages/mobile/mine/consumption-records/index.tsx b/nkebao/src/pages/mobile/mine/consumption-records/index.tsx new file mode 100644 index 000000000..64e20ac58 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/index.tsx @@ -0,0 +1,212 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { Card, List, Tag, SpinLoading, Empty } from "antd-mobile"; +import { useUserStore } from "@/store/module/user"; +import style from "./index.module.scss"; +import { + WalletOutlined, + RobotOutlined, + CrownOutlined, + ClockCircleOutlined, +} from "@ant-design/icons"; +import NavCommon from "@/components/NavCommon"; +import Layout from "@/components/Layout/Layout"; +import { getConsumptionRecords } from "./api"; +import { ConsumptionRecord } from "./data"; + +const ConsumptionRecords: React.FC = () => { + const navigate = useNavigate(); + const { user } = useUserStore(); + const [records, setRecords] = useState([]); + const [loading, setLoading] = useState(true); + const [hasMore, setHasMore] = useState(true); + const [page, setPage] = useState(1); + + useEffect(() => { + loadRecords(); + }, []); + + const loadRecords = async (reset = false) => { + if (loading) return; + setLoading(true); + try { + const currentPage = reset ? 1 : page; + const response = await getConsumptionRecords({ + page: currentPage, + limit: 20, + }); + + const newRecords = response.list || []; + setRecords(prev => (reset ? newRecords : [...prev, ...newRecords])); + setHasMore(newRecords.length === 20); + if (reset) setPage(1); + else setPage(currentPage + 1); + } catch (error) { + console.error("加载消费记录失败:", error); + } finally { + setLoading(false); + } + }; + + const getTypeIcon = (type: string) => { + switch (type) { + case "recharge": + return ; + case "ai_service": + return ; + case "version_upgrade": + return ; + default: + return ; + } + }; + + const getTypeColor = (type: string) => { + switch (type) { + case "recharge": + return "#52c41a"; + case "ai_service": + return "#1890ff"; + case "version_upgrade": + return "#722ed1"; + default: + return "#666"; + } + }; + + const getStatusText = (status: string) => { + switch (status) { + case "success": + return "成功"; + case "pending": + return "处理中"; + case "failed": + return "失败"; + default: + return "未知"; + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case "success": + return "#52c41a"; + case "pending": + return "#faad14"; + case "failed": + return "#ff4d4f"; + default: + return "#666"; + } + }; + + const formatAmount = (amount: number, type: string) => { + if (type === "recharge") { + return `+¥${amount.toFixed(2)}`; + } else { + return `-¥${amount.toFixed(2)}`; + } + }; + + const formatTime = (timeStr: string) => { + const date = new Date(timeStr); + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + + if (days === 0) { + return date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + }); + } else if (days === 1) { + return ( + "昨天 " + + date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + }) + ); + } else if (days < 7) { + return `${days}天前`; + } else { + return date.toLocaleDateString("zh-CN"); + } + }; + + const renderRecordItem = (record: ConsumptionRecord) => ( + +
+
+
+ {getTypeIcon(record.type)} +
+
+
+ {record.description} +
+
+ + {formatTime(record.createTime)} +
+
+
+
+
+ {formatAmount(record.amount, record.type)} +
+ + {getStatusText(record.status)} + +
+
+ {record.balance !== undefined && ( +
+ 余额: ¥{record.balance.toFixed(2)} +
+ )} +
+ ); + + return ( + }> +
+ {records.length === 0 && !loading ? ( + } + /> + ) : ( +
+ {records.map(renderRecordItem)} + {loading && ( +
+ +
加载中...
+
+ )} + {!loading && hasMore && ( +
loadRecords()}> + 加载更多 +
+ )} +
+ )} +
+
+ ); +}; + +export default ConsumptionRecords; diff --git a/nkebao/src/api/devices.ts b/nkebao/src/pages/mobile/mine/devices/api.ts similarity index 96% rename from nkebao/src/api/devices.ts rename to nkebao/src/pages/mobile/mine/devices/api.ts index efadbc918..c8e91198d 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: { @@ -19,7 +19,7 @@ export const fetchDeviceRelatedAccounts = (id: string | number) => export const fetchDeviceHandleLogs = ( id: string | number, page = 1, - limit = 10 + limit = 10, ) => request(`/v1/devices/${id}/handle-logs`, { page, limit }, "GET"); // 更新设备任务配置 diff --git a/nkebao/src/pages/mobile/mine/devices/index.tsx b/nkebao/src/pages/mobile/mine/devices/index.tsx index 84464d2f1..444cfea3c 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,18 +7,18 @@ import { ReloadOutlined, SearchOutlined, QrcodeOutlined, - ArrowLeftOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; -import MeauMobile from "@/components/MeauMobile/MeauMoible"; import { fetchDeviceList, 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"; +import NavCommon from "@/components/NavCommon"; const Devices: React.FC = () => { // 设备列表相关 @@ -47,6 +47,7 @@ const Devices: React.FC = () => { const [delLoading, setDelLoading] = useState(false); const navigate = useNavigate(); + const { user } = useUserStore(); // 加载设备列表 const loadDevices = useCallback( async (reset = false) => { @@ -68,7 +69,7 @@ const Devices: React.FC = () => { setLoading(false); } }, - [loading, search, page] + [loading, search, page], ); // 首次加载和搜索 @@ -86,7 +87,7 @@ const Devices: React.FC = () => { setPage(p => p + 1); } }, - { threshold: 0.5 } + { threshold: 0.5 }, ); if (observerRef.current) observer.observe(observerRef.current); return () => observer.disconnect(); @@ -112,7 +113,7 @@ const Devices: React.FC = () => { setQrLoading(true); setQrCode(null); try { - const accountId = localStorage.getItem("s2_accountId") || ""; + const accountId = user.s2_accountId; if (!accountId) throw new Error("未获取到用户信息"); const res = await fetchDeviceQRCode(accountId); setQrCode(res.qrCode); @@ -123,6 +124,11 @@ const Devices: React.FC = () => { } }; + const addDevice = async () => { + await handleGetQr(); + setAddVisible(true); + }; + // 手动添加设备 const handleAddDevice = async () => { if (!imei.trim() || !name.trim()) { @@ -166,7 +172,7 @@ const Devices: React.FC = () => { try { await comfirm( `将删除${selected.length}个设备,删除后本设备配置的计划任务操作也将失效。确认删除?`, - { title: "确认删除", confirmText: "确认删除", cancelText: "取消" } + { title: "确认删除", confirmText: "确认删除", cancelText: "取消" }, ); handleDelete(); } catch { @@ -189,33 +195,15 @@ const Devices: React.FC = () => { - - navigate(-1)} - /> -
- } - style={{ background: "#fff" }} + setAddVisible(true)} - > + } - > - - 设备管理 - - - + />
{/* 搜索栏 */}
@@ -301,7 +289,7 @@ const Devices: React.FC = () => { setSelected(prev => e.target.checked ? [...prev, device.id!] - : prev.filter(id => id !== device.id) + : prev.filter(id => id !== device.id), ); }} onClick={e => e.stopPropagation()} @@ -373,7 +361,7 @@ const Devices: React.FC = () => { {addTab === "scan" && (
+ ))} +
+ + + +
服务消耗
+
+ 使用以下服务将从余额中扣除相应费用。 +
+
+ {balance < 10 && ( + +
+ +
+
余额不足提醒
+
+ 当前余额较低,建议及时充值以免影响服务使用
- -
快捷充值
-
- {quickAmounts.map(amt => ( - - ))} -
- -
- -
服务消耗
-
- 使用以下服务将从余额中扣除相应费用。 -
-
- {balance < 10 && ( - -
- -
-
余额不足提醒
-
- 当前余额较低,建议及时充值以免影响服务使用 + )} +
+ ); + + // 渲染AI服务tab内容 + const renderAiServices = () => ( +
+
+
+ + AI智能服务收费 +
+
统一按次收费
+
+
+ 三项核心AI服务,按使用次数收费,每次1元 +
+ +
+ {aiServices.map(service => ( + +
+
+
{service.icon}
+
+
{service.name}
+
+ ¥{service.price}/次 +
+
+ {service.description} +
+
+ {service.features.map((feature, index) => ( +
+ + {feature} +
+ ))} +
+
+
今日使用进度
+
+
+
+
+ {service.usage.current} / {service.usage.total} +
+
- )} + ))} +
+
+ ); + + // 渲染版本套餐tab内容 + const renderVersionPackages = () => ( +
+
+ + 存客宝版本套餐 +
+
+ 选择适合的版本,享受不同级别的AI服务 +
+ +
+ {versionPackages.map(pkg => ( + +
+
+
{pkg.icon}
+
+
+ {pkg.name} + {pkg.tag && ( + + {pkg.tag} + + )} +
+
{pkg.price}
+
+
+
+
+ {pkg.description} +
+
+
包含功能:
+ {pkg.features.map((feature, index) => ( +
+ + {feature} +
+ ))} +
+ {pkg.status && ( +
{pkg.status}
+ )} + {pkg.buttonText && ( + + )} +
+ ))} +
+
+ ); + + return ( + navigate("/mine/consumption-records")} + > + +  记录 +
+ } + /> + } + > +
+ + + {renderAccountRecharge()} + + + {renderAiServices()} + + + {renderVersionPackages()} + +
); diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index dfad51951..0a0a1e92c 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -6,7 +6,7 @@ import type { } from "./data"; export function getTrafficPoolDetail( - wechatId: string + wechatId: string, ): Promise { return request("/v1/wechats/getWechatInfo", { wechatId }, "GET"); } diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index f478fdfc4..88fd1c415 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -520,7 +520,7 @@ const TrafficPoolDetail: React.FC = () => { {restriction.reason || "未知原因"} { setSelectedIds(prev => - checked ? [...prev, id] : prev.filter(i => i !== id) + checked ? [...prev, id] : prev.filter(i => i !== id), ); }; diff --git a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts index 0cb792811..d98ec6d2f 100644 --- a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts @@ -19,7 +19,7 @@ export function getWechatFriends(params: { limit: params.limit, keyword: params.keyword, }, - "GET" + "GET", ); } diff --git a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx index 83666dbcd..25271b237 100644 --- a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx @@ -133,7 +133,7 @@ const WechatAccountDetail: React.FC = () => { setIsFetchingFriends(false); } }, - [id] + [id], ); // 搜索好友 @@ -153,7 +153,7 @@ const WechatAccountDetail: React.FC = () => { setFriendsPage(page); fetchFriendsList(page, searchQuery); }, - [searchQuery, fetchFriendsList] + [searchQuery, fetchFriendsList], ); // 初始化数据 diff --git a/nkebao/src/pages/mobile/mine/wechat-accounts/list/api.ts b/nkebao/src/pages/mobile/mine/wechat-accounts/list/api.ts index 005b35441..db6f3a2cb 100644 --- a/nkebao/src/pages/mobile/mine/wechat-accounts/list/api.ts +++ b/nkebao/src/pages/mobile/mine/wechat-accounts/list/api.ts @@ -5,6 +5,7 @@ export function getWechatAccounts(params: { page: number; page_size: number; keyword?: string; + wechatStatus?: string; }) { return request("v1/wechats", params, "GET"); } diff --git a/nkebao/src/pages/mobile/mine/wechat-accounts/list/index.tsx b/nkebao/src/pages/mobile/mine/wechat-accounts/list/index.tsx index df93801c4..4b761b60e 100644 --- a/nkebao/src/pages/mobile/mine/wechat-accounts/list/index.tsx +++ b/nkebao/src/pages/mobile/mine/wechat-accounts/list/index.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { Button, SpinLoading, Toast } from "antd-mobile"; import { Pagination, Input, Tooltip } from "antd"; import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useSearchParams } from "react-router-dom"; import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; import { getWechatAccounts } from "./api"; @@ -26,6 +26,7 @@ const PAGE_SIZE = 10; const WechatAccounts: React.FC = () => { const navigate = useNavigate(); + const [searchParams] = useSearchParams(); const [accounts, setAccounts] = useState([]); const [searchTerm, setSearchTerm] = useState(""); const [currentPage, setCurrentPage] = useState(1); @@ -33,14 +34,24 @@ const WechatAccounts: React.FC = () => { const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); + // 获取路由参数 wechatStatus + const wechatStatus = searchParams.get("wechatStatus"); + const fetchAccounts = async (page = 1, keyword = "") => { setIsLoading(true); try { - const res = await getWechatAccounts({ + const params: any = { page, page_size: PAGE_SIZE, keyword, - }); + }; + + // 如果有 wechatStatus 参数,添加到请求参数中 + if (wechatStatus) { + params.wechatStatus = wechatStatus; + } + + const res = await getWechatAccounts(params); if (res && res.list) { setAccounts(res.list); setTotalAccounts(res.total || 0); @@ -87,7 +98,9 @@ const WechatAccounts: React.FC = () => { - +
{ image: item.image || "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png", - description: - scenarioDescriptions[item.name?.toLowerCase()] || - "通过该平台进行获客", + description: "", count: item.count, growth: item.growth, status: item.status, @@ -69,7 +67,7 @@ const Scene: React.FC = () => { const handleScenarioClick = (scenarioId: string, scenarioName: string) => { navigate( - `/scenarios/list/${scenarioId}/${encodeURIComponent(scenarioName)}` + `/scenarios/list/${scenarioId}/${encodeURIComponent(scenarioName)}`, ); }; @@ -144,11 +142,7 @@ const Scene: React.FC = () => {
{scenario.name}
- {scenario.description && ( -
- {scenario.description} -
- )} +
今日: {scenario.count} diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx b/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx index 68db5ee5b..f1bd371c3 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx @@ -211,7 +211,7 @@ const ScenarioList: React.FC = () => { if (response) { // 处理webhook URL,使用工具函数构建完整地址 const webhookUrl = buildApiUrl( - response.textUrl?.fullUrl || `webhook/${taskId}` + response.textUrl?.fullUrl || `webhook/${taskId}`, ); setCurrentApiSettings({ @@ -286,7 +286,7 @@ const ScenarioList: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); // 生成操作菜单 @@ -537,7 +537,7 @@ const ScenarioList: React.FC = () => { {item.icon} {item.text}
- ) + ), )}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx b/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx index 386cf58a3..ee67b59c3 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx @@ -317,7 +317,7 @@ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.o onClick={() => handleCopy( codeExamples[activeLanguage as keyof typeof codeExamples], - "代码" + "代码", ) } className={style["copy-code-btn"]} diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx index 013b0218b..f75c6cc7c 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx @@ -146,7 +146,7 @@ export default function NewPlan() { ? error : isEdit ? "更新计划失败,请重试" - : "创建计划失败,请重试" + : "创建计划失败,请重试", ); } }; diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx index 40431a5e7..eaec26cea 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx @@ -105,10 +105,10 @@ const BasicSettings: React.FC = ({ const [accounts] = useState(generateRandomAccounts(50)); const [materials] = useState(generatePosterMaterials()); const [selectedAccounts, setSelectedAccounts] = useState( - formData.accounts?.length > 0 ? formData.accounts : [] + formData.accounts?.length > 0 ? formData.accounts : [], ); const [selectedMaterials, setSelectedMaterials] = useState( - formData.materials?.length > 0 ? formData.materials : [] + formData.materials?.length > 0 ? formData.materials : [], ); // showAllScenarios 默认为 true const [showAllScenarios, setShowAllScenarios] = useState(true); @@ -128,7 +128,7 @@ const BasicSettings: React.FC = ({ const [customTags, setCustomTags] = useState(formData.customTags || []); const [tips, setTips] = useState(formData.tips || ""); const [selectedScenarioTags, setSelectedScenarioTags] = useState( - formData.scenarioTags || [] + formData.scenarioTags || [], ); // 电话获客相关状态 @@ -140,10 +140,10 @@ const BasicSettings: React.FC = ({ // 群设置相关状态 const [weixinqunName, setWeixinqunName] = useState( - formData.weixinqunName || "" + formData.weixinqunName || "", ); const [weixinqunNotice, setWeixinqunNotice] = useState( - formData.weixinqunNotice || "" + formData.weixinqunNotice || "", ); // 新增:自定义海报相关状态 @@ -232,7 +232,7 @@ const BasicSettings: React.FC = ({ onChange({ ...formData, customTags: updatedCustomTags }); // 同时从选中标签中移除 const updatedSelectedTags = selectedScenarioTags.filter( - (t: string) => t !== tagId + (t: string) => t !== tagId, ); setSelectedScenarioTags(updatedSelectedTags); onChange({ @@ -292,12 +292,12 @@ const BasicSettings: React.FC = ({ // 账号多选切换 const handleAccountToggle = (account: Account) => { const isSelected = selectedAccounts.some( - (a: Account) => a.id === account.id + (a: Account) => a.id === account.id, ); let newSelected; if (isSelected) { newSelected = selectedAccounts.filter( - (a: Account) => a.id !== account.id + (a: Account) => a.id !== account.id, ); } else { newSelected = [...selectedAccounts, account]; @@ -362,7 +362,7 @@ const BasicSettings: React.FC = ({ const [orderUploaded, setOrderUploaded] = useState(false); const handleOrderFileUpload = async ( - event: React.ChangeEvent + event: React.ChangeEvent, ) => { const file = event.target.files?.[0]; if (file) { @@ -518,7 +518,7 @@ const BasicSettings: React.FC = ({
{[...materials, ...customPosters].map(material => { const isSelected = selectedMaterials.some( - m => m.id === material.id + m => m.id === material.id, ); const isCustom = material.id.startsWith("custom-"); return ( diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx index 42b89d4ff..01775fcc6 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx @@ -38,7 +38,7 @@ const FriendRequestSettings: React.FC = ({ const [isTemplateDialogOpen, setIsTemplateDialogOpen] = useState(false); const [hasWarnings, setHasWarnings] = useState(false); const [selectedDevices, setSelectedDevices] = useState( - formData.device || [] + formData.device || [], ); const [showRemarkTip, setShowRemarkTip] = useState(false); diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx index 29d7d1207..6ce86565f 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx @@ -125,7 +125,7 @@ const MessageSettings: React.FC = ({ const handleUpdateMessage = ( dayIndex: number, messageIndex: number, - updates: Partial + updates: Partial, ) => { const updatedPlans = [...dayPlans]; updatedPlans[dayIndex].messages[messageIndex] = { @@ -181,7 +181,7 @@ const MessageSettings: React.FC = ({ setSelectedGroupId(groupId); setIsGroupSelectOpen(false); message.success( - `已选择群组:${mockGroups.find(g => g.id === groupId)?.name}` + `已选择群组:${mockGroups.find(g => g.id === groupId)?.name}`, ); }; @@ -189,7 +189,7 @@ const MessageSettings: React.FC = ({ const triggerUpload = ( dayIdx: number, msgIdx: number, - type: "miniprogram" | "link" + type: "miniprogram" | "link", ) => { setUploadingDay(dayIdx); setUploadingMsgIdx(msgIdx); @@ -539,7 +539,7 @@ const MessageSettings: React.FC = ({ handleFileUpload( dayIndex, messageIndex, - message.type as any + message.type as any, ) } > diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts b/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts index 1f5a5150d..8c68b53fc 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts @@ -33,7 +33,7 @@ export function deleteScenario(id: string) { export function getPlanList( scenarioId: string, page: number = 1, - limit: number = 20 + limit: number = 20, ) { return request(`/api/scenarios/${scenarioId}/plans`, { page, limit }, "GET"); } @@ -214,7 +214,7 @@ export function deleteAutoLikeTask(taskId: string) { return request( `/api/workspace/auto-like/tasks/${taskId}`, undefined, - "DELETE" + "DELETE", ); } @@ -240,7 +240,7 @@ export function deleteGroupPushTask(taskId: string) { return request( `/api/workspace/group-push/tasks/${taskId}`, undefined, - "DELETE" + "DELETE", ); } @@ -266,7 +266,7 @@ export function deleteAutoGroupTask(taskId: string) { return request( `/api/workspace/auto-group/tasks/${taskId}`, undefined, - "DELETE" + "DELETE", ); } @@ -287,7 +287,7 @@ export function getAIAnalysisReport() { return request( "/api/workspace/ai-assistant/analysis-report", undefined, - "GET" + "GET", ); } @@ -379,6 +379,6 @@ export function markNotificationAsRead(notificationId: string) { return request( `/api/system/notifications/${notificationId}/read`, undefined, - "PUT" + "PUT", ); } diff --git a/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx b/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx index 73426064c..a0d88e7d8 100644 --- a/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx @@ -59,7 +59,7 @@ const mockTaskDetail: GroupTaskDetail = { nickname: `用户${mIndex + 1}`, wechatId: `wx_${mIndex}`, tags: [`标签${(mIndex % 3) + 1}`], - }) + }), ), })), createTime: "2024-11-20 19:04:14", @@ -169,10 +169,10 @@ const GroupCreationProgress: React.FC<{ }> = ({ taskDetail, onComplete }) => { const [groups, setGroups] = useState(taskDetail.groups); const [currentGroupIndex, setCurrentGroupIndex] = useState( - taskDetail.currentGroupIndex + taskDetail.currentGroupIndex, ); const [status, setStatus] = useState( - taskDetail.status + taskDetail.status, ); useEffect(() => { @@ -207,7 +207,7 @@ const GroupCreationProgress: React.FC<{ }; } return group; - }) + }), ); }; diff --git a/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx b/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx index 0ef828615..1b0a9094e 100644 --- a/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx @@ -149,8 +149,8 @@ const AutoGroupList: React.FC = () => { ...task, status: task.status === "running" ? "paused" : "running", } - : task - ) + : task, + ), ); Toast.show({ content: "状态已切换" }); }; @@ -160,7 +160,7 @@ const AutoGroupList: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); return ( diff --git a/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts b/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts index 23b89dc1c..7f828d56a 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts +++ b/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts @@ -9,7 +9,7 @@ import { // 获取自动点赞任务列表 export function fetchAutoLikeTasks( - params = { type: 1, page: 1, limit: 100 } + params = { type: 1, page: 1, limit: 100 }, ): Promise { return request("/v1/workbench/list", params, "GET"); } @@ -49,7 +49,7 @@ export function fetchLikeRecords( workbenchId: string, page: number = 1, limit: number = 20, - keyword?: string + keyword?: string, ): Promise> { const params: any = { workbenchId, diff --git a/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx b/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx index 33cbb242a..a4910269f 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx @@ -224,7 +224,7 @@ const AutoLike: React.FC = () => { // 过滤任务 const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); return ( diff --git a/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx b/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx index 4443b09b1..56cd6a5a6 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx @@ -82,7 +82,7 @@ const NewAutoLike: React.FC = () => { }); setAutoEnabled( (taskDetail as any).status === 1 || - (taskDetail as any).status === "running" + (taskDetail as any).status === "running", ); } } catch (error) { diff --git a/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts b/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts index 81a7c26be..6142fde86 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts +++ b/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts @@ -9,7 +9,7 @@ import { // 获取自动点赞任务列表 export function fetchAutoLikeTasks( - params = { type: 1, page: 1, limit: 100 } + params = { type: 1, page: 1, limit: 100 }, ): Promise { return request("/v1/workbench/list", params, "GET"); } @@ -49,7 +49,7 @@ export function fetchLikeRecords( workbenchId: string, page: number = 1, limit: number = 20, - keyword?: string + keyword?: string, ): Promise> { const params: any = { workbenchId, diff --git a/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts b/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts index 8692db6e0..0295e2f96 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts +++ b/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts @@ -40,12 +40,12 @@ export async function deleteGroupPushTask(id: string): Promise { export async function toggleGroupPushTask( id: string, - status: string + status: string, ): Promise { return request( `/v1/workspace/group-push/tasks/${id}/toggle`, { status }, - "POST" + "POST", ); } @@ -54,20 +54,20 @@ export async function copyGroupPushTask(id: string): Promise { } export async function createGroupPushTask( - taskData: Partial + taskData: Partial, ): Promise { return request("/v1/workspace/group-push/tasks", taskData, "POST"); } export async function updateGroupPushTask( id: string, - taskData: Partial + taskData: Partial, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`, taskData, "PUT"); } export async function getGroupPushTaskDetail( - id: string + id: string, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`); } diff --git a/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx b/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx index 7ab430491..ab2a9a0f2 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx @@ -226,7 +226,7 @@ const Detail: React.FC = () => {
diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx b/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx index c2d98bd84..34ba3f9ed 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx @@ -105,7 +105,7 @@ const BasicSettings: React.FC = ({ onChange={e => handleChange( "dailyPushCount", - Number.parseInt(e.target.value) || 1 + Number.parseInt(e.target.value) || 1, ) } style={{ width: 80, textAlign: "center" }} diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx b/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx index 2d35916e6..ebcb1b529 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx @@ -81,7 +81,7 @@ const ContentSelector: React.FC = ({ const [libraries] = useState(mockLibraries); const filteredLibraries = libraries.filter(library => - library.name.toLowerCase().includes(searchTerm.toLowerCase()) + library.name.toLowerCase().includes(searchTerm.toLowerCase()), ); const handleLibraryToggle = (library: ContentLibrary, checked: boolean) => { diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx b/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx index 016e76f6d..c75138458 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx @@ -101,7 +101,9 @@ const GroupSelector: React.FC = ({ const filteredGroups = groups.filter( group => group.name.toLowerCase().includes(searchTerm.toLowerCase()) || - group.serviceAccount.name.toLowerCase().includes(searchTerm.toLowerCase()) + group.serviceAccount.name + .toLowerCase() + .includes(searchTerm.toLowerCase()), ); const handleGroupToggle = (group: WechatGroup, checked: boolean) => { diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts b/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts index 7b4b9ecce..15384c115 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts +++ b/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts @@ -1,6 +1,6 @@ import request from "@/api/request"; export async function createGroupPushTask( - taskData: Partial + taskData: Partial, ): Promise { return request("/v1/workspace/group-push/tasks", taskData, "POST"); } diff --git a/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts b/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts index 8692db6e0..0295e2f96 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts +++ b/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts @@ -40,12 +40,12 @@ export async function deleteGroupPushTask(id: string): Promise { export async function toggleGroupPushTask( id: string, - status: string + status: string, ): Promise { return request( `/v1/workspace/group-push/tasks/${id}/toggle`, { status }, - "POST" + "POST", ); } @@ -54,20 +54,20 @@ export async function copyGroupPushTask(id: string): Promise { } export async function createGroupPushTask( - taskData: Partial + taskData: Partial, ): Promise { return request("/v1/workspace/group-push/tasks", taskData, "POST"); } export async function updateGroupPushTask( id: string, - taskData: Partial + taskData: Partial, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`, taskData, "PUT"); } export async function getGroupPushTaskDetail( - id: string + id: string, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`); } diff --git a/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx b/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx index daf048be0..7634c769f 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx @@ -97,7 +97,7 @@ const GroupPush: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); const getStatusColor = (status: number) => { @@ -361,7 +361,7 @@ const GroupPush: React.FC = () => {
diff --git a/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx b/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx index 5f3b6bff8..3b1f0ffd3 100644 --- a/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx +++ b/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx @@ -73,7 +73,7 @@ const MomentsSyncDetail: React.FC = () => { await request( "/v1/workbench/update-status", { id, status: newStatus }, - "POST" + "POST", ); setTask({ ...task, status: newStatus }); message.success(newStatus === 1 ? "任务已开启" : "任务已暂停"); diff --git a/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx b/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx index 0815f9548..15c5bcfc8 100644 --- a/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx +++ b/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx @@ -55,7 +55,7 @@ const MomentsSync: React.FC = () => { const res = await request( "/v1/workbench/list", { type: 2, page: 1, limit: 100 }, - "GET" + "GET", ); setTasks(res.list || []); } catch (e) { @@ -96,10 +96,10 @@ const MomentsSync: React.FC = () => { await request( "/v1/workbench/update-status", { id, status: newStatus }, - "POST" + "POST", ); setTasks(prev => - prev.map(t => (t.id === id ? { ...t, status: newStatus } : t)) + prev.map(t => (t.id === id ? { ...t, status: newStatus } : t)), ); message.success("操作成功"); } catch { @@ -108,7 +108,7 @@ const MomentsSync: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); // 菜单 diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts index 4c8b968b5..db35a752f 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts @@ -8,14 +8,14 @@ export const getTrafficDistributionDetail = (id: string) => { // 更新流量分发 export const updateTrafficDistribution = ( - data: TrafficDistributionFormData + data: TrafficDistributionFormData, ) => { return request("/v1/workbench/update", data, "POST"); }; // 创建流量分发 export const createTrafficDistribution = ( - data: TrafficDistributionFormData + data: TrafficDistributionFormData, ) => { return request("/v1/workbench/create", data, "POST"); }; diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx index 7ebd417fe..5155245cc 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx @@ -393,7 +393,7 @@ const TrafficDistributionForm: React.FC = () => { setSelectedPools(val => e.target.checked ? [...val, pool.id] - : val.filter(v => v !== pool.id) + : val.filter(v => v !== pool.id), ); }} /> diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts index 0a071cfd4..ceacca1c5 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts @@ -17,7 +17,7 @@ export function updateDistributionRule(data: any): Promise { // 暂停/启用计划 export function toggleDistributionRuleStatus( id: number, - status: 0 | 1 + status: 0 | 1, ): Promise { return request("/v1/workbench/update-status", { id, status }, "POST"); } diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx index d38018ab8..5e9fd5a81 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx @@ -115,7 +115,7 @@ const TrafficDistributionList: React.FC = () => { // 新增:Switch点击切换计划状态 const handleSwitchChange = async ( checked: boolean, - item: DistributionRule + item: DistributionRule, ) => { setMenuLoadingId(item.id); try { @@ -124,8 +124,8 @@ const TrafficDistributionList: React.FC = () => { // 本地只更新当前item的status,不刷新全列表 setList(prevList => prevList.map(rule => - rule.id === item.id ? { ...rule, status: checked ? 1 : 0 } : rule - ) + rule.id === item.id ? { ...rule, status: checked ? 1 : 0 } : rule, + ), ); } catch (e) { message.error("操作失败"); diff --git a/nkebao/src/router/config.ts b/nkebao/src/router/config.ts index 136e3cb59..1b560e874 100644 --- a/nkebao/src/router/config.ts +++ b/nkebao/src/router/config.ts @@ -14,7 +14,7 @@ export const routeGroups = { // 基础路由 basic: { name: "基础功能", - routes: ["/", "/login", "/scene", "/work", "/mine"], + routes: ["/", "/login", "/guide", "/scene", "/work", "/mine"], }, // 设备管理 @@ -109,6 +109,7 @@ export const routePermissions = { user: [ "/", "/login", + "/guide", "/scene", "/work", "/mine", @@ -136,6 +137,7 @@ export const routePermissions = { export const routeTitles: Record = { "/": "首页", "/login": "登录", + "/guide": "设备绑定引导", "/scene": "场景获客", "/work": "工作台", "/mine": "我的", @@ -159,7 +161,7 @@ export const getRouteTitle = (path: string): string => { // 检查路由权限 export const checkRoutePermission = ( path: string, - userRole: string = "user" + userRole: string = "user", ): boolean => { const allowedRoutes = routePermissions[userRole as keyof typeof routePermissions] || []; diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx index 4a02716eb..117681a75 100644 --- a/nkebao/src/router/index.tsx +++ b/nkebao/src/router/index.tsx @@ -16,7 +16,7 @@ Object.values(modules).forEach((mod: any) => { // 权限包装 function wrapWithPermission( - route: RouteObject & { auth?: boolean; requiredRole?: string } + route: RouteObject & { auth?: boolean; requiredRole?: string }, ) { if (route.auth) { return { diff --git a/nkebao/src/router/module/auth.tsx b/nkebao/src/router/module/auth.tsx index b89c7d5d0..8d74fd49f 100644 --- a/nkebao/src/router/module/auth.tsx +++ b/nkebao/src/router/module/auth.tsx @@ -1,4 +1,5 @@ import Login from "@/pages/login/login"; +import Guide from "@/pages/guide"; const authRoutes = [ { @@ -6,6 +7,11 @@ const authRoutes = [ element: , auth: false, // 不需要权限 }, + { + path: "/guide", + element: , + auth: true, // 需要登录权限 + }, ]; export default authRoutes; diff --git a/nkebao/src/store/createPersistStore.ts b/nkebao/src/store/createPersistStore.ts index cf2c80c71..79d7b6347 100644 --- a/nkebao/src/store/createPersistStore.ts +++ b/nkebao/src/store/createPersistStore.ts @@ -5,12 +5,12 @@ import { persist, PersistOptions } from "zustand/middleware"; export function createPersistStore( createState: (set: any, get: any) => T, name: string, - partialize?: (state: T) => Partial + partialize?: (state: T) => Partial, ) { return create()( persist(createState, { name, partialize, - } as PersistOptions) + } as PersistOptions), ); } diff --git a/nkebao/src/store/module/user.ts b/nkebao/src/store/module/user.ts index 6ecd32f74..bee4f4c6e 100644 --- a/nkebao/src/store/module/user.ts +++ b/nkebao/src/store/module/user.ts @@ -15,6 +15,7 @@ export interface User { updateTime: string | null; lastLoginIp: string; lastLoginTime: number; + deviceTotal: number; // 设备总数 } interface UserState { @@ -24,7 +25,7 @@ interface UserState { setUser: (user: User) => void; setToken: (token: string) => void; clearUser: () => void; - login: (token: string, userInfo: User) => void; + login: (token: string, userInfo: User, deviceTotal: number) => void; logout: () => void; } @@ -36,7 +37,7 @@ export const useUserStore = createPersistStore( setUser: user => set({ user, isLoggedIn: true }), setToken: token => set({ token }), clearUser: () => set({ user: null, token: null, isLoggedIn: false }), - login: (token, userInfo) => { + login: (token, userInfo, deviceTotal) => { // 只将token存储到localStorage localStorage.setItem("token", token); @@ -56,6 +57,7 @@ export const useUserStore = createPersistStore( updateTime: userInfo.updateTime, lastLoginIp: userInfo.lastLoginIp, lastLoginTime: userInfo.lastLoginTime, + deviceTotal: deviceTotal, }; set({ user, token, isLoggedIn: true }); }, @@ -70,5 +72,5 @@ export const useUserStore = createPersistStore( user: state.user, token: state.token, isLoggedIn: state.isLoggedIn, - }) + }), ); diff --git a/nkebao/src/utils/common.ts b/nkebao/src/utils/common.ts index 3c54d78e6..2dd200f89 100644 --- a/nkebao/src/utils/common.ts +++ b/nkebao/src/utils/common.ts @@ -12,7 +12,7 @@ export const comfirm = ( title?: string; cancelText?: string; confirmText?: string; - } + }, ): Promise => { return new Promise((resolve, reject) => { Modal.show({