From 689b6ec82dacfb73c887a628187f6123526d8629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Fri, 9 May 2025 11:46:34 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E7=A7=81=E5=9F=9F=E6=93=8D=E7=9B=98?= =?UTF-8?q?=E6=89=8B=20-=20=E5=88=A0=E9=99=A4=E8=AE=BE=E5=A4=87=E8=BF=94?= =?UTF-8?q?=E5=B7=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/common/model/DeviceUser.php | 32 +--- Server/application/cunkebao/config/route.php | 4 +- .../cunkebao/controller/Device.php | 175 ------------------ .../device/DeleteDeviceV1Controller.php | 137 ++++++++++++++ Server/application/cunkebao/model/Device.php | 106 ----------- .../cunkebao/model/DeviceHandleLog.php | 90 --------- .../application/cunkebao/model/DeviceUser.php | 175 ------------------ 7 files changed, 144 insertions(+), 575 deletions(-) delete mode 100644 Server/application/cunkebao/controller/Device.php create mode 100644 Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php delete mode 100644 Server/application/cunkebao/model/Device.php delete mode 100644 Server/application/cunkebao/model/DeviceHandleLog.php delete mode 100644 Server/application/cunkebao/model/DeviceUser.php diff --git a/Server/application/common/model/DeviceUser.php b/Server/application/common/model/DeviceUser.php index fcbebaba..738ca4ef 100644 --- a/Server/application/common/model/DeviceUser.php +++ b/Server/application/common/model/DeviceUser.php @@ -3,6 +3,7 @@ namespace app\common\model; use think\Model; +use think\model\concern\SoftDelete; /** * 设备用户关联模型 @@ -10,6 +11,8 @@ use think\Model; */ class DeviceUser extends Model { + use SoftDelete; + /** * 数据表名 * @var string @@ -20,31 +23,6 @@ class DeviceUser extends Model protected $autoWriteTimestamp = true; protected $createTime = 'createTime'; protected $updateTime = 'updateTime'; - - /** - * 关联用户模型 - * @return \think\model\relation\BelongsTo - */ - public function user() - { - return $this->belongsTo('User', 'userId', 'id'); - } - - /** - * 关联设备模型 - * @return \think\model\relation\BelongsTo - */ - public function device() - { - return $this->belongsTo('app\common\model\Device', 'deviceId', 'id'); - } - - /** - * 关联公司模型 - * @return \think\model\relation\BelongsTo - */ - public function company() - { - return $this->belongsTo('app\common\model\Company', 'companyId', 'id'); - } + protected $deleteTime = 'deleteTime'; + protected $defaultSoftDelete = 0; } \ No newline at end of file diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 49d7af83..0ce2a9dc 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -17,8 +17,8 @@ Route::group('v1/', function () { Route::get(':id', 'app\cunkebao\controller\device\GetDeviceDetailV1Controller@index'); // 获取设备详情 Route::post('', 'app\cunkebao\controller\device\PostAddDeviceV1Controller@index'); // 添加设备 Route::put('refresh', 'app\cunkebao\controller\device\RefreshDeviceDetailV1Controller@index'); // 刷新设备状态 - Route::delete(':id', 'app\cunkebao\controller\Device@delete'); // 删除设备 - Route::post('task-config', 'app\cunkebao\controller\device\UpdateDeviceTaskConfigV1Controller@index'); + Route::delete(':id', 'app\cunkebao\controller\device\DeleteDeviceV1Controller@index'); // 删除设备 + Route::post('task-config', 'app\cunkebao\controller\device\UpdateDeviceTaskConfigV1Controller@index'); // 设备任务配置 }); // 设备微信相关 diff --git a/Server/application/cunkebao/controller/Device.php b/Server/application/cunkebao/controller/Device.php deleted file mode 100644 index 749151ba..00000000 --- a/Server/application/cunkebao/controller/Device.php +++ /dev/null @@ -1,175 +0,0 @@ -userInfo; - - // 检查用户权限,只有管理员可以添加设备 - if ($userInfo['isAdmin'] != 1) { - return json([ - 'code' => 403, - 'msg' => '您没有权限添加设备' - ]); - } - - // 获取设备数据 - $data = Request::post(); - - // 验证IMEI是否为空 - if (empty($data['imei'])) { - return json([ - 'code' => 400, - 'msg' => '设备IMEI不能为空' - ]); - } - - // 验证IMEI是否已存在 - $exists = DeviceModel::where('imei', $data['imei'])->where('isDeleted', 0)->find(); - - if ($exists) { - return json([ - 'code' => 400, - 'msg' => '设备IMEI已存在' - ]); - } - - // 设置设备公司ID - $data['companyId'] = $userInfo['companyId']; - $data['id'] = time(); - - try { - Db::startTrans(); - - // 添加设备 - $id = DeviceModel::addDevice($data); - - // 添加设备操作记录 - DeviceHandleLog::addLog( - [ - 'imei' => $data['imei'], - 'userId' => $userInfo['id'], - 'content' => '添加设备', - 'companyId' => $userInfo['companyId'], - ] - ); - Db::commit(); - } catch (\Exception $e) { - Db::rollback(); - - return json([ - 'code' => 500, - 'msg' => '添加失败:' . $e->getMessage() - ]); - } - - // 此处调用底层API - return json([ - 'code' => 200, - 'msg' => '添加成功', - 'data' => [ - 'id' => $id - ] - ]); - } catch (\Exception $e) { - return json([ - 'code' => 500, - 'msg' => '添加失败:' . $e->getMessage() - ]); - } - } - - /** - * 删除设备 - * @return \think\response\Json - */ - public function delete() - { - try { - // 获取登录用户信息 - $userInfo = request()->userInfo; - - // 检查用户权限,只有管理员可以删除设备 - if ($userInfo['isAdmin'] != 1) { - return json([ - 'code' => 403, - 'msg' => '您没有权限删除设备' - ]); - } - - // 获取设备ID - $id = Request::param('id/d'); - if (empty($id)) { - return json([ - 'code' => 400, - 'msg' => '参数错误' - ]); - } - - // 验证设备是否存在 - $exists = DeviceModel::where('id', $id) - ->where('isDeleted', 0) - ->where('companyId', $userInfo['companyId']) - ->find(); - - if (!$exists) { - return json([ - 'code' => 404, - 'msg' => '设备不存在或无权限操作' - ]); - } - - // 删除设备 - $result = DeviceModel::deleteDevice($id); - - return json([ - 'code' => 200, - 'msg' => '删除成功', - 'data' => [ - 'result' => $result - ] - ]); - } catch (\Exception $e) { - return json([ - 'code' => 500, - 'msg' => '删除失败:' . $e->getMessage() - ]); - } - } - - -} \ No newline at end of file diff --git a/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php b/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php new file mode 100644 index 00000000..9d6642bf --- /dev/null +++ b/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php @@ -0,0 +1,137 @@ +getUserInfo('companyId'); + $deviceUser = DeviceUserModel::where(compact('companyId', 'deviceId'))->find(); + + // 有关联数据则删除 + if ($deviceUser) { + if (!$deviceUser->delete()) { + throw new \Exception('设备用户关联数据删除失败', 402); + } + } + } + + /** + * 删除设备任务配置记录 + * + * @param int $deviceId + * @return void + * @throws \Exception + */ + protected function deleteDeviceConf(int $deviceId): void + { + $companyId = $this->getUserInfo('companyId'); + $deviceConf = DeviceTaskconfModel::where(compact('companyId', 'deviceId'))->find(); + + // 有配置信息则删除 + if ($deviceConf) { + if (!$deviceConf->delete()) { + throw new \Exception('设备设置信息删除失败', 402); + } + } + } + + /** + * 删除主设备信息 + * + * @param int $id + * @return DeviceModel + * @throws \Exception + */ + protected function deleteDevice(int $id): void + { + $device = DeviceModel::where('companyId', $this->getUserInfo('companyId'))->find($id); + + if (!$device) { + throw new \Exception('设备不存在或无权限操作', 404); + } + + if (!$device->delete()) { + throw new \Exception('设备删除失败', 402); + } + } + + /** + * 删除存客宝设备数据 + * + * @param int $id + * @return $this + * @throws \Exception + */ + protected function deleteCkbAbout(int $id): self + { + $this->deleteDevice($id); + $this->deleteDeviceConf($id); + $this->deleteDeviceUser($id); + + return $this; + } + + /** + * TODO 删除存客宝设备数据 + * + * @return self + */ + protected function deleteS2About(): self + { + } + + /** + * 检查用户权限,只有操盘手可以删除设备 + * + * @return $this + */ + protected function checkPermission(): self + { + if ($this->getUserInfo('typeId') != UserModel::MASTER_USER) { + throw new \Exception('您没有权限删除设备', 403); + } + + return $this; + } + + /** + * 删除设备 + * + * @return \think\response\Json + */ + public function index() + { + try { + $id = $this->request->param('id/d'); + + Db::startTrans(); + $this->checkPermission(); + $this->deleteCkbAbout($id)->deleteS2About($id); + Db::commit(); + + return ResponseHelper::success(); + } catch (\Exception $e) { + Db::rollback(); + return ResponseHelper::error($e->getMessage(), $e->getCode()); + } + } +} \ No newline at end of file diff --git a/Server/application/cunkebao/model/Device.php b/Server/application/cunkebao/model/Device.php deleted file mode 100644 index 19b1da19..00000000 --- a/Server/application/cunkebao/model/Device.php +++ /dev/null @@ -1,106 +0,0 @@ - $value) { - if (strpos($key, '.') !== false) { - $hasAlias = true; - break; - } - } - - // 如果使用了表别名,则需要使用查询构造器 - if ($hasAlias) { - return self::alias('d')->where($where)->count(); - } else { - return self::where($where)->count(); - } - } - - /** - * 添加设备 - * @param array $data 设备数据 - * @return int 新增设备ID - */ - public static function addDevice($data) - { - $device = new self(); - $device->allowField(true)->save($data); - return $device->id; - } - - /** - * 更新设备 - * @param int $id 设备ID - * @param array $data 设备数据 - * @return bool 更新结果 - */ - public static function updateDevice($id, $data) - { - return self::where('id', $id) - ->where('isDeleted', 0) - ->update($data); - } - - /** - * 删除设备(软删除) - * @param int $id 设备ID - * @return bool 删除结果 - */ - public static function deleteDevice($id) - { - return self::where('id', $id) - ->update([ - 'isDeleted' => 1, - 'deleteTime' => date('Y-m-d H:i:s', time()) - ]); - } - - /** - * 按设备品牌统计数量 - * @return array 统计结果 - */ - public static function countByBrand() - { - return self::where('isDeleted', 0) - ->group('brand') - ->field('brand, count(*) as count') - ->select(); - } - - /** - * 按设备在线状态统计数量 - * @return array 统计结果 - */ - public static function countByStatus() - { - return self::where('isDeleted', 0) - ->group('alive') - ->field('alive, count(*) as count') - ->select(); - } -} \ No newline at end of file diff --git a/Server/application/cunkebao/model/DeviceHandleLog.php b/Server/application/cunkebao/model/DeviceHandleLog.php deleted file mode 100644 index 4afe46cf..00000000 --- a/Server/application/cunkebao/model/DeviceHandleLog.php +++ /dev/null @@ -1,90 +0,0 @@ -order($order) - ->paginate($limit, false, ['page' => $page]); - } - - /** - * 根据IMEI获取设备操作日志 - * @param string $imei 设备IMEI - * @param int $companyId 租户ID - * @param int $limit 获取条数 - * @return array 日志记录 - */ - public static function getLogsByImei($imei, $companyId = null, $limit = 10) - { - $query = self::where('imei', $imei); - - if ($companyId !== null) { - $query->where('companyId', $companyId); - } - - return $query->order('createTime', 'desc') - ->limit($limit) - ->select(); - } - - /** - * 根据用户ID获取操作日志 - * @param int $userId 用户ID - * @param int $companyId 租户ID - * @param int $page 页码 - * @param int $limit 每页数量 - * @return \think\Paginator 分页对象 - */ - public static function getLogsByUser($userId, $companyId = null, $page = 1, $limit = 10) - { - $query = self::where('userId', $userId); - - if ($companyId !== null) { - $query->where('companyId', $companyId); - } - - return $query->order('createTime', 'desc') - ->paginate($limit, false, ['page' => $page]); - } - - /** - * 记录设备操作日志的便捷方法 - * @param string $imei 设备IMEI - * @param int $userId 操作用户ID - * @param string $content 操作内容 - * @param int $companyId 租户ID - * @return int 日志ID - */ - public static function recordLog($imei, $userId, $content, $companyId = null) - { - $data = [ - 'imei' => $imei, - 'userId' => $userId, - 'content' => $content, - 'companyId' => $companyId - ]; - - return self::addLog($data); - } -} \ No newline at end of file diff --git a/Server/application/cunkebao/model/DeviceUser.php b/Server/application/cunkebao/model/DeviceUser.php deleted file mode 100644 index 411f9759..00000000 --- a/Server/application/cunkebao/model/DeviceUser.php +++ /dev/null @@ -1,175 +0,0 @@ - $userId]; - - if (!is_null($companyId)) { - $where['companyId'] = $companyId; - } - - return self::where($where)->column('deviceId'); - } - - /** - * 获取指定设备的所有用户ID - * @param int $deviceId 设备ID - * @param int $companyId 公司ID - * @return array 用户ID数组 - */ - public static function getDeviceUserIds($deviceId, $companyId = null) - { - $where = ['deviceId' => $deviceId]; - - if (!is_null($companyId)) { - $where['companyId'] = $companyId; - } - - return self::where($where) - ->column('userId'); - } - - /** - * 添加设备与用户的关联 - * @param int $companyId 公司ID - * @param int $userId 用户ID - * @param int $deviceId 设备ID - * @return bool 是否添加成功 - */ - public static function addRelation($companyId, $userId, $deviceId) - { - // 检查关联是否已存在 - $exists = self::where([ - 'companyId' => $companyId, - 'userId' => $userId, - 'deviceId' => $deviceId - ])->find(); - - if ($exists) { - return true; // 已存在,视为添加成功 - } - - // 添加新关联 - return self::create([ - 'companyId' => $companyId, - 'userId' => $userId, - 'deviceId' => $deviceId - ]) ? true : false; - } - - /** - * 批量添加设备与用户的关联 - * @param int $companyId 公司ID - * @param int $userId 用户ID - * @param array $deviceIds 设备ID数组 - * @return int 成功添加的记录数 - */ - public static function batchAddRelations($companyId, $userId, array $deviceIds) - { - if (empty($deviceIds)) { - return 0; - } - - $data = []; - foreach ($deviceIds as $deviceId) { - $data[] = [ - 'companyId' => $companyId, - 'userId' => $userId, - 'deviceId' => $deviceId - ]; - } - - // 批量添加前先删除已存在的关联,避免主键冲突 - self::where('userId', $userId) - ->where('companyId', $companyId) - ->whereIn('deviceId', $deviceIds) - ->delete(); - - return self::insertAll($data); - } - - /** - * 删除设备与用户的关联 - * @param int $companyId 公司ID - * @param int $userId 用户ID - * @param int $deviceId 设备ID - * @return bool 是否删除成功 - */ - public static function removeDeviceUserRelation($companyId, $userId, $deviceId) - { - return self::where([ - 'companyId' => $companyId, - 'userId' => $userId, - 'deviceId' => $deviceId - ])->delete() !== false; - } - - /** - * 检查用户是否有权限操作指定设备 - * @param int $userId 用户ID - * @param int $deviceId 设备ID - * @param int $companyId 公司ID - * @return bool 是否有权限 - */ - public static function checkUserDevicePermission($userId, $deviceId, $companyId = null) - { - $where = [ - 'userId' => $userId, - 'deviceId' => $deviceId - ]; - - if (!is_null($companyId)) { - $where['companyId'] = $companyId; - } - - return self::where($where)->count() > 0; - } - - /** - * 关联用户模型 - * @return \think\model\relation\BelongsTo - */ - public function user() - { - return $this->belongsTo('User', 'userId', 'id'); - } - - /** - * 关联设备模型 - * @return \think\model\relation\BelongsTo - */ - public function device() - { - return $this->belongsTo('app\devices\model\Device', 'deviceId', 'id'); - } - - /** - * 关联公司模型 - * @return \think\model\relation\BelongsTo - */ - public function company() - { - return $this->belongsTo('Company', 'companyId', 'id'); - } -} \ No newline at end of file From 4ab353c1980bc500bc08d4636bf8f93500950f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Fri, 9 May 2025 14:13:39 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E7=A7=81=E5=9F=9F=E6=93=8D=E7=9B=98?= =?UTF-8?q?=E6=89=8B=20-=20=E5=88=A0=E9=99=A4=E8=AE=BE=E5=A4=87=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=8D=95=E9=80=89=E5=B9=B6=E4=B8=94=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E8=AE=BE=E5=A4=87=E6=97=B6=E5=A2=9E=E5=8A=A0=E6=A8=A1?= =?UTF-8?q?=E6=80=81=E6=8F=90=E7=A4=BA=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/.gitignore | 2 + Cunkebao/app/devices/page.tsx | 225 +++++++----------- .../device/DeleteDeviceV1Controller.php | 1 + 3 files changed, 85 insertions(+), 143 deletions(-) diff --git a/Cunkebao/.gitignore b/Cunkebao/.gitignore index f650315f..e39c24cf 100644 --- a/Cunkebao/.gitignore +++ b/Cunkebao/.gitignore @@ -7,6 +7,8 @@ /.next/ /out/ +/.history/ + # production /build diff --git a/Cunkebao/app/devices/page.tsx b/Cunkebao/app/devices/page.tsx index 3d6fe02e..8751f07a 100644 --- a/Cunkebao/app/devices/page.tsx +++ b/Cunkebao/app/devices/page.tsx @@ -5,17 +5,18 @@ import { useRouter } from "next/navigation" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import { ChevronLeft, Plus, Filter, Search, RefreshCw, QrCode, Smartphone, Loader2, AlertTriangle } from "lucide-react" +import { ChevronLeft, Plus, Filter, Search, RefreshCw, QrCode, Smartphone, Loader2, AlertTriangle, Trash2 } from "lucide-react" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Checkbox } from "@/components/ui/checkbox" import { toast } from "@/components/ui/use-toast" import { Badge } from "@/components/ui/badge" -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { fetchDeviceList, deleteDevice } from "@/api/devices" import { ServerDevice } from "@/types/device" import { api } from "@/lib/api" import { ImeiDisplay } from "@/components/ImeiDisplay" +import type { ApiResponse } from "@/lib/api" // 设备接口更新为与服务端接口对应的类型 interface Device extends ServerDevice { @@ -33,14 +34,12 @@ export default function DevicesPage() { const [searchQuery, setSearchQuery] = useState("") const [statusFilter, setStatusFilter] = useState("all") const [currentPage, setCurrentPage] = useState(1) - const [selectedDevices, setSelectedDevices] = useState([]) + const [selectedDeviceId, setSelectedDeviceId] = useState(null) const [isLoading, setIsLoading] = useState(false) const [hasMore, setHasMore] = useState(true) const [totalCount, setTotalCount] = useState(0) const observerTarget = useRef(null) - // 使用ref来追踪当前页码,避免依赖effect循环 const pageRef = useRef(1) - // 添加设备相关状态 const [deviceImei, setDeviceImei] = useState("") const [deviceName, setDeviceName] = useState("") const [qrCodeImage, setQrCodeImage] = useState("") @@ -59,14 +58,14 @@ export default function DevicesPage() { showAnimation: false }); - // 添加轮询定时器引用 const pollingTimerRef = useRef(null); - const devicesPerPage = 20 // 每页显示20条记录 + const devicesPerPage = 20 + + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false) + const [deviceToDelete, setDeviceToDelete] = useState(null) - // 获取设备列表 const loadDevices = useCallback(async (page: number, refresh: boolean = false) => { - // 检查是否已经在加载中,避免重复请求 if (isLoading) return; try { @@ -74,20 +73,17 @@ export default function DevicesPage() { const response = await fetchDeviceList(page, devicesPerPage, searchQuery) if (response.code === 200 && response.data) { - // 转换数据格式,确保status类型正确 const serverDevices = response.data.list.map(device => ({ ...device, status: device.alive === 1 ? "online" as const : "offline" as const })) - // 更新设备列表 if (refresh) { setDevices(serverDevices) } else { setDevices(prev => [...prev, ...serverDevices]) } - // 更新统计信息 const total = response.data.total const online = response.data.list.filter(d => d.alive === 1).length setStats({ @@ -95,16 +91,13 @@ export default function DevicesPage() { onlineDevices: online }) - // 更新分页信息 setTotalCount(response.data.total) - // 更新hasMore状态,确保有更多数据且返回的数据数量等于每页数量 const hasMoreData = serverDevices.length > 0 && serverDevices.length === devicesPerPage && (page * devicesPerPage) < response.data.total; setHasMore(hasMoreData) - // 更新当前页码的ref值 pageRef.current = page } else { toast({ @@ -123,54 +116,37 @@ export default function DevicesPage() { } finally { setIsLoading(false) } - // 移除isLoading依赖,只保留真正需要的依赖 - }, [searchQuery]) // devicesPerPage是常量,不需要加入依赖 + }, [searchQuery]) - // 加载下一页数据的函数,使用ref来追踪页码,避免依赖循环 const loadNextPage = useCallback(() => { - // 如果正在加载或者没有更多数据,直接返回 if (isLoading || !hasMore) return; - // 使用ref来获取下一页码,避免依赖currentPage const nextPage = pageRef.current + 1; - // 设置UI显示的当前页 setCurrentPage(nextPage); - // 加载下一页数据 loadDevices(nextPage, false); - // 只依赖必要的状态 }, [hasMore, isLoading, loadDevices]); - // 追踪组件是否已挂载 const isMounted = useRef(true); - // 组件卸载时更新挂载状态 useEffect(() => { return () => { isMounted.current = false; }; }, []); - // 初始加载和搜索时刷新列表 useEffect(() => { - // 组件未挂载,不执行操作 if (!isMounted.current) return; - // 重置页码 setCurrentPage(1) pageRef.current = 1 - // 加载第一页数据 loadDevices(1, true) }, [searchQuery, loadDevices]) - // 无限滚动加载实现 useEffect(() => { - // 如果没有更多数据或者正在加载,不创建observer if (!hasMore || isLoading) return; - // 创建观察器观察加载点 const observer = new IntersectionObserver( entries => { - // 如果交叉了,且有更多数据,且当前不在加载状态,且组件仍然挂载 if (entries[0].isIntersecting && hasMore && !isLoading && isMounted.current) { loadNextPage(); } @@ -178,24 +154,20 @@ export default function DevicesPage() { { threshold: 0.5 } ) - // 只在客户端时观察节点 if (typeof window !== 'undefined' && observerTarget.current) { observer.observe(observerTarget.current) } - // 清理观察器 return () => { observer.disconnect(); } }, [hasMore, isLoading, loadNextPage]) - // 获取设备二维码 const fetchDeviceQRCode = async () => { try { setIsLoadingQRCode(true) - setQrCodeImage("") // 清空当前二维码 + setQrCodeImage("") - // 获取保存的accountId const accountId = localStorage.getItem('s2_accountId') if (!accountId) { toast({ @@ -206,7 +178,6 @@ export default function DevicesPage() { return } - // 发起请求获取二维码 - 直接使用fetch避免api工具添加基础URL const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/api/device/add`, { method: 'POST', headers: { @@ -218,10 +189,8 @@ export default function DevicesPage() { }) }) - // 保存原始响应文本以便调试 const responseText = await response.text(); - // 尝试将响应解析为JSON let result; try { result = JSON.parse(responseText); @@ -235,7 +204,6 @@ export default function DevicesPage() { } if (result && result.code === 200) { - // 尝试多种可能的返回数据结构 let qrcodeData = null; if (result.data?.qrCode) { @@ -245,7 +213,6 @@ export default function DevicesPage() { } else if (result.data?.image) { qrcodeData = result.data.image; } else if (result.data?.url) { - // 如果返回的是URL而不是base64 qrcodeData = result.data.url; setQrCodeImage(qrcodeData); @@ -254,9 +221,8 @@ export default function DevicesPage() { description: "请使用手机扫描新的二维码添加设备", }); - return; // 直接返回,不进行base64处理 + return; } else if (typeof result.data === 'string') { - // 如果data直接是字符串 qrcodeData = result.data; } else { toast({ @@ -267,7 +233,6 @@ export default function DevicesPage() { return; } - // 检查数据是否为空 if (!qrcodeData) { toast({ title: "获取二维码失败", @@ -277,24 +242,18 @@ export default function DevicesPage() { return; } - // 检查是否已经是完整的data URL if (qrcodeData.startsWith('data:image')) { setQrCodeImage(qrcodeData); } - // 检查是否是URL else if (qrcodeData.startsWith('http')) { setQrCodeImage(qrcodeData); } - // 尝试作为base64处理 else { try { - // 确保base64字符串没有空格等干扰字符 const cleanedBase64 = qrcodeData.trim(); - // 直接以图片src格式设置 setQrCodeImage(`data:image/png;base64,${cleanedBase64}`); - // 预加载图片,确认是否有效 const img = new Image(); img.onload = () => { // 图片加载成功 @@ -339,7 +298,6 @@ export default function DevicesPage() { } } - // 清理轮询函数 const cleanupPolling = useCallback(() => { if (pollingTimerRef.current) { clearTimeout(pollingTimerRef.current); @@ -353,14 +311,12 @@ export default function DevicesPage() { }); }, []); - // 轮询检测设备添加状态 const startPolling = useCallback(() => { let pollCount = 0; const maxPolls = 60; - const pollInterval = 1000; // 1秒 - const initialDelay = 5000; // 5秒后开始轮询 + const pollInterval = 1000; + const initialDelay = 5000; - // 初始提示 setPollingStatus({ isPolling: false, message: '请扫描二维码添加设备,5秒后将开始检测添加结果', @@ -398,7 +354,7 @@ export default function DevicesPage() { messageType: 'success', showAnimation: false }); - setQrCodeImage('/broken-qr.png'); // 显示损坏的二维码 + setQrCodeImage('/broken-qr.png'); cleanupPolling(); return; } @@ -428,7 +384,6 @@ export default function DevicesPage() { } }; - // 5秒后开始轮询 pollingTimerRef.current = setTimeout(() => { setPollingStatus({ isPolling: true, @@ -440,7 +395,6 @@ export default function DevicesPage() { }, initialDelay); }, [cleanupPolling]); - // 修改打开添加设备模态框的处理函数 const handleOpenAddDeviceModal = () => { setIsAddDeviceOpen(true); setDeviceImei(""); @@ -456,13 +410,11 @@ export default function DevicesPage() { startPolling(); } - // 修改关闭模态框的处理函数 const handleCloseAddDeviceModal = () => { cleanupPolling(); setIsAddDeviceOpen(false); } - // 通过IMEI添加设备 const handleAddDeviceByImei = async () => { if (!deviceImei) { toast({ @@ -476,7 +428,6 @@ export default function DevicesPage() { try { setIsSubmittingImei(true); - // 使用api.post发送请求到/v1/devices const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/devices`, { method: 'POST', headers: { @@ -490,10 +441,8 @@ export default function DevicesPage() { }) }); - // 保存原始响应文本以便调试 const responseText = await response.text(); - // 尝试将响应解析为JSON let result; try { result = JSON.parse(responseText); @@ -512,12 +461,10 @@ export default function DevicesPage() { description: result.data?.msg || "设备已成功添加", }); - // 清空输入并关闭弹窗 setDeviceImei(""); setDeviceName(""); setIsAddDeviceOpen(false); - // 刷新设备列表 loadDevices(1, true); } else { toast({ @@ -537,7 +484,6 @@ export default function DevicesPage() { } } - // 刷新设备列表 const handleRefresh = () => { setCurrentPage(1) pageRef.current = 1 @@ -548,7 +494,6 @@ export default function DevicesPage() { }) } - // 筛选设备 const filteredDevices = devices.filter(device => { const matchesStatus = statusFilter === "all" || (statusFilter === "online" && device.alive === 1) || @@ -556,57 +501,64 @@ export default function DevicesPage() { return matchesStatus }) - // 处理批量删除 - const handleBatchDelete = async () => { - if (selectedDevices.length === 0) { + const handleDeleteClick = () => { + if (!selectedDeviceId) { toast({ title: "请选择设备", - description: "您需要选择至少一个设备来执行批量删除操作", + description: "请先选择要删除的设备", variant: "destructive", }) return } + setDeviceToDelete(selectedDeviceId) + setIsDeleteDialogOpen(true) + } - // 这里需要实现批量删除逻辑 - // 目前只是单个删除的循环 - let successCount = 0 - for (const deviceId of selectedDevices) { - try { - const response = await deleteDevice(deviceId) - if (response.code === 200) { - successCount++ - } - } catch (error) { - console.error(`删除设备 ${deviceId} 失败`, error) + const handleConfirmDelete = async () => { + if (!deviceToDelete) return + + try { + const response = await deleteDevice(deviceToDelete) + if (response.code === 200) { + setIsDeleteDialogOpen(false) + setDeviceToDelete(null) + setSelectedDeviceId(null) + toast({ + title: "删除成功", + description: "设备已成功删除", + }) + handleRefresh() + } else { + toast({ + title: "删除失败", + description: response.message || "请稍后重试", + variant: "destructive", + }) + setIsDeleteDialogOpen(false) + setDeviceToDelete(null) } - } - - // 删除后刷新列表 - if (successCount > 0) { - toast({ - title: "批量删除成功", - description: `已删除 ${successCount} 个设备`, - }) - setSelectedDevices([]) - handleRefresh() - } else { + } catch (error) { + console.error(`删除设备 ${deviceToDelete} 失败`, error) toast({ - title: "批量删除失败", + title: "删除失败", description: "请稍后重试", variant: "destructive", }) + setIsDeleteDialogOpen(false) + setDeviceToDelete(null) } } - // 设备详情页跳转 + const handleCancelDelete = () => { + setIsDeleteDialogOpen(false) + setDeviceToDelete(null) + } + const handleDeviceClick = (deviceId: number, event: React.MouseEvent) => { - // 判断点击事件是否来自ImeiDisplay组件或其后代元素 - // 如果点击事件已经被处理(例如ImeiDisplay中已阻止传播),则不执行跳转 if (event.defaultPrevented) { return; } - // 如果点击的元素或其父元素有imei-display类,则不跳转 let target = event.target as HTMLElement; while (target && target !== event.currentTarget) { if (target.classList.contains('imei-display-area')) { @@ -618,7 +570,6 @@ export default function DevicesPage() { router.push(`/devices/${deviceId}`); } - // 处理添加设备 const handleAddDevice = async () => { try { const s2_accountId = localStorage.getItem('s2_accountId'); @@ -650,7 +601,6 @@ export default function DevicesPage() { variant: 'default', }); setIsAddDeviceOpen(false); - // 刷新设备列表 loadDevices(1, true); } else { toast({ @@ -728,25 +678,12 @@ export default function DevicesPage() { 离线 -
- 0} - onCheckedChange={(checked) => { - if (checked) { - setSelectedDevices(filteredDevices.map((d) => d.id)) - } else { - setSelectedDevices([]) - } - }} - /> - 全选 -
@@ -761,12 +698,12 @@ export default function DevicesPage() { >
{ if (checked) { - setSelectedDevices([...selectedDevices, device.id]) + setSelectedDeviceId(device.id) } else { - setSelectedDevices(selectedDevices.filter((id) => id !== device.id)) + setSelectedDeviceId(null) } }} onClick={(e) => e.stopPropagation()} @@ -791,7 +728,6 @@ export default function DevicesPage() { ))} - {/* 加载更多观察点 */}
{isLoading &&
加载中...
} {!hasMore && devices.length > 0 &&
没有更多设备了
} @@ -802,7 +738,6 @@ export default function DevicesPage() {
- {/* 修改添加设备对话框 */} { if (!open) { handleCloseAddDeviceModal(); @@ -814,20 +749,8 @@ export default function DevicesPage() { - {/* - - - 扫码添加 - - - - 手动添加 - - */} -
- {/* 悬浮提示区域,上移50% */}
{pollingStatus.isPolling || pollingStatus.showAnimation ? ( @@ -859,9 +782,7 @@ export default function DevicesPage() { className="w-full h-full object-contain" onError={(e) => { console.error("二维码图片加载失败"); - // 隐藏图片 e.currentTarget.style.display = 'none'; - // 显示错误信息 const container = document.getElementById('qrcode-container'); if (container) { const errorEl = container.querySelector('.qrcode-error'); @@ -920,7 +841,7 @@ export default function DevicesPage() {

为设备添加一个便于识别的名称

-
+
- + -
-
+
+ - {/* 在二维码显示区域下方添加状态提示 */}
{pollingStatus.message && (
@@ -970,6 +890,25 @@ export default function DevicesPage() {
)}
+ + + + + 确认删除 + + 设备删除后,本设备配置的计划任务操作也将失效。 + + + + + + + + ) } diff --git a/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php b/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php index 9d6642bf..a2b149ad 100644 --- a/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php +++ b/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php @@ -97,6 +97,7 @@ class DeleteDeviceV1Controller extends BaseController */ protected function deleteS2About(): self { + return $this; } /** From 1eecc85551f7998fdeabea8bec0c49e1e0c67ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Fri, 9 May 2025 14:19:55 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E7=A7=81=E5=9F=9F=E6=93=8D=E7=9B=98?= =?UTF-8?q?=E6=89=8B=20-=20=E8=AE=BE=E5=A4=87=E8=AF=A6=E6=83=85=E4=B8=AD?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E8=AE=BE=E5=A4=87=E7=BB=91=E5=AE=9A=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E7=9A=84=E6=B6=88=E6=81=AF=E6=80=BB=E6=95=B0=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0TODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/GetDeviceDetailV1Controller.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php b/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php index 6e5a454b..dbbfdff0 100644 --- a/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php +++ b/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php @@ -66,16 +66,13 @@ class GetDeviceDetailV1Controller extends BaseController */ protected function getTaskConfig(int $deviceId): array { - $where = [ - 'deviceId' => $deviceId, - 'companyId' => $this->getUserInfo('companyId'), - ]; + $companyId = $this->getUserInfo('companyId'); $conf = DeviceTaskconfModel::alias('c') ->field([ 'c.autoAddFriend', 'c.autoReply', 'c.momentsSync', 'c.aiChat' ]) - ->where($where) + ->where(compact('companyId', 'deviceId')) ->find(); if (!is_null($conf)) { @@ -95,12 +92,9 @@ class GetDeviceDetailV1Controller extends BaseController */ protected function getTotalFriend(int $deviceId): int { - $where = [ - 'deviceId' => $deviceId, - 'companyId' => $this->getUserInfo('companyId'), - ]; + $companyId = $this->getUserInfo('companyId'); - $ownerWechatId = DeviceWechatLogin::where($where)->order('createTime desc')->value('wechatId'); + $ownerWechatId = DeviceWechatLogin::where(compact('companyId', 'deviceId'))->order('createTime desc')->value('wechatId'); if ($ownerWechatId) { return WechatFriend::where(['ownerWechatId' => $ownerWechatId])->count(); @@ -110,7 +104,7 @@ class GetDeviceDetailV1Controller extends BaseController } /** - * 获取设备绑定微信你的消息总数 + * TODO 获取设备绑定微信的消息总数 * * @return int */