表名同步
This commit is contained in:
@@ -1,81 +1,109 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
import { useState, useEffect } from "react"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Search, RefreshCw } from "lucide-react"
|
||||
import { Search, RefreshCw, Loader2 } from "lucide-react"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { api } from "@/lib/api"
|
||||
import { showToast } from "@/lib/toast"
|
||||
|
||||
interface ServerDevice {
|
||||
id: number
|
||||
imei: string
|
||||
memo: string
|
||||
wechatId: string
|
||||
alive: number
|
||||
totalFriend: number
|
||||
}
|
||||
|
||||
interface Device {
|
||||
id: string
|
||||
id: number
|
||||
name: string
|
||||
imei: string
|
||||
wxid: string
|
||||
status: "online" | "offline"
|
||||
usedInPlans: number
|
||||
totalFriend: number
|
||||
}
|
||||
|
||||
interface DeviceSelectionDialogProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
selectedDevices: string[]
|
||||
onSelect: (devices: string[]) => void
|
||||
selectedDevices: number[]
|
||||
onSelect: (devices: number[]) => void
|
||||
}
|
||||
|
||||
export function DeviceSelectionDialog({ open, onOpenChange, selectedDevices, onSelect }: DeviceSelectionDialogProps) {
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [statusFilter, setStatusFilter] = useState("all")
|
||||
const [devices, setDevices] = useState<Device[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [tempSelectedDevices, setTempSelectedDevices] = useState<number[]>(selectedDevices)
|
||||
|
||||
// 模拟设备数据
|
||||
const devices: Device[] = [
|
||||
{
|
||||
id: "1",
|
||||
name: "设备 1",
|
||||
imei: "IMEI-radz6ewal",
|
||||
wxid: "wxid_98179ujy",
|
||||
status: "offline",
|
||||
usedInPlans: 0,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "设备 2",
|
||||
imei: "IMEI-i6iszi6d",
|
||||
wxid: "wxid_viqnaic8",
|
||||
status: "online",
|
||||
usedInPlans: 2,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "设备 3",
|
||||
imei: "IMEI-01z2izj97",
|
||||
wxid: "wxid_9sb23gxr",
|
||||
status: "online",
|
||||
usedInPlans: 2,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "设备 4",
|
||||
imei: "IMEI-x6o9rpcr0",
|
||||
wxid: "wxid_k0gxzbit",
|
||||
status: "online",
|
||||
usedInPlans: 1,
|
||||
},
|
||||
]
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setTempSelectedDevices(selectedDevices)
|
||||
fetchDevices()
|
||||
}
|
||||
}, [open, selectedDevices])
|
||||
|
||||
const filteredDevices = devices.filter((device) => {
|
||||
const matchesSearch =
|
||||
const fetchDevices = async () => {
|
||||
const loadingToast = showToast("正在加载设备列表...", "loading", true);
|
||||
try {
|
||||
setLoading(true)
|
||||
const response = await api.get<{code: number, msg: string, data: {list: ServerDevice[], total: number}}>('/v1/devices?page=1&limit=100')
|
||||
|
||||
if (response.code === 200 && response.data.list) {
|
||||
const transformedDevices: Device[] = response.data.list.map(device => ({
|
||||
id: device.id,
|
||||
name: device.memo || device.imei || '',
|
||||
imei: device.imei || '',
|
||||
wxid: device.wechatId || '',
|
||||
status: device.alive === 1 ? "online" : "offline",
|
||||
totalFriend: device.totalFriend || 0
|
||||
}))
|
||||
setDevices(transformedDevices)
|
||||
} else {
|
||||
showToast(response.msg || "获取设备列表失败", "error")
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('获取设备列表失败:', error)
|
||||
showToast(error?.message || "请检查网络连接", "error")
|
||||
} finally {
|
||||
loadingToast.remove();
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
fetchDevices()
|
||||
}
|
||||
|
||||
const handleDeviceToggle = (deviceId: number, checked: boolean) => {
|
||||
if (checked) {
|
||||
setTempSelectedDevices(prev => [...prev, deviceId])
|
||||
} else {
|
||||
setTempSelectedDevices(prev => prev.filter(id => id !== deviceId))
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
onSelect(tempSelectedDevices)
|
||||
onOpenChange(false)
|
||||
}
|
||||
|
||||
// 过滤设备列表
|
||||
const filteredDevices = devices.filter(device => {
|
||||
const matchesSearch = searchQuery === "" ||
|
||||
device.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
device.imei.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
device.wxid.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
|
||||
const matchesStatus =
|
||||
statusFilter === "all" ||
|
||||
(statusFilter === "online" && device.status === "online") ||
|
||||
(statusFilter === "offline" && device.status === "offline")
|
||||
|
||||
const matchesStatus = statusFilter === "all" || device.status === statusFilter
|
||||
|
||||
return matchesSearch && matchesStatus
|
||||
})
|
||||
@@ -107,38 +135,51 @@ export function DeviceSelectionDialog({ open, onOpenChange, selectedDevices, onS
|
||||
<SelectItem value="offline">离线</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="icon">
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
<Button variant="outline" size="icon" onClick={handleRefresh} disabled={loading}>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="flex-1 -mx-6 px-6">
|
||||
<RadioGroup value={selectedDevices[0]} onValueChange={(value) => onSelect([value])}>
|
||||
{filteredDevices.map((device) => (
|
||||
<label
|
||||
key={device.id}
|
||||
className="flex items-start space-x-3 p-4 rounded-lg hover:bg-gray-50 cursor-pointer"
|
||||
>
|
||||
<RadioGroupItem value={device.id} id={device.id} className="mt-1" />
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">{device.name}</span>
|
||||
<Badge variant={device.status === "online" ? "success" : "secondary"}>
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
<ScrollArea className="flex-1">
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center py-8">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-blue-500" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{filteredDevices.map((device) => (
|
||||
<label
|
||||
key={device.id}
|
||||
className="flex items-center space-x-3 p-4 rounded-lg hover:bg-gray-50 cursor-pointer"
|
||||
style={{paddingLeft: '0px',paddingRight: '0px'}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={tempSelectedDevices.includes(device.id)}
|
||||
onCheckedChange={(checked) => handleDeviceToggle(device.id, checked as boolean)}
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">{device.name}</span>
|
||||
<Badge variant={device.status === "online" ? "default" : "secondary"}>
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 mt-1">
|
||||
<div>IMEI: {device.imei || '--'}</div>
|
||||
<div>微信号: {device.wxid || '--'}</div>
|
||||
<div>好友数: {device.totalFriend}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 mt-1">
|
||||
<div>IMEI: {device.imei}</div>
|
||||
<div>微信号: {device.wxid}</div>
|
||||
</div>
|
||||
{device.usedInPlans > 0 && (
|
||||
<div className="text-sm text-orange-500 mt-1">已用于 {device.usedInPlans} 个计划</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
|
||||
<DialogFooter className="mt-4 flex gap-4 -mx-6 px-6">
|
||||
<Button className="flex-1" onClick={handleConfirm}>确认</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { api, ApiResponse } from "@/lib/api"
|
||||
import { showToast } from "@/lib/toast"
|
||||
import { DeviceSelectionDialog } from "../auto-like/components/device-selection-dialog"
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -26,7 +27,7 @@ import {
|
||||
interface SyncTask {
|
||||
id: string
|
||||
name: string
|
||||
status: "running" | "paused"
|
||||
status: number // 修改为数字类型:1-运行中,0-暂停
|
||||
deviceCount: number
|
||||
contentLib: string
|
||||
syncCount: number
|
||||
@@ -47,6 +48,8 @@ export default function MomentsSyncPage() {
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(10)
|
||||
const [total, setTotal] = useState(0)
|
||||
const [deviceDialogOpen, setDeviceDialogOpen] = useState(false)
|
||||
const [selectedDevices, setSelectedDevices] = useState<number[]>([])
|
||||
|
||||
// 获取任务列表
|
||||
const fetchTasks = async () => {
|
||||
@@ -91,13 +94,13 @@ export default function MomentsSyncPage() {
|
||||
}
|
||||
|
||||
// 切换任务状态
|
||||
const toggleTaskStatus = async (taskId: string, currentStatus: "running" | "paused") => {
|
||||
const toggleTaskStatus = async (taskId: string, currentStatus: number) => {
|
||||
const loadingToast = showToast("正在更新任务状态...", "loading", true);
|
||||
try {
|
||||
const newStatus = currentStatus === "running" ? "paused" : "running"
|
||||
const newStatus = currentStatus === 1 ? 0 : 1
|
||||
const response = await api.post<ApiResponse>('/v1/workbench/update-status', {
|
||||
id: taskId,
|
||||
status: newStatus === "running" ? 1 : 0
|
||||
status: newStatus
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
@@ -107,7 +110,7 @@ export default function MomentsSyncPage() {
|
||||
)
|
||||
)
|
||||
loadingToast.remove();
|
||||
showToast(`任务已${newStatus === "running" ? "启用" : "暂停"}`, "success")
|
||||
showToast(`任务已${newStatus === 1 ? "启用" : "暂停"}`, "success")
|
||||
} else {
|
||||
loadingToast.remove();
|
||||
showToast(response.msg || "操作失败", "error")
|
||||
@@ -184,6 +187,11 @@ export default function MomentsSyncPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理设备选择
|
||||
const handleDeviceSelect = (devices: number[]) => {
|
||||
setSelectedDevices(devices)
|
||||
}
|
||||
|
||||
// 过滤任务
|
||||
const filteredTasks = tasks.filter(
|
||||
(task) => task.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
@@ -322,44 +330,36 @@ export default function MomentsSyncPage() {
|
||||
</div>
|
||||
|
||||
{/* 分页组件 */}
|
||||
<div className="flex justify-between items-center mt-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm text-gray-500">每页显示</span>
|
||||
<select
|
||||
className="border rounded px-2 py-1 text-sm"
|
||||
value={pageSize}
|
||||
onChange={(e) => handlePageSizeChange(Number(e.target.value))}
|
||||
>
|
||||
<option value={10}>10</option>
|
||||
<option value={20}>20</option>
|
||||
<option value={50}>50</option>
|
||||
</select>
|
||||
<span className="text-sm text-gray-500">条</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
上一页
|
||||
</Button>
|
||||
<span className="text-sm text-gray-500">
|
||||
第 {currentPage} 页 / 共 {Math.ceil(total / pageSize)} 页
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
disabled={currentPage >= Math.ceil(total / pageSize)}
|
||||
>
|
||||
下一页
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex justify-center items-center mt-4 space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
上一页
|
||||
</Button>
|
||||
<span className="text-sm">
|
||||
第 {currentPage} 页 共 {Math.ceil(total / pageSize)} 页
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
disabled={currentPage >= Math.ceil(total / pageSize)}
|
||||
>
|
||||
下一页
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<DeviceSelectionDialog
|
||||
open={deviceDialogOpen}
|
||||
onOpenChange={setDeviceDialogOpen}
|
||||
selectedDevices={selectedDevices}
|
||||
onSelect={handleDeviceSelect}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 删除确认对话框 */}
|
||||
|
||||
@@ -133,7 +133,7 @@ class Device extends Controller
|
||||
$order = Request::param('order', 'desc');
|
||||
|
||||
// 添加公司ID过滤条件
|
||||
$where['d.companyId'] = $userInfo['companyId'];
|
||||
$where['d.tenantId'] = $userInfo['companyId'];
|
||||
|
||||
// 根据用户管理员状态调整查询条件
|
||||
if ($userInfo['isAdmin'] == 1) {
|
||||
|
||||
@@ -10,7 +10,7 @@ use think\Db;
|
||||
class Device extends Model
|
||||
{
|
||||
// 设置表名
|
||||
protected $name = 'device';
|
||||
protected $table = 's2_device';
|
||||
|
||||
/**
|
||||
* 获取设备总数
|
||||
|
||||
@@ -10,8 +10,9 @@ use think\model\concern\SoftDelete;
|
||||
*/
|
||||
class Workbench extends Model
|
||||
{
|
||||
|
||||
protected $table = 'ck_workbench';
|
||||
protected $pk = 'id';
|
||||
protected $name = 'workbenches';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
@@ -9,6 +9,7 @@ use think\Model;
|
||||
*/
|
||||
class WorkbenchAutoLike extends Model
|
||||
{
|
||||
protected $table = 'ck_workbench_auto_like';
|
||||
protected $pk = 'id';
|
||||
protected $name = 'workbench_auto_like';
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use think\Model;
|
||||
|
||||
class WorkbenchGroupCreate extends Model
|
||||
{
|
||||
protected $table = 'ck_workbench_group_create';
|
||||
protected $pk = 'id';
|
||||
protected $name = 'workbench_group_create';
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use think\Model;
|
||||
|
||||
class WorkbenchGroupPush extends Model
|
||||
{
|
||||
protected $table = 'ck_workbench_group_push';
|
||||
protected $pk = 'id';
|
||||
protected $name = 'workbench_group_push';
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use think\Model;
|
||||
|
||||
class WorkbenchMomentsSync extends Model
|
||||
{
|
||||
protected $table = 'ck_workbench_moments_sync';
|
||||
protected $pk = 'id';
|
||||
protected $name = 'workbench_moments_sync';
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class FriendTaskJob
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0) {
|
||||
// 更新缓存中的页码,设置10分钟过期
|
||||
Cache::set('friendTaskPage', $pageIndex + 1, 600);
|
||||
Cache::set('friendTaskPage', $pageIndex + 1, 86400);
|
||||
Log::info('更新缓存,下一页页码:' . ($pageIndex + 1) . ',缓存时间:10分钟');
|
||||
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
@@ -93,7 +93,7 @@ class FriendTaskJob
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
} else {
|
||||
// 没有下一页,重置缓存,设置10分钟过期
|
||||
Cache::set('friendTaskPage', 0, 600);
|
||||
Cache::set('friendTaskPage', 0, 86400);
|
||||
Log::info('获取完成,重置缓存,缓存时间:10分钟');
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class WechatChatroomJob
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0) {
|
||||
// 更新缓存中的页码,设置10分钟过期
|
||||
Cache::set('chatroomPage', $pageIndex + 1, 600);
|
||||
Cache::set('chatroomPage', $pageIndex + 1, 86400);
|
||||
Log::info('更新缓存,下一页页码:' . ($pageIndex + 1) . ',缓存时间:10分钟');
|
||||
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
@@ -93,7 +93,7 @@ class WechatChatroomJob
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
} else {
|
||||
// 没有下一页,重置缓存,设置10分钟过期
|
||||
Cache::set('chatroomPage', 0, 600);
|
||||
Cache::set('chatroomPage', 0, 86400);
|
||||
Log::info('获取完成,重置缓存,缓存时间:10分钟');
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ class WechatFriendJob
|
||||
$lastFriendId = $data[count($data)-1]['id'];
|
||||
|
||||
// 更新缓存中的页码和最后一个好友ID,设置10分钟过期
|
||||
Cache::set('friendsPage', $pageIndex + 1, 600);
|
||||
Cache::set('preFriendId', $lastFriendId, 600);
|
||||
Cache::set('friendsPage', $pageIndex + 1, 86400);
|
||||
Cache::set('preFriendId', $lastFriendId, 86400);
|
||||
|
||||
Log::info('更新缓存,下一页页码:' . ($pageIndex + 1) . ',最后好友ID:' . $lastFriendId . ',缓存时间:10分钟');
|
||||
|
||||
@@ -102,8 +102,8 @@ class WechatFriendJob
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
} else {
|
||||
// 没有下一页,重置缓存,设置10分钟过期
|
||||
Cache::set('friendsPage', 0, 600);
|
||||
Cache::set('preFriendId', '', 600);
|
||||
Cache::set('friendsPage', 0, 86400);
|
||||
Cache::set('preFriendId', '', 86400);
|
||||
Log::info('获取完成,重置缓存,缓存时间:10分钟');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user