| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- 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<LayerTemplateSearchForm>({
- deviceId: "",
- templateName: "",
- syncStatus: ""
- });
- // 数据列表状态
- const dataList = ref<LayerTemplateItem[]>([]);
- // 加载状态
- const loading = ref(false);
- // 分页配置
- const pagination = reactive<PaginationProps>({
- total: 0,
- pageSize: 10,
- currentPage: 1,
- background: true
- });
- // ========== 新增:设备列表相关状态 ==========
- // 设备选项列表(用于操作栏的下拉选择)
- const deviceOptions = ref<DeviceOption[]>([]);
- // 设备列表加载状态
- const deviceLoading = ref(false);
- // 当前选中的设备ID(用于单个设备同步)
- const selectedDeviceId = ref<string>("");
- // 全量同步加载状态
- 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<number, { label: string; type: string }> = {
- 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 // 手动刷新设备列表
- };
- }
|