feat: 处理语法校验
This commit is contained in:
@@ -65,6 +65,21 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 先声明handleLogout,避免useEffect依赖提前引用
|
||||
const handleLogout = () => {
|
||||
// 先清除所有认证相关的状态
|
||||
safeLocalStorage.removeItem("token");
|
||||
safeLocalStorage.removeItem("token_expired");
|
||||
safeLocalStorage.removeItem("s2_accountId");
|
||||
safeLocalStorage.removeItem("userInfo");
|
||||
safeLocalStorage.removeItem("user");
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
// 跳转到登录页面
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
// 检查token是否过期
|
||||
const isTokenExpired = (): boolean => {
|
||||
const tokenExpired = safeLocalStorage.getItem("token_expired");
|
||||
@@ -184,22 +199,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
||||
}, 60000); // 每分钟检查一次
|
||||
|
||||
return () => clearInterval(checkTokenInterval);
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const handleLogout = () => {
|
||||
// 先清除所有认证相关的状态
|
||||
safeLocalStorage.removeItem("token");
|
||||
safeLocalStorage.removeItem("token_expired");
|
||||
safeLocalStorage.removeItem("s2_accountId");
|
||||
safeLocalStorage.removeItem("userInfo");
|
||||
safeLocalStorage.removeItem("user");
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
|
||||
// 跳转到登录页面
|
||||
navigate('/login');
|
||||
};
|
||||
}, [isAuthenticated, handleLogout]);
|
||||
|
||||
const login = (newToken: string, userData: User) => {
|
||||
safeLocalStorage.setItem("token", newToken);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
import BackButton from '@/components/BackButton';
|
||||
import { useSimpleBack } from '@/hooks/useBackNavigation';
|
||||
import { ChevronLeft, Smartphone, Battery, Wifi, MessageCircle, Users, Settings, History, RefreshCw, Loader2 } from 'lucide-react';
|
||||
import { Smartphone, Battery, Wifi, MessageCircle, Users, Settings, History, RefreshCw, Loader2 } from 'lucide-react';
|
||||
import { devicesApi, fetchDeviceDetail, fetchDeviceRelatedAccounts, fetchDeviceHandleLogs, updateDeviceTaskConfig } from '@/api/devices';
|
||||
import { useToast } from '@/components/ui/toast';
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function DeviceDetail() {
|
||||
momentsSync: false,
|
||||
aiChat: false
|
||||
});
|
||||
const [tabChangeLoading, setTabChangeLoading] = useState(false);
|
||||
|
||||
const [accountPage, setAccountPage] = useState(1);
|
||||
const [hasMoreAccounts, setHasMoreAccounts] = useState(true);
|
||||
const accountsPerPage = 10;
|
||||
@@ -277,7 +277,7 @@ export default function DeviceDetail() {
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [hasMoreLogs, logsLoading]);
|
||||
}, [hasMoreLogs, logsLoading, loadMoreLogs]);
|
||||
|
||||
// 获取任务配置
|
||||
const fetchTaskConfig = async () => {
|
||||
@@ -305,7 +305,6 @@ export default function DeviceDetail() {
|
||||
// 标签页切换处理
|
||||
const handleTabChange = (value: string) => {
|
||||
setActiveTab(value);
|
||||
setTabChangeLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
if (value === "accounts" && device && (!device.wechatAccounts || device.wechatAccounts.length === 0)) {
|
||||
@@ -315,7 +314,6 @@ export default function DeviceDetail() {
|
||||
setHasMoreLogs(true);
|
||||
fetchHandleLogs();
|
||||
}
|
||||
setTabChangeLoading(false);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
@@ -421,7 +419,7 @@ export default function DeviceDetail() {
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [hasMoreAccounts, accountsLoading]);
|
||||
}, [hasMoreAccounts, accountsLoading, loadMoreAccounts]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Plus, Search, RefreshCw, QrCode, Smartphone, Loader2, AlertTriangle, Trash2, X } from 'lucide-react';
|
||||
import { Plus, Search, RefreshCw, QrCode, Loader2, AlertTriangle, X } from 'lucide-react';
|
||||
import { devicesApi } from '@/api';
|
||||
import { useToast } from '@/components/ui/toast';
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
@@ -27,6 +27,7 @@ export default function Devices() {
|
||||
});
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("all");
|
||||
// 恢复分页功能
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [selectedDeviceId, setSelectedDeviceId] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -374,9 +374,9 @@ export default function Login() {
|
||||
className="text-sm text-gray-600 leading-relaxed cursor-pointer"
|
||||
>
|
||||
我已阅读并同意
|
||||
<a href="#" className="text-blue-500 hover:text-blue-600 mx-1">《存客宝用户协议》</a>
|
||||
<button type="button" className="text-blue-500 hover:text-blue-600 mx-1">《存客宝用户协议》</button>
|
||||
和
|
||||
<a href="#" className="text-blue-500 hover:text-blue-600 mx-1">《隐私政策》</a>
|
||||
<button type="button" className="text-blue-500 hover:text-blue-600 mx-1">《隐私政策》</button>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { Check, Settings } from 'lucide-react';
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
@@ -71,7 +71,7 @@ export default function NewPlan() {
|
||||
const { toast } = useToast();
|
||||
const [currentStep, setCurrentStep] = useState(1);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingScenes, setLoadingScenes] = useState(false);
|
||||
|
||||
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
planName: "",
|
||||
@@ -164,8 +164,6 @@ export default function NewPlan() {
|
||||
|
||||
// 基础设置步骤
|
||||
const BasicSettings = () => {
|
||||
const selectedScenario = scenarioOptions.find(s => s.id === formData.sceneId);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
|
||||
@@ -28,7 +28,6 @@ interface ScenarioData {
|
||||
export default function ScenarioDetail() {
|
||||
const { scenarioId } = useParams<{ scenarioId: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { goBack } = useSimpleBack('/scenarios');
|
||||
const [scenario, setScenario] = useState<ScenarioData | null>(null);
|
||||
const [plans, setPlans] = useState<Plan[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function GongzhonghaoScenario() {
|
||||
setError("");
|
||||
fetchPlanList(channel, page, pageSize)
|
||||
.then((res) => {
|
||||
if (res.code === 200 && Array.isArray(res.data?.list)) {
|
||||
if (res.code === 200 && res.data && Array.isArray(res.data.list)) {
|
||||
setTasks(res.data.list);
|
||||
setTotal(res.data.total || 0);
|
||||
} else {
|
||||
|
||||
@@ -12,12 +12,8 @@ import {
|
||||
Info,
|
||||
UserPlus,
|
||||
Search,
|
||||
Filter,
|
||||
Tag,
|
||||
ChevronRight,
|
||||
Loader2,
|
||||
AlertCircle,
|
||||
ArrowRightLeft
|
||||
Loader2
|
||||
} from 'lucide-react';
|
||||
import { useWechatAccount } from '@/contexts/WechatAccountContext';
|
||||
import { fetchWechatAccountSummary, fetchWechatFriends, fetchWechatFriendDetail } from '@/api/wechat-accounts';
|
||||
@@ -106,7 +102,6 @@ export default function WechatAccountDetail() {
|
||||
const [isFriendsEmpty, setIsFriendsEmpty] = useState(false);
|
||||
const friendsObserver = useRef<IntersectionObserver | null>(null);
|
||||
const friendsLoadingRef = useRef<HTMLDivElement | null>(null);
|
||||
const friendsContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// 如果没有账号数据,返回上一页
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Search, RefreshCw, ArrowRightLeft, AlertCircle, Loader2 } from 'lucide-react';
|
||||
import { Search, RefreshCw, Loader2 } from 'lucide-react';
|
||||
import { fetchWechatAccountList, transformWechatAccount } from '@/api/wechat-accounts';
|
||||
import { useToast } from '@/components/ui/toast';
|
||||
import { useWechatAccount } from '@/contexts/WechatAccountContext';
|
||||
@@ -119,7 +119,6 @@ export default function WechatAccounts() {
|
||||
}
|
||||
};
|
||||
|
||||
const filteredAccounts = accounts;
|
||||
const totalPages = Math.ceil(totalAccounts / accountsPerPage);
|
||||
|
||||
const handleTransferFriends = (account: WechatAccount) => {
|
||||
@@ -259,7 +258,7 @@ export default function WechatAccounts() {
|
||||
handleTransferFriends(account);
|
||||
}}
|
||||
>
|
||||
<ArrowRightLeft className="h-4 w-4" />
|
||||
{/* ArrowRightLeft className="h-4 w-4" */}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-500 space-y-1">
|
||||
@@ -274,7 +273,7 @@ export default function WechatAccounts() {
|
||||
<span>今日可添加:</span>
|
||||
<span className="font-medium">{account.remainingAdds}</span>
|
||||
<div className="relative group">
|
||||
<AlertCircle className="h-4 w-4 text-gray-400" />
|
||||
{/* AlertCircle className="h-4 w-4 text-gray-400" */}
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 text-xs bg-gray-800 text-white rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
|
||||
每日最多添加 {account.maxDailyAdds} 个好友
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import { Plus, Filter, Search, RefreshCw, MoreVertical, Clock, Edit, Trash2, Eye, Copy } from 'lucide-react';
|
||||
import { Plus, Search, RefreshCw, MoreVertical, Clock, Edit, Trash2, Eye, Copy } from 'lucide-react';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -244,7 +244,7 @@ export default function MomentsSync() {
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" size="icon" onClick={handleSearch}>
|
||||
<Filter className="h-4 w-4" />
|
||||
{/* <Filter className="h-4 w-4" /> */}
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" onClick={fetchTasks} disabled={isLoading}>
|
||||
<RefreshCw className={`h-4 w-4 ${isLoading ? 'animate-spin' : ''}`} />
|
||||
|
||||
11850
nkebao/yarn.lock
Normal file
11850
nkebao/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user