FEAT => 本次更新项目为:
存客宝设备绑定引导页
This commit is contained in:
BIN
nkebao/public/logo.png
Normal file
BIN
nkebao/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 488 KiB |
@@ -1,7 +1,7 @@
|
||||
.guideContainer {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20px;
|
||||
height: 100vh;
|
||||
background: var(--primary-color);
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
@@ -26,7 +26,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
.loadingText {
|
||||
@@ -38,42 +38,40 @@
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.iconContainer {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 20px;
|
||||
backdrop-filter: blur(10px);
|
||||
margin: 0 auto 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.warningIcon {
|
||||
font-size: 40px;
|
||||
color: #ffd700;
|
||||
.logo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
max-width: 300px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
max-width: 280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -81,34 +79,36 @@
|
||||
flex: 1;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.deviceStatus {
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.statusCard {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
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: 50px;
|
||||
height: 50px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 12px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: var(--primary-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
margin-right: 12px;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.statusInfo {
|
||||
@@ -116,69 +116,69 @@
|
||||
}
|
||||
|
||||
.statusTitle {
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.statusValue {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.deviceCount {
|
||||
color: #667eea;
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.guideSteps {
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stepsTitle {
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 12px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.stepList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.stepItem {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
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(-2px);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.stepNumber {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
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: 14px;
|
||||
margin-right: 16px;
|
||||
font-size: 12px;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -187,23 +187,23 @@
|
||||
}
|
||||
|
||||
.stepTitle {
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.stepDesc {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.tips {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
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);
|
||||
}
|
||||
@@ -211,24 +211,24 @@
|
||||
.tipsTitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tipsIcon {
|
||||
color: #ff6b6b;
|
||||
margin-right: 8px;
|
||||
font-size: 18px;
|
||||
margin-right: 6px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tipsContent {
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@@ -237,41 +237,41 @@
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
margin-top: 16px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.primaryButton {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
border-radius: 8px;
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
|
||||
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 2px 10px rgba(102, 126, 234, 0.4);
|
||||
box-shadow: 0 1px 6px rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.buttonIcon {
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
margin-left: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.secondaryButton {
|
||||
border: 2px solid rgba(255, 255, 255, 0.8);
|
||||
border-radius: 12px;
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
border-radius: 8px;
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
background: transparent;
|
||||
@@ -285,27 +285,27 @@
|
||||
// 响应式设计
|
||||
@media (max-width: 480px) {
|
||||
.guideContainer {
|
||||
padding: 16px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.statusCard {
|
||||
padding: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.stepItem {
|
||||
padding: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
padding: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
@@ -325,7 +325,7 @@
|
||||
.deviceStatus,
|
||||
.guideSteps,
|
||||
.tips {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.guideSteps {
|
||||
@@ -337,5 +337,5 @@
|
||||
}
|
||||
|
||||
.footer {
|
||||
animation: fadeInUp 0.6s ease-out 0.3s both;
|
||||
animation: fadeInUp 0.5s ease-out 0.3s both;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, useCallback, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button, Toast } from "antd-mobile";
|
||||
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 { useUserStore } from "@/store/module/user";
|
||||
import { getDashboard } from "@/pages/mobile/home/api";
|
||||
import { fetchDeviceQRCode, addDeviceByImei } from "@/api/devices";
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
const Guide: React.FC = () => {
|
||||
@@ -16,21 +18,29 @@ const Guide: React.FC = () => {
|
||||
const { user } = useUserStore();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [deviceCount, setDeviceCount] = useState(0);
|
||||
const [hasDevices, setHasDevices] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
checkDeviceStatus();
|
||||
}, []);
|
||||
// 添加设备弹窗状态
|
||||
const [addVisible, setAddVisible] = useState(false);
|
||||
const [addTab, setAddTab] = useState("scan");
|
||||
const [qrLoading, setQrLoading] = useState(false);
|
||||
const [qrCode, setQrCode] = useState<string | null>(null);
|
||||
const [imei, setImei] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
const [addLoading, setAddLoading] = useState(false);
|
||||
|
||||
// 轮询监听相关
|
||||
const [isPolling, setIsPolling] = useState(false);
|
||||
const pollingRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const initialDeviceCountRef = useRef(0);
|
||||
|
||||
// 检查设备绑定状态
|
||||
const checkDeviceStatus = async () => {
|
||||
const checkDeviceStatus = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const dashboardData = await getDashboard();
|
||||
const deviceNum = dashboardData?.deviceNum || 0;
|
||||
|
||||
setDeviceCount(deviceNum);
|
||||
setHasDevices(deviceNum > 0);
|
||||
|
||||
// 如果已有设备,直接跳转到首页
|
||||
if (deviceNum > 0) {
|
||||
@@ -46,16 +56,116 @@ const Guide: React.FC = () => {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
checkDeviceStatus();
|
||||
}, [checkDeviceStatus]);
|
||||
|
||||
// 开始轮询监听设备状态
|
||||
const startPolling = useCallback(() => {
|
||||
if (isPolling) return;
|
||||
|
||||
setIsPolling(true);
|
||||
initialDeviceCountRef.current = deviceCount;
|
||||
|
||||
const pollDeviceStatus = async () => {
|
||||
try {
|
||||
const dashboardData = await getDashboard();
|
||||
const currentDeviceCount = dashboardData?.deviceNum || 0;
|
||||
|
||||
// 如果设备数量增加了,说明有新设备添加成功
|
||||
if (currentDeviceCount > initialDeviceCountRef.current) {
|
||||
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, deviceCount]);
|
||||
|
||||
// 停止轮询
|
||||
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 = () => {
|
||||
navigate("/devices");
|
||||
handleGetQr();
|
||||
setAddVisible(true);
|
||||
};
|
||||
|
||||
// 跳转到首页(跳过引导)
|
||||
const handleSkipGuide = () => {
|
||||
navigate("/");
|
||||
// 手动添加设备
|
||||
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) {
|
||||
@@ -74,12 +184,10 @@ const Guide: React.FC = () => {
|
||||
{/* 头部区域 */}
|
||||
<div className={styles.header}>
|
||||
<div className={styles.iconContainer}>
|
||||
<ExclamationCircleOutlined className={styles.warningIcon} />
|
||||
<img src="/logo.png" alt="存客宝" className={styles.logo} />
|
||||
</div>
|
||||
<h1 className={styles.title}>欢迎使用存客宝</h1>
|
||||
<p className={styles.subtitle}>
|
||||
为了更好的使用体验,请先绑定您的设备
|
||||
</p>
|
||||
<p className={styles.subtitle}>请先绑定设备以获得完整功能体验</p>
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
@@ -92,7 +200,7 @@ const Guide: React.FC = () => {
|
||||
<div className={styles.statusInfo}>
|
||||
<div className={styles.statusTitle}>设备绑定状态</div>
|
||||
<div className={styles.statusValue}>
|
||||
已绑定设备:
|
||||
已绑定:
|
||||
<span className={styles.deviceCount}>{deviceCount}</span> 台
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,14 +208,14 @@ const Guide: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className={styles.guideSteps}>
|
||||
<h2 className={styles.stepsTitle}>绑定设备步骤</h2>
|
||||
<h2 className={styles.stepsTitle}>绑定步骤</h2>
|
||||
<div className={styles.stepList}>
|
||||
<div className={styles.stepItem}>
|
||||
<div className={styles.stepNumber}>1</div>
|
||||
<div className={styles.stepContent}>
|
||||
<div className={styles.stepTitle}>准备设备</div>
|
||||
<div className={styles.stepDesc}>
|
||||
确保您的手机设备已安装存客宝应用
|
||||
确保手机已安装存客宝应用
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,9 +223,7 @@ const Guide: React.FC = () => {
|
||||
<div className={styles.stepNumber}>2</div>
|
||||
<div className={styles.stepContent}>
|
||||
<div className={styles.stepTitle}>扫描二维码</div>
|
||||
<div className={styles.stepDesc}>
|
||||
在设备管理页面扫描二维码绑定设备
|
||||
</div>
|
||||
<div className={styles.stepDesc}>在设备管理页面扫描绑定</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.stepItem}>
|
||||
@@ -138,7 +244,7 @@ const Guide: React.FC = () => {
|
||||
温馨提示
|
||||
</div>
|
||||
<div className={styles.tipsContent}>
|
||||
<p>• 绑定设备后可以享受更完整的功能体验</p>
|
||||
<p>• 绑定设备后可享受完整功能体验</p>
|
||||
<p>• 每个账号最多可绑定10台设备</p>
|
||||
<p>• 如需帮助请联系客服</p>
|
||||
</div>
|
||||
@@ -157,18 +263,86 @@ const Guide: React.FC = () => {
|
||||
立即绑定设备
|
||||
<ArrowRightOutlined className={styles.buttonIcon} />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
block
|
||||
fill="outline"
|
||||
size="large"
|
||||
className={styles.secondaryButton}
|
||||
onClick={handleSkipGuide}
|
||||
>
|
||||
稍后绑定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 添加设备弹窗 */}
|
||||
<Popup
|
||||
visible={addVisible}
|
||||
onMaskClick={handleClosePopup}
|
||||
bodyStyle={{
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
minHeight: 320,
|
||||
}}
|
||||
>
|
||||
<div style={{ padding: 20 }}>
|
||||
<Tabs
|
||||
activeKey={addTab}
|
||||
onChange={setAddTab}
|
||||
style={{ marginBottom: 16 }}
|
||||
>
|
||||
<Tabs.Tab title="扫码添加" key="scan" />
|
||||
<Tabs.Tab title="手动添加" key="manual" />
|
||||
</Tabs>
|
||||
{addTab === "scan" && (
|
||||
<div style={{ textAlign: "center", minHeight: 200 }}>
|
||||
<Button color="primary" onClick={handleGetQr} loading={qrLoading}>
|
||||
<QrcodeOutlined />
|
||||
获取二维码
|
||||
</Button>
|
||||
{qrCode && (
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<img
|
||||
src={qrCode}
|
||||
alt="二维码"
|
||||
style={{
|
||||
width: 180,
|
||||
height: 180,
|
||||
background: "#f5f5f5",
|
||||
borderRadius: 8,
|
||||
margin: "0 auto",
|
||||
}}
|
||||
/>
|
||||
<div style={{ color: "#888", fontSize: 12, marginTop: 8 }}>
|
||||
请用手机扫码添加设备
|
||||
</div>
|
||||
{isPolling && (
|
||||
<div
|
||||
style={{ color: "#1890ff", fontSize: 12, marginTop: 8 }}
|
||||
>
|
||||
正在监听设备添加状态...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{addTab === "manual" && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||||
<Input
|
||||
placeholder="设备名称"
|
||||
value={name}
|
||||
onChange={val => setName(val)}
|
||||
clearable
|
||||
/>
|
||||
<Input
|
||||
placeholder="设备IMEI"
|
||||
value={imei}
|
||||
onChange={val => setImei(val)}
|
||||
clearable
|
||||
/>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleAddDevice}
|
||||
loading={addLoading}
|
||||
>
|
||||
添加
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popup>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ const Login: React.FC = () => {
|
||||
try {
|
||||
const dashboardData = await getDashboard();
|
||||
const deviceNum = dashboardData?.deviceNum || 0;
|
||||
|
||||
console.log(deviceNum, "deviceNum");
|
||||
// 如果没有绑定设备,跳转到引导页面
|
||||
if (deviceNum === 0) {
|
||||
navigate("/guide");
|
||||
|
||||
@@ -46,9 +46,7 @@ const Scene: 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,
|
||||
@@ -144,11 +142,7 @@ const Scene: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className={style["card-title"]}>{scenario.name}</div>
|
||||
{scenario.description && (
|
||||
<div className={style["card-desc"]}>
|
||||
{scenario.description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={style["card-stats"]}>
|
||||
<span className={style["card-count"]}>
|
||||
今日: {scenario.count}
|
||||
|
||||
Reference in New Issue
Block a user