import dayjs from "dayjs"; import { message } from "@/utils/message"; import type { PaginationProps } from "@pureadmin/table"; import { getLayerTemplateList, deleteLayerTemplate, syncLayerTemplate, batchSyncLayerTemplates } from "@/api/layer-template"; import { getDeviceList } from "@/api/device"; import { ElMessageBox } from "element-plus"; import { type Ref, ref, reactive, onMounted, h } from "vue"; import { ElTag } from "element-plus"; /** * 层模版搜索表单类型定义 */ export interface LayerTemplateSearchForm { /** 设备ID / Device ID */ deviceId: string; /** 模板名称 / Template name */ templateName: string; /** 同步状态 / Sync status */ syncStatus: string | number; } /** * 层模版数据项类型 */ export interface LayerTemplateItem { /** 主键ID / Primary key ID */ id?: number; /** 模版名称 / Template name */ templateName?: string; /** 模版编码 / Template code */ templateCode?: string; /** 设备ID / Device ID */ deviceId?: number | string; /** 设备名称 / Device name */ deviceName?: string; /** 总楼层数 / Total floor count */ totalFloors?: number; /** 同步状态 (0-未同步, 1-同步中, 2-已同步, 3-同步失败) */ syncStatus?: number; /** 最后同步时间 / Last sync time */ lastSyncTime?: string; /** 状态 (0-禁用, 1-启用) */ status?: number; /** 备注 / Remarks */ remark?: string; /** 创建时间 / Create time */ createTime?: string; /** 更新时间 / Update time */ updateTime?: string; } /** * 设备选项类型(用于下拉选择) */ export interface DeviceOption { /** 设备ID / Device ID (SN号) */ deviceId: string; /** 设备名称/编码 / Device name or code */ label: string; /** 门店名称 / Shop name */ shopName?: string; /** 在线状态 / Online status */ isOnline?: number; } export function useProduct(tableRef?: Ref) { // 搜索表单状态 const form = reactive({ deviceId: "", templateName: "", syncStatus: "" }); // 数据列表状态 const dataList = ref([]); // 加载状态 const loading = ref(false); // 分页配置 const pagination = reactive({ total: 0, pageSize: 10, currentPage: 1, background: true }); // ========== 新增:设备列表相关状态 ========== // 设备选项列表(用于操作栏的下拉选择) const deviceOptions = ref([]); // 设备列表加载状态 const deviceLoading = ref(false); // 当前选中的设备ID(用于单个设备同步) const selectedDeviceId = ref(""); // 全量同步加载状态 const syncAllLoading = ref(false); // 同步状态选项 const syncStatusOptions = [ { label: "未同步", value: 0 }, { label: "同步中", value: 1 }, { label: "已同步", value: 2 }, { label: "同步失败", value: 3 } ]; // 表格列定义 const columns: TableColumnList = [ { label: "ID", prop: "id", width: 100 }, { label: "模板名称", prop: "templateName", minWidth: 100, showOverflowTooltip: true }, { label: "设备ID", prop: "deviceId", minWidth: 100 }, { label: "机柜层数", prop: "totalFloors", width: 100, formatter: ({ totalFloors }) => (totalFloors != null ? `${totalFloors}层` : "-") }, { label: "同步状态", prop: "syncStatus", width: 100, cellRenderer: ({ row }) => { const statusMap: Record = { 0: { label: "未同步", type: "info" }, 1: { label: "同步中", type: "warning" }, 2: { label: "已同步", type: "success" }, 3: { label: "同步失败", type: "danger" } }; const status = statusMap[row.syncStatus] || { label: "未知", type: "info" }; return h(ElTag, { type: status.type as any }, () => status.label); } }, { label: "最后同步时间", prop: "lastSyncTime", minWidth: 160, formatter: ({ lastSyncTime }) => lastSyncTime ? dayjs(lastSyncTime).format("YYYY-MM-DD HH:mm:ss") : "-" }, { label: "创建时间", prop: "createTime", minWidth: 160, formatter: ({ createTime }) => createTime ? dayjs(createTime).format("YYYY-MM-DD HH:mm:ss") : "-" }, { label: "操作", fixed: "right", width: 200, slot: "operation" } ]; /** * 加载设备列表(用于同步时选择设备) * Load device list for device selection during sync */ async function loadDeviceList() { if (deviceLoading.value) return; deviceLoading.value = true; try { // 调用设备列表API获取所有设备 // 注意:参数名必须是 pageSize(不是 limit) const res = await getDeviceList({ page: 1, pageSize: 9999 }); console.log("设备API返回数据:", res); // 调试日志 // 注意:后端成功响应的 code 是 200,不是 0 if ((res.code === 200 || res.code === 0) && res.data?.list && Array.isArray(res.data.list)) { // 根据 Device 实体类的实际字段进行映射: // - deviceId: 设备ID/SN号 // - name: 设备名称(注意不是 deviceName!) // - isOnline: Boolean 类型在线状态 // - status: Integer 类型状态码 deviceOptions.value = res.data.list .filter((device: any) => device && device.deviceId) // 过滤掉无效数据 .map((device: any) => ({ deviceId: String(device.deviceId || ""), // 确保转换为字符串 label: device.shopName ? `${device.deviceId} — ${device.shopName}` : device.deviceId, shopName: device.shopName || "", isOnline: device.isOnline ?? (device.status === 1), // 兼容处理 status: device.status // 保留原始状态值 })); console.log("转换后的设备选项:", deviceOptions.value); // 调试日志 if (deviceOptions.value.length === 0) { console.warn("设备列表为空,原始数据:", res.data.list); } } else { console.warn("获取设备列表返回异常:", res); deviceOptions.value = []; } } catch (error) { console.error("加载设备列表失败:", error); message("加载设备列表失败,请检查网络连接", { type: "warning" }); deviceOptions.value = []; } finally { deviceLoading.value = false; } } /** * 搜索查询方法 * Search query method */ async function onSearch() { loading.value = true; try { const searchParams: any = { page: pagination.currentPage, pageSize: pagination.pageSize }; // 只添加非空的搜索条件 if (form.deviceId) searchParams.deviceId = form.deviceId.trim(); if (form.templateName) searchParams.templateName = form.templateName.trim(); if (form.syncStatus !== "" && form.syncStatus != null) { searchParams.syncStatus = parseInt(form.syncStatus.toString(), 10); } const { data } = await getLayerTemplateList(searchParams); dataList.value = data.list || []; pagination.total = Number(data.total) || 0; } catch (error) { console.error("获取层模版列表失败:", error); message("获取层模版列表失败", { type: "error" }); } finally { setTimeout(() => { loading.value = false; }, 300); } } /** * 重置表单并重新查询 * Reset form and re-query */ function resetForm(formEl?: any) { if (!formEl) return; formEl.resetFields(); pagination.currentPage = 1; onSearch(); } /** * 分页大小改变处理 * Handle page size change */ function handleSizeChange(val: number) { pagination.pageSize = val; onSearch(); } /** * 当前页码改变处理 * Handle current page change */ function handleCurrentChange(val: number) { pagination.currentPage = val; onSearch(); } /** * 打开新增/编辑对话框 * Open add/edit dialog * @param title - 对话框标题 * @param row - 编辑时的行数据(可选) */ function openDialog(title?: string, row?: LayerTemplateItem) { // 此方法将在index.vue中与EditDialog组件配合使用 // 通过事件或ref调用EditDialog的打开方法 console.log("openDialog called with:", title, row); // 触发自定义事件或通过其他方式通知父组件 } /** * 删除层模版 * Delete layer template * @param row - 要删除的行数据 */ async function handleDelete(row: LayerTemplateItem) { try { await ElMessageBox.confirm( `确认要删除模板 "${row.templateName}" 吗?`, "删除确认", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } ); const res = await deleteLayerTemplate(row.id!); if (res.code === 200 || res.code === 0) { message(`成功删除模板 ${row.templateName}`, { type: "success" }); onSearch(); } else { message(res.message || "删除失败", { type: "error" }); } } catch (error: any) { if (error !== "cancel") { message("删除操作失败", { type: "error" }); } } } /** * 根据选择的设备ID同步单个设备的层模版 * Sync layer template for a specific selected device * @param deviceId - 设备ID(从下拉框选择的) */ async function handleSyncByDevice(deviceId: string) { if (!deviceId) { message("请先选择要同步的设备", { type: "warning" }); return; } try { // 查找设备显示名称 const device = deviceOptions.value.find(d => d.deviceId === deviceId); const deviceLabel = device?.label || deviceId; await ElMessageBox.confirm( `确认要同步设备 "${deviceLabel}" 的层模版信息吗?`, "同步确认", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } ); const res = await syncLayerTemplate(deviceId); if (res.code === 200 || res.code === 0) { message(`已提交设备 "${deviceLabel}" 的同步请求`, { type: "success" }); // 延迟刷新以获取最新状态 setTimeout(() => { onSearch(); }, 2000); } else { message(res.message || "同步失败", { type: "error" }); } } catch (error: any) { if (error !== "cancel") { console.error("同步失败:", error); message("同步操作失败", { type: "error" }); } } } /** * 全量同步所有设备的层模版(默认行为) * Batch sync all devices' layer templates (default behavior) * * 流程: * 1. 从设备列表获取所有设备的deviceId * 2. 批量调用同步接口 */ async function handleSyncAll() { console.log("开始全量同步, 当前设备列表长度:", deviceOptions.value.length); // 如果设备列表还没加载或为空,先加载 if (deviceOptions.value.length === 0) { console.log("设备列表为空, 尝试重新加载..."); await loadDeviceList(); } // 再次检查 if (deviceOptions.value.length === 0) { console.error("全量同步失败: 设备列表仍为空"); message( "没有可用的设备数据,无法执行全量同步\n\n可能原因:\n1. 数据库中没有设备记录\n2. 设备列表API返回异常\n3. 网络连接问题", { type: "warning", duration: 5000 } ); return; } try { const deviceCount = deviceOptions.value.length; await ElMessageBox.confirm( `确认要同步全部 ${deviceCount} 个设备的层模版信息吗?\n\n此操作将从哈哈零兽平台拉取每个设备的最新层模版配置。\n\n预计耗时:${Math.ceil(deviceCount / 10)} 秒`, "全量同步确认", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } ); syncAllLoading.value = true; // 提取所有设备的deviceId(确保都是非空字符串) const allDeviceIds = deviceOptions.value .map(d => d.deviceId) .filter(id => id && id.trim() !== ""); // 过滤空值 console.log("准备同步的设备ID列表:", allDeviceIds); if (allDeviceIds.length === 0) { message("所有设备的ID都为空,无法执行同步", { type: "error" }); return; } const res = await batchSyncLayerTemplates(allDeviceIds); console.log("批量同步API返回:", res); // 注意:后端成功响应的 code 是 200,不是 0 if (res.code === 200 || res.code === 0) { message(`成功提交 ${allDeviceIds.length} 个设备的同步请求`, { type: "success" }); // 延迟刷新以获取最新状态 setTimeout(() => { onSearch(); }, 3000); // 全量同步等待更长时间 } else { message(res.message || "全量同步失败", { type: "error" }); } } catch (error: any) { if (error !== "cancel") { console.error("全量同步失败:", error); message("全量同步操作失败: " + (error.message || "未知错误"), { type: "error" }); } } finally { syncAllLoading.value = false; } } // 组件挂载时自动加载数据和设备列表 onMounted(async () => { // 并行加载层模版列表和设备列表 await Promise.all([onSearch(), loadDeviceList()]); }); return { // 状态 form, loading, dataList, pagination, columns, syncStatusOptions, // 设备相关状态(新增) deviceOptions, deviceLoading, selectedDeviceId, syncAllLoading, // 方法 onSearch, resetForm, openDialog, handleDelete, // 同步相关方法(重构) handleSyncByDevice, // 选择单个设备同步 handleSyncAll, // 全量同步所有设备 // 兼容旧接口(保持向后兼容) handleSync: handleSyncByDevice, handleBatchSync: handleSyncAll, // 分页方法 handleSizeChange, handleCurrentChange, // 工具方法 loadDeviceList // 手动刷新设备列表 }; }