浏览代码

feat:"添加远程制作记录,远程操作权限,优化部分页面"

soobin 5 月之前
父节点
当前提交
7eceed516c

+ 55 - 2
src/assets/language/en.json

@@ -95,6 +95,7 @@
     "total": "Total ",
     "permissions": " permissions",
     "rolePermissions": "Role Permissions",
+    "chirdAccountTotal": "Total Subaccounts",
     "add": "Add Sub",
     "screen": "Filter",
     "fullName": "Full Name",
@@ -449,7 +450,7 @@
     "jam": "Jam",
     "nuts​": "Toppings",
     "confirmMake": "Start Production",
-    "todaysSugarList": "Today's Orders",
+    "todaysSugarList": "Production Log",
     "todaysMakeList": "Production Log",
     "goods": "Item:",
     "doTime": "Time:",
@@ -627,7 +628,25 @@
       "adRulePlace": "Select rules first"
     },
     "flowerType": "Patterns",
-    "flowerTypePlace": "Select pattern"
+    "flowerTypePlace": "Select pattern",
+    "makeRecord": {
+      "title": "Production Records",
+      "total": "entries",
+      "condition": "Filters",
+      "status": "Status",
+      "time": "Time",
+      "startTime": "Start",
+      "endTime": "End",
+      "reset": "Reset",
+      "filter": "Filter",
+      "export": "Export",
+      "exportTips": "Export filtered data?",
+      "reason": "Failure Reason",
+      "all": "All",
+      "success": "Success",
+      "warning": "Machine Error",
+      "waiting": "Pending"
+    }
   },
   "alarmClockSet": {
     "monday": "Mon",
@@ -1117,6 +1136,11 @@
     "total": "Total ",
     "inTotal": " roles",
     "addTo": "Add Group",
+    "commonPermission": "Common Permissions",
+    "remotePermission": "Remote Permissions",
+    "deleteTip": "Delete this role? This action cannot be undone!",
+    "toggleAll": "Select All",
+    "toggleNoAll": "Invert Selection",
     "edit": "Edit",
     "delete": "Delete",
     "operationConfirmation": "Confirm",
@@ -1702,5 +1726,34 @@
     "M19": "APK",
     "M21": "MQTT",
     "M22": "Terminals"
+  },
+  "remote": {
+    "C1": "Restart Machine",
+    "C2": "Sleep Toggle",
+    "C3": "Start Heating",
+    "C4": "Stop Heating",
+    "C5": "Remote Unlock",
+    "C6": "Remote Production",
+    "C7": "Timer Control",
+    "C8": "Volume Adjust",
+    "C9": "Parameter Adjustment",
+    "C10": "Distribution Setup",
+    "C11": "Price Adjustment",
+    "C12": "Material Monitor",
+    "C13": "Product Management",
+    "C14": "Change Password",
+    "C15": "DIY Patterns (Candy Floss)",
+    "C16": "Restart Touchscreen",
+    "C17": "Maintenance (Candy Floss)",
+    "C18": "Coin Refund",
+    "C19": "Offline Mode",
+    "C20": "View Logs",
+    "C21": "Delete Machine",
+    "C22": "Custom Logo",
+    "C23": "Remote Lock",
+    "C24": "Cooling Mode (Ice Cream)",
+    "C25": "Mixing Mode (Ice Cream)",
+    "C26": "Freshness Mode (Ice Cream)",
+    "C27": "Defrost Mode (Ice Cream)"
   }
 }

+ 54 - 1
src/assets/language/es.json

@@ -95,6 +95,7 @@
         "total": "Total: ",
         "permissions": " permisos",
         "rolePermissions": "Permisos rol",
+        "chirdAccountTotal": "Total Subcuentas",
         "add": "Subcuentas",
         "screen": "Filtrar",
         "fullName": "Nombre",
@@ -627,7 +628,25 @@
             "adRulePlace": "Seleccionar regla"
         },
         "flowerType": "Tipos de diseño",
-        "flowerTypePlace": "Seleccionar diseño"
+        "flowerTypePlace": "Seleccionar diseño",
+        "makeRecord": {
+            "title": "Historial de Producción",
+            "total": "registros",
+            "condition": "Filtros",
+            "status": "Estado",
+            "time": "Tiempo",
+            "startTime": "Inicio",
+            "endTime": "Fin",
+            "reset": "Reiniciar",
+            "filter": "Filtrar",
+            "export": "Exportar",
+            "exportTips": "¿Exportar datos filtrados?",
+            "reason": "Motivo del Fallo",
+            "all": "Todos",
+            "success": "Éxito",
+            "warning": "Error de Máquina",
+            "waiting": "Pendiente"
+        }
     },
     "alarmClockSet": {
         "monday": "L",
@@ -1116,6 +1135,11 @@
         "total": "Total: ",
         "inTotal": " roles",
         "addTo": "Añadir grupo",
+        "commonPermission": "Permisos comunes",
+        "remotePermission": "Permisos remotos",
+        "deleteTip": "¿Eliminar este rol? ¡Esta acción es irreversible!",
+        "toggleAll": "Seleccionar todo",
+        "toggleNoAll": "Invertir selección",
         "edit": "Editar",
         "delete": "Eliminar",
         "operationConfirmation": "Confirmación",
@@ -1701,5 +1725,34 @@
         "M19": "APK",
         "M21": "MQTT",
         "M22": "Terminales"
+    },
+    "remote": {
+        "C1": "Reiniciar máquina",
+        "C2": "Alternar suspensión",
+        "C3": "Encender calefacción",
+        "C4": "Apagar calefacción",
+        "C5": "Desbloqueo remoto",
+        "C6": "Producción remota",
+        "C7": "Temporizador",
+        "C8": "Ajustar volumen",
+        "C9": "Ajuste parámetros",
+        "C10": "Config. distribución",
+        "C11": "Ajuste precios",
+        "C12": "Monitor materiales",
+        "C13": "Gestión productos",
+        "C14": "Cambiar contraseña",
+        "C15": "Diseños DIY (Algodón)",
+        "C16": "Reiniciar pantalla táctil",
+        "C17": "Limpieza (Algodón)",
+        "C18": "Devolución monedas",
+        "C19": "Modo offline",
+        "C20": "Ver registros",
+        "C21": "Eliminar máquina",
+        "C22": "Logo personalizado",
+        "C23": "Bloqueo remoto",
+        "C24": "Refrigeración (Helado)",
+        "C25": "Mezcla (Helado)",
+        "C26": "Conservación (Helado)",
+        "C27": "Descongelación (Helado)"
     }
 }

+ 55 - 2
src/assets/language/fr.json

@@ -95,6 +95,7 @@
         "total": "Total ",
         "permissions": " permissions",
         "rolePermissions": "Rôles",
+        "chirdAccountTotal": "Total Sous-comptes",
         "add": "Ajouter",
         "screen": "Filtrer",
         "fullName": "Nom complet",
@@ -449,7 +450,7 @@
         "jam": "Confiture",
         "nuts​": "Noix",
         "confirmMake": "Confirmer production",
-        "todaysSugarList": "Productions du jour",
+        "todaysSugarList": "Liste productions",
         "todaysMakeList": "Liste productions",
         "goods": "Produit :",
         "doTime": ", Heure :",
@@ -627,7 +628,25 @@
             "adRulePlace": "Sélectionner règle"
         },
         "flowerType": "Type motif",
-        "flowerTypePlace": "Sélectionner motif"
+        "flowerTypePlace": "Sélectionner motif",
+        "makeRecord": {
+            "title": "Historique de production",
+            "total": "entrées",
+            "condition": "Filtres",
+            "status": "Statut",
+            "time": "Période",
+            "startTime": "Début",
+            "endTime": "Fin",
+            "reset": "Réinitialiser",
+            "filter": "Filtrer",
+            "export": "Exporter",
+            "exportTips": "Exporter avec les filtres actuels ?",
+            "reason": "Raison de l'échec",
+            "all": "Tous",
+            "success": "Réussi",
+            "warning": "Erreur machine",
+            "waiting": "En attente"
+        }
     },
     "alarmClockSet": {
         "monday": "Lun",
@@ -1116,6 +1135,11 @@
         "total": "Total ",
         "inTotal": " rôles",
         "addTo": "Ajouter groupe",
+        "commonPermission": "Permissions communes",
+        "remotePermission": "Autorisations distantes",
+        "deleteTip": "Supprimer ce rôle ? Cette action est irréversible !",
+        "toggleAll": "Tout sélectionner",
+        "toggleNoAll": "Inverser sélection",
         "edit": "Modifier",
         "delete": "Supprimer",
         "operationConfirmation": "Confirmation",
@@ -1732,5 +1756,34 @@
         "M19": "APK",
         "M21": "MQTT",
         "M22": "Terminaux"
+    },
+    "remote": {
+        "C1": "Redémarrer machine",
+        "C2": "Mode veille",
+        "C3": "Activer chauffage",
+        "C4": "Désactiver chauffage",
+        "C5": "Déverrouillage distant",
+        "C6": "Production à distance",
+        "C7": "Programmateur",
+        "C8": "Réglage volume",
+        "C9": "Réglage paramètres",
+        "C10": "Param. distribution",
+        "C11": "Modifier prix",
+        "C12": "Surveillance matières",
+        "C13": "Gestion produits",
+        "C14": "Modifier mot de passe",
+        "C15": "Motifs DIY (Barbe à papa)",
+        "C16": "Redémarrer écran tactile",
+        "C17": "Nettoyage (Barbe à papa)",
+        "C18": "Remboursement",
+        "C19": "Mode hors ligne",
+        "C20": "Voir logs",
+        "C21": "Supprimer machine",
+        "C22": "Logo personnalisé",
+        "C23": "Verrouillage distant",
+        "C24": "Réfrigération (Glace)",
+        "C25": "Mélange (Glace)",
+        "C26": "Conservation (Glace)",
+        "C27": "Décongélation (Glace)"
     }
 }

+ 56 - 3
src/assets/language/ja.json

@@ -94,6 +94,7 @@
         "total": "合計",
         "permissions": "権限",
         "rolePermissions": "役割権限",
+        "chirdAccountTotal": "サブアカウント総数",
         "add": "サブアカウント",
         "screen": "フィルター",
         "fullName": "フルネーム",
@@ -443,8 +444,8 @@
         "jam": "ジャム",
         "nuts​": "砕いたナッツ",
         "confirmMake": "リモート制作を確定します",
-        "todaysSugarList": "今日の綿菓子リスト",
-        "todaysMakeList": "今日の製造リスト",
+        "todaysSugarList": "製造リスト",
+        "todaysMakeList": "製造リスト",
         "goods": "商品:",
         "doTime": "、時間:",
         "byDeice": "、機械:",
@@ -621,7 +622,25 @@
             "adRulePlace": "広告ルールを先に選択"
         },
         "flowerType": "デザイン種類",
-        "flowerTypePlace": "デザインの種類を選択"
+        "flowerTypePlace": "デザインの種類を選択",
+        "makeRecord": {
+            "title": "製造記録",
+            "total": "件",
+            "condition": "フィルター",
+            "status": "ステータス",
+            "time": "時間",
+            "startTime": "開始",
+            "endTime": "終了",
+            "reset": "リセット",
+            "filter": "フィルター",
+            "export": "エクスポート",
+            "exportTips": "フィルター後のデータをエクスポートしますか?",
+            "reason": "失敗理由",
+            "all": "すべて",
+            "success": "成功",
+            "warning": "機器異常",
+            "waiting": "未製造"
+        }
     },
     "alarmClockSet": {
         "monday": "月",
@@ -1103,6 +1122,11 @@
         "total": "合計 ",
         "inTotal": " 個の役割",
         "addTo": "権限グループに追加",
+        "commonPermission": "常用権限",
+        "remotePermission": "遠隔権限",
+        "deleteTip": "このロールを削除しますか?この操作は元に戻せません!",
+        "toggleAll": "全選択",
+        "toggleNoAll": "選択反転",
         "edit": "編集",
         "delete": "削除",
         "operationConfirmation": "操作確認",
@@ -1693,5 +1717,34 @@
         "M19": "APK",
         "M21": "MQTT",
         "M22": "端末"
+    },
+    "remote": {
+        "C1": "マシン再起動",
+        "C2": "スリープ切替",
+        "C3": "ヒーター起動",
+        "C4": "ヒーター停止",
+        "C5": "遠隔解錠",
+        "C6": "遠隔製造",
+        "C7": "タイマー設定",
+        "C8": "音量調整",
+        "C9": "パラメータ設定",
+        "C10": "販売網設定",
+        "C11": "価格変更",
+        "C12": "材料モニター",
+        "C13": "商品管理",
+        "C14": "パスワード変更",
+        "C15": "DIY型設定(綿菓子)",
+        "C16": "タッチパネル再起動",
+        "C17": "メンテナンス(綿菓子)",
+        "C18": "コイン返却",
+        "C19": "オフラインモード",
+        "C20": "ログ確認",
+        "C21": "マシン削除",
+        "C22": "ロゴ設定",
+        "C23": "遠隔ロック",
+        "C24": "冷却モード(アイスクリーム)",
+        "C25": "攪拌モード(アイスクリーム)",
+        "C26": "鮮度保持(アイスクリーム)",
+        "C27": "解凍モード(アイスクリーム)"
     }
 }

+ 56 - 3
src/assets/language/pt.json

@@ -95,6 +95,7 @@
         "total": "Total: ",
         "permissions": " permissões",
         "rolePermissions": "Perfis de acesso",
+        "chirdAccountTotal": "Total de Subcontas",
         "add": "Adicionar",
         "screen": "Filtrar",
         "fullName": "Nome",
@@ -449,8 +450,8 @@
         "jam": "Geleia",
         "nuts​": "Granulado",
         "confirmMake": "Confirmar Produção",
-        "todaysSugarList": "Produção Diária",
-        "todaysMakeList": "Produções de Hoje",
+        "todaysSugarList": "Registros de produção",
+        "todaysMakeList": "Registros de produção",
         "goods": "Produto:",
         "doTime": ", Horário:",
         "byDeice": ", Equipamento:",
@@ -627,7 +628,25 @@
             "adRulePlace": "Selecione uma Regra"
         },
         "flowerType": "Tipos de Modelo",
-        "flowerTypePlace": "Selecione o Modelo"
+        "flowerTypePlace": "Selecione o Modelo",
+        "makeRecord": {
+            "title": "Registros de Produção",
+            "total": "registros",
+            "condition": "Filtros",
+            "status": "Status",
+            "time": "Período",
+            "startTime": "Início",
+            "endTime": "Fim",
+            "reset": "Redefinir",
+            "filter": "Filtrar",
+            "export": "Exportar",
+            "exportTips": "Exportar dados filtrados?",
+            "reason": "Motivo da Falha",
+            "all": "Todos",
+            "success": "Sucesso",
+            "warning": "Erro da Máquina",
+            "waiting": "Pendente"
+        }
     },
     "alarmClockSet": {
         "monday": "Seg",
@@ -1116,6 +1135,11 @@
         "total": "Total: ",
         "inTotal": " perfis",
         "addTo": "Adicionar Grupo",
+        "commonPermission": "Permissões comuns",
+        "remotePermission": "Permissões remotas",
+        "deleteTip": "Excluir esta função? Esta ação é irreversível!",
+        "toggleAll": "Selecionar tudo",
+        "toggleNoAll": "Inverter seleção",
         "edit": "Editar",
         "delete": "Excluir",
         "operationConfirmation": "Confirmação",
@@ -1701,5 +1725,34 @@
         "M19": "APK",
         "M21": "MQTT",
         "M22": "Terminais"
+    },
+    "remote": {
+        "C1": "Reiniciar máquina",
+        "C2": "Alternar sono",
+        "C3": "Ligar aquecimento",
+        "C4": "Desligar aquecimento",
+        "C5": "Destravar remotamente",
+        "C6": "Produção remota",
+        "C7": "Controle temporal",
+        "C8": "Ajustar volume",
+        "C9": "Ajustar parâmetros",
+        "C10": "Config. distribuição",
+        "C11": "Ajustar preços",
+        "C12": "Monitor materiais",
+        "C13": "Gestão produtos",
+        "C14": "Alterar senha",
+        "C15": "Padrões DIY (Algodão-doce)",
+        "C16": "Reiniciar touchscreen",
+        "C17": "Limpeza (Algodão-doce)",
+        "C18": "Reembolso moedas",
+        "C19": "Modo offline",
+        "C20": "Ver logs",
+        "C21": "Excluir máquina",
+        "C22": "Logo personalizado",
+        "C23": "Bloqueio remoto",
+        "C24": "Resfriamento (Sorvete)",
+        "C25": "Mistura (Sorvete)",
+        "C26": "Conservação (Sorvete)",
+        "C27": "Descongelamento (Sorvete)"
     }
 }

+ 56 - 3
src/assets/language/ru.json

@@ -95,6 +95,7 @@
         "total": "Всего: ",
         "permissions": " прав",
         "rolePermissions": "Права роли",
+        "chirdAccountTotal": "Всего подаккаунтов",
         "add": "Добавить",
         "screen": "Фильтр",
         "fullName": "Имя",
@@ -449,8 +450,8 @@
         "jam": "Джем",
         "nuts​": "Крошка",
         "confirmMake": "Подтвердить",
-        "todaysSugarList": "Сегодняшние заказы",
-        "todaysMakeList": "Производство",
+        "todaysSugarList": "Запись производства",
+        "todaysMakeList": "Запись производства",
         "goods": "Товар:",
         "doTime": ", время:",
         "byDeice": ", уст-во:",
@@ -627,7 +628,25 @@
             "adRulePlace": "Выберите"
         },
         "flowerType": "Тип узора",
-        "flowerTypePlace": "Выберите"
+        "flowerTypePlace": "Выберите",
+        "makeRecord": {
+            "title": "История производства",
+            "total": "записей",
+            "condition": "Фильтры",
+            "status": "Статус",
+            "time": "Время",
+            "startTime": "Начало",
+            "endTime": "Конец",
+            "reset": "Сброс",
+            "filter": "Фильтр",
+            "export": "Экспорт",
+            "exportTips": "Экспортировать данные с текущими фильтрами?",
+            "reason": "Причина сбоя",
+            "all": "Все",
+            "success": "Успешно",
+            "warning": "Ошибка машины",
+            "waiting": "Ожидание"
+        }
     },
     "alarmClockSet": {
         "monday": "Пн",
@@ -1116,6 +1135,11 @@
         "total": "Всего: ",
         "inTotal": " ролей",
         "addTo": "+ Группа прав",
+        "commonPermission": "Общие права",
+        "remotePermission": "Удалённые права",
+        "deleteTip": "Удалить эту роль? Действие необратимо!",
+        "toggleAll": "Выбрать всё",
+        "toggleNoAll": "Инвертировать выбор",
         "edit": "Изменить",
         "delete": "Удалить",
         "operationConfirmation": "Подтверждение",
@@ -1732,5 +1756,34 @@
         "M19": "APK",
         "M21": "MQTT",
         "M22": "Терминалы"
+    },
+    "remote": {
+        "C1": "Перезагрузка машины",
+        "C2": "Переключение сна",
+        "C3": "Включить нагрев",
+        "C4": "Выключить нагрев",
+        "C5": "Дистанционная разблокировка",
+        "C6": "Удалённое производство",
+        "C7": "Таймер",
+        "C8": "Громкость",
+        "C9": "Настройки",
+        "C10": "Настройки дистрибуции",
+        "C11": "Корректировка цен",
+        "C12": "Мониторинг",
+        "C13": "Управление товарами",
+        "C14": "Сменить пароль",
+        "C15": "Пользовательские формы (Сахарная вата)",
+        "C16": "Перезагрузка сенсора",
+        "C17": "Техобслуживание (Сахарная вата)",
+        "C18": "Возврат монет",
+        "C19": "Автономный режим",
+        "C20": "Просмотр логов",
+        "C21": "Удалить машину",
+        "C22": "Настройка лого",
+        "C23": "Дистанционная блокировка",
+        "C24": "Охлаждение (Мороженое)",
+        "C25": "Перемешивание (Мороженое)",
+        "C26": "Сохранение свежести (Мороженое)",
+        "C27": "Разморозка (Мороженое)"
     }
 }

+ 56 - 3
src/assets/language/zh.json

@@ -95,6 +95,7 @@
     "total": "共 ",
     "permissions": " 条权限",
     "rolePermissions": "角色权限",
+    "chirdAccountTotal": "子账户总数",
     "add": "子商家",
     "screen": "筛选",
     "fullName": "姓名",
@@ -449,8 +450,8 @@
     "jam": "果酱",
     "nuts​": "果碎",
     "confirmMake": "确定远程制作",
-    "todaysSugarList": "今日做糖列表",
-    "todaysMakeList": "今日制作列表",
+    "todaysSugarList": "远程制作记录",
+    "todaysMakeList": "远程制作记录",
     "goods": "商品:",
     "doTime": ",时间:",
     "byDeice": ",设备:",
@@ -627,7 +628,25 @@
       "adRulePlace": "请先选择广告规则"
     },
     "flowerType": "花型种类",
-    "flowerTypePlace": "请选择花型种类"
+    "flowerTypePlace": "请选择花型种类",
+    "makeRecord": {
+      "title": "制作记录",
+      "total": "条",
+      "condition": "筛选条件",
+      "status": "制作状态",
+      "time": "制作时间",
+      "startTime": "开始时间",
+      "endTime": "结束时间",
+      "reset": "重置",
+      "filter": "筛选",
+      "export": "导出数据",
+      "exportTips": "确认导出当前筛选条件下的数据吗?",
+      "reason": "制作失败原因",
+      "all": "全部",
+      "success": "成功",
+      "warning": "机器异常",
+      "waiting": "未制作"
+    }
   },
   "alarmClockSet": {
     "monday": "一",
@@ -1117,6 +1136,11 @@
     "total": "共 ",
     "inTotal": " 个角色",
     "addTo": "添加权限组",
+    "commonPermission": "常用权限",
+    "remotePermission": "远程权限",
+    "deleteTip": "确定要删除该角色吗?此操作不可恢复!",
+    "toggleAll": "全选",
+    "toggleNoAll": "反选",
     "edit": "编辑",
     "delete": "删除",
     "operationConfirmation": "操作确认",
@@ -1702,5 +1726,34 @@
     "M19": "APK",
     "M21": "MQTT",
     "M22": "终端管理"
+  },
+  "remote": {
+    "C1": "重启设备",
+    "C2": "睡眠开关",
+    "C3": "开启炉头",
+    "C4": "关闭炉头",
+    "C5": "远程开门",
+    "C6": "远程制作",
+    "C7": "定时开关",
+    "C8": "音量调节",
+    "C9": "参数调整",
+    "C10": "设置分销",
+    "C11": "修改价格",
+    "C12": "物料监控",
+    "C13": "商品管理",
+    "C14": "修改机器密码",
+    "C15": "DIY花型(棉花糖)",
+    "C16": "重启触摸屏",
+    "C17": "设备清洗(棉花糖)",
+    "C18": "远程退币",
+    "C19": "系统脱机",
+    "C20": "查看日志",
+    "C21": "删除设备",
+    "C22": "自定Logo",
+    "C23": "远程锁机",
+    "C24": "制冷模式(冰淇淋)",
+    "C25": "搅拌模式(冰淇淋)",
+    "C26": "保鲜模式(冰淇淋)",
+    "C27": "解冻模式(冰淇淋)"
   }
 }

二进制
src/assets/taskMessage/makeIcon.png


+ 31 - 0
src/common/js/utils.js

@@ -27,6 +27,37 @@ export const $M_Menus = {
   "M21": t("permission.M21"),//MQTT
   "M22": t("permission.M22"),//终端管理
 }
+
+// 远程操作权限
+export const remoteMenus = {
+  "C1": t("remote.C1"),
+  "C2": t("remote.C2"),
+  "C3": t("remote.C3"),
+  "C4": t("remote.C4"),
+  "C5": t("remote.C5"),
+  "C6": t("remote.C6"),
+  "C7": t("remote.C7"),
+  "C8": t("remote.C8"),
+  "C9": t("remote.C9"),
+  "C10": t("remote.C10"),
+  "C11": t("remote.C11"),
+  "C12": t("remote.C12"),
+  "C13": t("remote.C13"),
+  "C14": t("remote.C14"),
+  "C15": t("remote.C15"),
+  "C16": t("remote.C16"),
+  "C17": t("remote.C17"),
+  "C18": t("remote.C18"),
+  "C19": t("remote.C19"),
+  "C20": t("remote.C20"),
+  "C21": t("remote.C21"),
+  "C22": t("remote.C22"),
+  "C23": t("remote.C23"),
+  "C24": t("remote.C24"),
+  "C25": t("remote.C25"),
+  "C26": t("remote.C26"),
+  "C27": t("remote.C27")
+}
 // 判断值是否是数字 true:数值型的,false:非数值型
 export const $M_IsNaN = (num) => {
   return typeof num === 'number' && !isNaN(num);

+ 20 - 20
src/components/typeDownMenu/index.vue

@@ -24,7 +24,7 @@
 </template>
 
 <script>
-import { onMounted, toRefs, reactive, ref } from "vue";
+import { onMounted, toRefs, computed, reactive, ref } from "vue";
 import { getAdminList, getEquipmentList } from '@/service/typeSelectList';
 import { getLoginUser } from '@/common/js/utils';
 import { useI18n } from 'vue-i18n';
@@ -46,9 +46,9 @@ export default {
     // 支付方式
     const paymentType = ref(null);
     // 支付方式标题
-    const paymentTypeTitle = ref(t('typeSelectList.paymentMethod'));
+    const paymentTypeTitle = computed(() =>(t('typeSelectList.paymentMethod')));
     // 支付方式列表
-    const payList = ref(user.ifForeign === '0' ? [
+    const payList = computed(() =>(user.ifForeign === '0' ? [
       { text: t('typeSelectList.whole'), value: '' },
       { text: t('typeSelectList.mainSweepOfAlipay'), value: 'ALIPAY_NATIVE' },
       { text: t('typeSelectList.weChatScanning'), value: 'WEIXIN_NATIVE' },
@@ -63,7 +63,7 @@ export default {
       { text: t('typeSelectList.coinsNotes'), value: 3 },
       { text: t('typeSelectList.creditCard'), value: 4 },
       { text: t('typeSelectList.electronicPayment'), value: 5 },
-    ]);
+    ]));
     // 选择支付方式
     const paymentTypeChange = (value) => {
       paymentTypeTitle.value = payList.value.find(payList => payList.value === value).text;
@@ -75,16 +75,16 @@ export default {
     // 公司平台
     const companyType = ref(null);
     // 公司平台标题
-    const companyTypeTitle = ref(t('typeSelectList.companies'));
+    const companyTypeTitle = computed(() =>(t('typeSelectList.companies')));
     // 公司平台列表
-    const companyList = [
+    const companyList = computed(() =>[
       { text: t('typeSelectList.whole'), value: '' },
       { text: t('typeSelectList.sz'), value: '0' },
       { text: t('typeSelectList.sc'), value: '1' },
-    ];
+    ]);
     // 选择公司平台
     const companyTypeChange = (value) => {
-      companyTypeTitle.value = companyList.find(companyList => companyList.value === value).text;
+      companyTypeTitle.value = companyList.value.find(companyList => companyList.value === value).text;
       searchParams.companyType = value;
       outputDate();
     }
@@ -92,15 +92,15 @@ export default {
     // 账号类型
     const foreignType = ref(null);
     // 账号类型标题
-    const foreignTypeTitle = ref(t('typeSelectList.ifForeignes'));
+    const foreignTypeTitle = computed(() =>(t('typeSelectList.ifForeignes')));
     // 账号地区列表
-    const foreignList = [
+    const foreignList = computed(() =>[
       { text: t('typeSelectList.mainland'), value: '0' },
       { text: t('typeSelectList.overseas'), value: '1' },
-    ];
+    ]);
     // 选择账号类型
     const foreignTypeChange = (value) => {
-      foreignTypeTitle.value = foreignList.find(foreignList => foreignList.value === value).text;
+      foreignTypeTitle.value = foreignList.value.find(foreignList => foreignList.value === value).text;
       if (value == "0") {
         payList.value = [
           { text: t('typeSelectList.whole'), value: '' },
@@ -128,17 +128,17 @@ export default {
     // 设备类型
     const machineType = ref(null);
     // 设备类型标题
-    const machineTypeTitle = ref(t('typeSelectList.machines'));
+    const machineTypeTitle = computed(() =>(t('typeSelectList.machines')));
     // 设备类型列表
-    const machineList = [
+    const machineList = computed(() =>[
       { text: t('typeSelectList.whole'), value: '' },
       { text: t('typeSelectList.MG'), value: '0' },
       { text: t('typeSelectList.POP'), value: '1' },
       { text: t('typeSelectList.ICE'), value: '2' },
-    ];
+    ]);
     // 选择设备类型
     const machineTypeChange = (value) => {
-      machineTypeTitle.value = machineList.find(machineList => machineList.value === value).text;
+      machineTypeTitle.value = machineList.value.find(machineList => machineList.value === value).text;
       searchParams.machineType = value;
       outputDate();
     }
@@ -173,18 +173,18 @@ export default {
     // 订单状态
     const orderStatus = ref(null);
     // 订单状态标题
-    const orderStatusTitle = ref(t('typeSelectList.orderStatus'));
+    const orderStatusTitle = computed(() =>(t('typeSelectList.orderStatus')));
     // 订单状态列表
-    const orderStatusList = [
+    const orderStatusList = computed(() =>[
       { text: t('typeSelectList.whole'), value: '' },
       { text: t('typeSelectList.unpaid'), value: 0 },
       { text: t('typeSelectList.paid'), value: 1 },
       { text: t('typeSelectList.refunding'), value: 2 },
       { text: t('typeSelectList.refunded'), value: 3 },
-    ]
+    ]);
     // 选择订单状态
     const orderStatusChange = (value) => {
-      orderStatusTitle.value = orderStatusList.find(orderStatusList => orderStatusList.value === value).text;
+      orderStatusTitle.value = orderStatusList.value.find(orderStatusList => orderStatusList.value === value).text;
       searchParams.status = value;
       outputDate();
     }

+ 10 - 0
src/service/device/index.js

@@ -82,6 +82,16 @@ export function selectSugarList(params) {
     return axios.get(`/SZWL-SERVER/tSugarDo/selectSugarList?${stringToUrl(params)}`);
 }
 
+// 远程制作列表
+export function selectMakeList(params) {
+    return axios.post(`/SZWL-SERVER/tSugarDo/selectSugarRecord`, params);
+}
+
+// 导出制作列表
+export function exportMakeList(params) {
+    return axios.post(`/SZWL-SERVER/tSugarDo/exportSugarRecord`, params, { responseType: 'blob' });
+}
+
 // 查询定时开关列表
 export function getAlaramClockList(params) {
     return axios.get(`/SZWL-SERVER/tAlarmClock/list?${stringToUrl(params)}`);

+ 12 - 0
src/service/purse/index.js

@@ -0,0 +1,12 @@
+import axios from "../../utils/axios";
+import { stringToUrl } from '@/common/js/utils';
+
+// MQTT发送消息
+export function sendMqttMessage(params) {
+  return axios.post(`/SZWL-SERVER/purse/balance`, params);
+}
+
+// 获取余额
+export function getBalance(params) {
+  return axios.get(`/SZWL-SERVER/purse/balance?${stringToUrl(params)}`);
+}

+ 5 - 0
src/service/user.js

@@ -64,4 +64,9 @@ export function getAreaById(params) {
 // 海外账号获取所属地区
 export function getCitiesById(params) {
   return axios.get(`/SZWL-SERVER/tAdmin/getCitiesById?${stringToUrl(params)}`, params);
+}
+
+// 获取账号权限
+export function getAdminRole(params) {
+  return axios.get(`/SZWL-SERVER/tAdmin/getPermission?${stringToUrl(params)}`, params);
 }

+ 11 - 0
src/styles/taskMessage/index.less

@@ -61,6 +61,17 @@
           right: 0.2rem;
           top: 0.45rem;
         }
+
+        &.makeIcon::after {
+          content: '';
+          position: absolute;
+          background: #fff url('../../assets/taskMessage/makeIcon.png') top center no-repeat;
+          background-size: 100%;
+          width: 0.55rem;
+          height: 0.55rem;
+          right: 0.2rem;
+          top: 0.45rem;
+        }
       }
 
       .taskRight {

+ 151 - 62
src/views/accountPer/add.vue

@@ -1,69 +1,69 @@
 <template>
   <!-- 新增账号 -->
-  <div class="accountPerPage flex-col">
+  <div class="accountPerPage">
     <s-header :name="pageTitle" :noback="false"></s-header>
-    <div class="accountPerBox flex-col">
-      <van-form @submit="onSubmit">
-        <van-field v-model="username" name="username" :label="$t('accountPer.usernameLabel')" :readonly="isUserName"
-          :placeholder="$t('accountPer.usernamePlaceholder')"
-          :rules="[{ pattern: /^[a-zA-Z][a-zA-Z0-9]*$/, message: $t('register.usernameRequired') }]" />
-        <van-field v-model="password" name="password" type="password" :label="$t('accountPer.passwordLabel')"
-          :placeholder="passwordPlaceholder" :rules="[
-      { required: havePassword, message: $t('accountPer.passwordPlaceholder') },
-      // { pattern: passwordPattern, message: $t('register.passwordPattern') }
-    ]" />
-        <van-field v-model="name" name="name" :label="$t('accountPer.nameLabel')"
-          :placeholder="$t('accountPer.namePlaceholder')" :rules="[
-      { required: true, message: $t('accountPer.namePlaceholder') },
-    ]" />
-        <van-field v-model="phone" name="phone" :label="$t('accountPer.phoneLabel')"
-          :placeholder="$t('accountPer.phonePlaceholder')" :rules="[
-      { required: true, message: $t('accountPer.phonePlaceholder') },
-    ]" />
-        <van-field name="radio" label="类型" v-if="user.type === 0">
+    <div class="accountPerBox">
+      <van-form @submit="onSubmit" class="modern-form">
+        <!-- 表单字段 -->
+        <van-field v-model="username" :label="$t('accountPer.usernameLabel')"
+          :placeholder="$t('accountPer.usernamePlaceholder')" left-icon="user-o" class="modern-field" />
+
+        <van-field v-model="password" type="password" :label="$t('accountPer.passwordLabel')"
+          :placeholder="passwordPlaceholder" left-icon="lock" class="modern-field" />
+
+        <van-field v-model="name" :label="$t('accountPer.nameLabel')" :placeholder="$t('accountPer.namePlaceholder')"
+          left-icon="contact" class="modern-field" />
+
+        <van-field v-model="phone" :label="$t('accountPer.phoneLabel')" :placeholder="$t('accountPer.phonePlaceholder')"
+          left-icon="phone" class="modern-field" />
+
+        <!-- 类型选择 -->
+        <van-field name="radio" v-if="user.type === 0" class="radio-field">
           <template #input>
-            <van-radio-group v-model="type" direction="horizontal">
-              <van-radio name="1">公司人员</van-radio>
-              <van-radio name="4">营销经理</van-radio>
+            <van-radio-group v-model="type" class="radio-group">
+              <van-radio name="1" class="radio-item">公司人员</van-radio>
+              <van-radio name="4" class="radio-item">营销经理</van-radio>
             </van-radio-group>
           </template>
         </van-field>
+
+        <!-- 角色选择 -->
         <van-field v-model="roleText" is-link readonly :label="$t('accountPer.roleLabel')"
-          :placeholder="$t('accountPer.rolePlaceholder')" @click="roleShow = true"
-          :rules="[{ required: true, message: $t('accountPer.rolePlaceholder') }]" />
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label">
-            <label id="van-field-4-label" for="van-field-4-input">{{ $t("accountPer.setUp") }}</label>
-          </div>
-          <div class="van-cell__value">
-            <div class="van-field__body">
-              <van-switch v-model="isEnabled" size="20px" class="o-mr-10" /><span>{{
-      $t("accountPer.startOrNot")
-    }}</span>
+          :placeholder="$t('accountPer.rolePlaceholder')" left-icon="friends-o" class="modern-field"
+          @click="roleShow = true" />
+
+        <!-- 启用开关 -->
+        <van-field class="switch-field">
+          <template #input>
+            <div class="switch-container">
+              <van-switch v-model="isEnabled" size="20px" />
+              <span class="switch-label">{{ $t("accountPer.startOrNot") }}</span>
             </div>
-          </div>
-        </div>
-        <van-field :border="false" @click-input="busiEquipInpClk" readonly clearable
-          v-model="cofficentForm.equipmentNames" :label="$t('accountPer.manageMachinesLabel')"
-          :placeholder="$t('accountPer.manageMachinesPlaceholder')">
-          <!-- :rules="[{ required: true, message: $t('accountPer.manageMachinesPlaceholder') }]" -->
+          </template>
+        </van-field>
+
+        <!-- 设备管理 -->
+        <van-field v-model="cofficentForm.equipmentNames" :label="$t('accountPer.manageMachinesLabel')"
+          :placeholder="$t('accountPer.manageMachinesPlaceholder')" left-icon="cluster-o" class="modern-field" readonly
+          clearable @click="busiEquipInpClk">
           <template #right-icon>
-            <div class="l-flex-RC">
-              <van-icon v-if="cofficentForm.equipmentNames"
-                @click="cofficentForm.equipmentNames = ''; equipmentIds = ''" class="o-mr-6" name="clear" />
-              <van-icon @click="busiEquipInpClk" name="arrow" />
+            <div class="field-icons">
+              <van-icon v-if="cofficentForm.equipmentNames" name="clear" @click="cofficentForm.equipmentNames = ''; equipmentIds = ''" />
+              <van-icon name="arrow" @click="busiEquipInpClk"/>
             </div>
           </template>
         </van-field>
-        <!-- 操作 -->
-        <van-row justify="space-around" style="padding: 1em">
-          <van-button span="5" round type="primary" style="height: 2em; padding: 0 2em" native-type="submit">{{
-      $t("accountPer.submit") }}</van-button>
-        </van-row>
-        <van-row justify="space-around" style="padding: 1em" v-if="pageTitle === $t('accountPer.modifyAccount')">
-          <van-button span="5" round type="danger" style="height: 2em; padding: 0 2em" @click="deleteLoginUserFun()">{{
-      $t("accountPer.delete") }}</van-button>
-        </van-row>
+
+        <!-- 操作按钮 -->
+        <div class="action-buttons">
+          <van-button round type="primary" native-type="submit" class="submit-btn">
+            {{ $t("accountPer.submit") }}
+          </van-button>
+          <van-button v-if="pageTitle === $t('accountPer.modifyAccount')" round type="danger" class="delete-btn"
+            @click="deleteLoginUserFun">
+            {{ $t("accountPer.delete") }}
+          </van-button>
+        </div>
       </van-form>
     </div>
     <van-popup v-model:show="roleShow" round position="bottom">
@@ -155,7 +155,6 @@ export default {
         passwordPlaceholder.value = t('register.passwordPlaceholder');
         havePassword.value = true;
       }
-      console.log(passwordPattern.value)
       getSysRoleListFun();
       getEquipmentListFun();
     });
@@ -163,9 +162,12 @@ export default {
     const getSysRoleListFun = async () => {
       const { data } = await getSysRoleList({ adminId: user.id });
       if (data.code === "00000") {
-        roleOptions.value = data.data.map((item) => {
-          return { text: item.label, value: item.value };
-        });
+        roleOptions.value = data.data
+          .filter(item => item.label !== "商家")
+          .map(item => ({
+            text: item.label,
+            value: item.value
+          }));
         if (pageTitle.value === t("accountPer.modifyAccount")) {
           roleText.value = roleOptions.value.filter(
             (v) => v.value === addParams.roleList
@@ -265,7 +267,6 @@ export default {
     const busiEquipInpClk = () => {
       if (cofficentForm.value.equipmentNames) {
         let list = cofficentForm.value.equipmentNames.split(",");
-        console.log(list, "list");
         kSelectPopEquipRef.value.selPopOpen(list);
       } else {
         kSelectPopEquipRef.value.selPopOpen();
@@ -276,13 +277,9 @@ export default {
     const kSelectPopEquipRef = ref(null);
     // 机器弹窗的确定按钮
     const selEquipConfirm = (e, e1) => {
-      console.log("e", e);
-      console.log("e1", e1);
       if (e.length > 0) {
         cofficentForm.value.equipmentNames = e.join(",");
         addParams.equipmentIds = e1.join(",");
-        console.log("equipmentNames", cofficentForm.value.equipmentNames);
-        console.log("equipmentIds", addParams.equipmentIds);
       } else {
         cofficentForm.value.equipmentNames = "";
         addParams.equipmentIds = '';
@@ -320,4 +317,96 @@ export default {
 
 <style lang="less" scoped>
 @import "../../common/style/common.less";
+@primary-color: #4d6add;
+@danger-color: #FF6B6B;
+
+.modern-form {
+  padding: 16px;
+
+  .modern-field {
+    margin-bottom: 16px;
+    border-radius: 8px;
+    background: #f8f9ff;
+    
+    :deep(.van-field__label) {
+      color: #666;
+      font-size: 14px;
+    }
+    
+    :deep(.van-field__control) {
+      color: #333;
+      font-size: 14px;
+    }
+    
+    :deep(.van-field__left-icon) {
+      color: @primary-color;
+      margin-right: 8px;
+    }
+  }
+
+  .radio-field {
+    :deep(.van-field__body) {
+      padding: 8px 0;
+    }
+  }
+
+  .radio-group {
+    display: flex;
+    gap: 24px;
+    
+    .radio-item {
+      margin: 0;
+      :deep(.van-radio__label) {
+        color: #333;
+      }
+    }
+  }
+
+  .switch-field {
+    :deep(.van-field__body) {
+      padding: 8px 0;
+    }
+    
+    .switch-container {
+      display: flex;
+      align-items: center;
+      
+      .switch-label {
+        margin-left: 12px;
+        color: #333;
+        font-size: 14px;
+      }
+    }
+  }
+
+  .action-buttons {
+    margin-top: 24px;
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+    
+    .submit-btn {
+      background: @primary-color;
+      width: 100%;
+      height: 44px;
+    }
+    
+    .delete-btn {
+      background: @danger-color;
+      width: 100%;
+      height: 44px;
+    }
+  }
+
+  .field-icons {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    .van-icon {
+      color: #c0c4cc;
+      font-size: 18px;
+    }
+  }
+}
 </style>

+ 218 - 48
src/views/accountPer/index.vue

@@ -1,60 +1,62 @@
 <template>
   <!-- 账号权限 -->
-  <div class="accountPerPage flex-col">
-    <s-header :name="$t('accountPer.accountPermission')" :noback="false"></s-header>
-    <!-- <s-header :name="$t('accountPer.accountPermission')" :noback="false" targetPath="/accountOperation"></s-header> -->
-    <!-- <van-nav-bar :title="$t('accountPer.accountPermission')" left-text="" left-arrow @click-left="onClickLeft" /> -->
-
-    <div class="accountPerBox flex-col">
-      <div class="searchRow flex-row justify-between">
-        <div class="flex-col">
-          <div class="flex-row justify-between bd3">
-            <div class="flex-col outer4"></div>
-            <span class="flex-col txt2">{{ $t('accountPer.total') }}<span class="discountNumber">{{ accountPerList.length
-            }}</span>{{ $t('accountPer.permissions') }}</span>
+  <div class="account-management-page">
+    <s-header :name="$t('accountPer.accountPermission')" :noback="false" />
+
+    <div class="management-container">
+      <!-- 操作工具栏 -->
+      <div class="dashboard-toolbar">
+        <!-- 统计信息 -->
+        <div class="statistics-card">
+          <van-icon name="user" class="statistics-icon" />
+          <div class="statistics-content">
+            <div class="statistics-label">{{ $t('accountPer.chirdAccountTotal') }}</div>
+            <div class="statistics-value">{{ accountPerList.length }}</div>
           </div>
         </div>
-        <div class="flex-col">
-          <div class="operBox">
-            <div class="listBox" @click="pushRole()">
-              <img class="iconImg" src="../../assets/accountPer/listIcon.png" />
-              <span>{{ $t('accountPer.rolePermissions') }}</span>
-            </div>
-            <div class="addBox" @click="pushAdd()">
-              <img class="iconImg" src="../../assets/accountPer/addicon.png" />
-              <span>{{ $t('accountPer.add') }}</span>
-            </div>
-            <div class="searchBox" @click="searchClick()">
-              <img class="iconImg" src="../../assets/accountPer/sxIcon.png" />
-              <span>{{ $t('accountPer.screen') }}</span>
-            </div>
-          </div>
+
+        <!-- 操作按钮 -->
+        <div class="action-buttons">
+          <van-button icon="friends" class="action-btn" @click="pushRole">
+            {{ $t('accountPer.rolePermissions') }}
+          </van-button>
+          <van-button type="primary" icon="plus" class="action-btn" @click="pushAdd">
+            {{ $t('accountPer.add') }}
+          </van-button>
+          <van-button icon="filter-o" class="action-btn" @click="searchClick">
+            {{ $t('accountPer.screen') }}
+          </van-button>
         </div>
       </div>
-      <div class="listBox">
-        <div v-for="(item, index) in accountPerList" :key="index" class="listItem" @click="accountClick(item)">
-          <div class="itemBox">
-            <div class="itemRow"><span class="itemTitle">{{ $t('accountPer.fullName') }}:&nbsp;</span>{{
-              decodeURIComponent(item.name) }}</div>
-            <div class="itemRow"><span class="itemTitle">{{ $t('accountPer.role') }}:&nbsp;</span>{{ item.roleName }}
+
+      <!-- 账户列表 -->
+      <div class="account-list">
+        <van-cell-group>
+          <div v-for="(item, index) in accountPerList" :key="index" class="account-card" @click="accountClick(item)">
+            <div class="card-header">
+              <div class="account-info">
+                <h3 class="account-name">
+                  {{ decodeURIComponent(item.name) }}
+                  <van-tag :color="item.isEnabled ? '#e8f5e9' : '#ffebee'"
+                    :text-color="item.isEnabled ? '#4caf50' : '#ff5252'" class="status-tag">
+                    {{ item.isEnabled ? $t('accountPer.effective') : $t('accountPer.paused') }}
+                  </van-tag>
+                </h3>
+                <div class="account-id">@{{ item.username }}</div>
+              </div>
+              <van-icon name="arrow" class="arrow-icon" />
             </div>
-            <div class="itemRow"><span class="itemTitle">{{ $t('accountPer.accountNumber') }}:&nbsp;</span>{{
-              item.username }}
+
+            <div class="role-info">
+              <van-icon name="user" class="role-icon" />
+              <span class="role-text">{{ item.roleName }}</span>
             </div>
-            <!-- <div class="itemRow"><span class="itemTitle">邮箱:&nbsp;</span>{{showDate(item.lastUseDate)}}</div> -->
-          </div>
-          <div class="tipBox" v-if="item.isEnabled">
-            {{ $t('accountPer.effective') }}
-            <div class="outer2 flex-col"></div>
           </div>
-          <div class="tipBox isUseTip" v-if="!item.isEnabled">
-            {{ $t('accountPer.paused') }}
-            <div class="outer2 flex-col"></div>
-          </div>
-        </div>
+        </van-cell-group>
       </div>
     </div>
-    <accountPerSearch ref="searchRef" @search="search($event)"></accountPerSearch>
+
+    <accountPerSearch ref="searchRef" @search="search($event)" />
   </div>
 </template>
 
@@ -63,7 +65,7 @@ import { onMounted, reactive, toRefs, ref } from 'vue';
 import sHeader from "../../components/SimpleHeader";
 import { getChildAdminList } from '../../service/accountPar/index';
 import { showFailToast } from 'vant';
-import { getLoginUser, styleUrl } from "../../common/js/utils";
+import { getLoginUser } from "../../common/js/utils";
 import accountPerSearch from './search.vue';
 import { useRouter } from "vue-router";
 
@@ -85,7 +87,7 @@ export default {
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
-      styleUrl('accountPer')
+      // styleUrl('accountPer')
       // 初始化列表及页码
       const user = getLoginUser();
       if (user) {
@@ -144,4 +146,172 @@ export default {
 
 <style lang="less" scoped>
 @import "../../common/style/common.less";
+
+.account-management-page {
+  background: #f8f9fa;
+  min-height: 100vh;
+
+  .management-container {
+    padding: 16px;
+
+    .dashboard-toolbar {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      gap: 16px;
+      margin-bottom: 24px;
+      padding: 0 16px;
+
+      .statistics-card {
+        flex: 1;
+        background: linear-gradient(135deg, #f8f9ff, #ffffff);
+        border-radius: 12px;
+        padding: 16px;
+        display: flex;
+        align-items: center;
+        box-shadow: 0 4px 12px rgba(78, 107, 221, 0.08);
+
+        .statistics-icon {
+          font-size: 24px;
+          color: #4e6bdd;
+          margin-right: 12px;
+          padding: 8px;
+          background: rgba(78, 107, 221, 0.1);
+          border-radius: 8px;
+        }
+
+        .statistics-content {
+          .statistics-label {
+            font-size: 12px;
+            color: #8787a6;
+            margin-bottom: 4px;
+          }
+
+          .statistics-value {
+            font-size: 24px;
+            font-weight: 600;
+            color: #2c3e50;
+          }
+        }
+      }
+
+      .action-buttons {
+        display: flex;
+        gap: 12px;
+        flex-shrink: 0;
+
+        .action-btn {
+          height: 40px;
+          padding: 0 16px;
+          border-radius: 20px;
+          font-size: 14px;
+          transition: all 0.2s;
+
+          &:hover {
+            transform: translateY(-1px);
+            box-shadow: 0 4px 12px rgba(78, 107, 221, 0.15);
+          }
+
+          &:active {
+            transform: translateY(0);
+          }
+
+          // 主按钮特殊样式
+          &[type="primary"] {
+            background: linear-gradient(135deg, #4e6bdd, #3b5ab3);
+            border: none;
+          }
+        }
+      }
+    }
+
+    @media (max-width: 768px) {
+      .dashboard-toolbar {
+        flex-direction: column;
+
+        .statistics-card {
+          width: 100%;
+        }
+
+        .action-buttons {
+          width: 100%;
+          flex-wrap: wrap;
+
+          .action-btn {
+            flex: 1;
+            min-width: 120px;
+          }
+        }
+      }
+    }
+
+    .account-list {
+      .account-card {
+        margin-bottom: 12px;
+        padding: 16px;
+        background: #fff;
+        border-radius: 8px;
+        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+        transition: transform 0.2s;
+
+        &:active {
+          transform: scale(0.98);
+        }
+
+        .card-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 12px;
+
+          .account-info {
+            flex: 1;
+
+            .account-name {
+              margin: 0 0 4px 0;
+              font-size: 16px;
+              color: #2c3e50;
+              display: flex;
+              align-items: center;
+
+              .status-tag {
+                margin-left: 8px;
+                font-size: 12px;
+                border: none;
+              }
+            }
+
+            .account-id {
+              font-size: 12px;
+              color: #8787a6;
+            }
+          }
+
+          .arrow-icon {
+            color: #c0c4cc;
+            font-size: 16px;
+          }
+        }
+
+        .role-info {
+          display: flex;
+          align-items: center;
+          padding: 8px 0;
+          border-top: 1px solid #eee;
+
+          .role-icon {
+            color: #4e6bdd;
+            font-size: 14px;
+            margin-right: 6px;
+          }
+
+          .role-text {
+            font-size: 14px;
+            color: #666;
+          }
+        }
+      }
+    }
+  }
+}
 </style>

+ 0 - 3
src/views/accountPer/search.vue

@@ -6,9 +6,6 @@
         <van-form @submit="onSubmit">
           <van-field v-model="name" name="name" :label="$t('accountPer.merchantNameLabel')"
             :placeholder="$t('accountPer.merchantNamePlaceholder')" />
-          <van-field v-model="usernameText" is-link readonly name="username"
-            :label="$t('accountPer.merchantAccountLabel')" :placeholder="$t('accountPer.merchantAccountPlaceholder')"
-            @click="show = true" />
           <!-- 操作 -->
           <van-row justify="space-around" style="padding: 1em">
             <van-button span="5" round type="primary" style="height: 2em; padding: 0 2em" native-type="submit">{{

+ 63 - 103
src/views/commonTools/index.vue

@@ -5,11 +5,11 @@
         <div class="commonToolsBox flex-col">
             <div class="outer11 flex-row">
                 <div class="main25 flex-col" v-for="(item, index) in pushToolList" :key="index"
-                    @click="pushToolPage(item.value)">
-                    <img class="mod7 flex-col" :src="showLogo(item.value)" />
+                    @click="pushToolPage(item)">
+                    <img class="mod7 flex-col" :src="showLogo(item)" />
                     <div class="TextGroup14 flex-col">
                         <!-- <span class="info15" v-html="item.label"></span> -->
-                        <span class="info15">{{ $t("permission."+ item.value) }}</span>
+                        <span class="info15">{{ $t("permission." + item) }}</span>
                     </div>
                 </div>
             </div>
@@ -19,134 +19,41 @@
 
 
 <script>
-import { onMounted, reactive } from 'vue';
+import { onMounted, ref } from 'vue';
 import sHeader from "@/components/SimpleHeader";
 import { useRouter } from "vue-router";
-import { getLoginUser, styleUrl, $M_Menus } from "../../common/js/utils";
-import { useI18n } from "vue-i18n";
+import { getAdminRole } from "@/service/user";
+import { getLoginUser, styleUrl } from "../../common/js/utils";
 
 export default {
     components: { sHeader },
     setup() {
         const router = useRouter();
         const user = getLoginUser();
-        const pushToolList = reactive([]);
-        const menuList = [];
-
-        const { t } = useI18n();
-        // 赋值菜单
-        pushToolList.value = menuList;
-
-        // 设置菜单权限
-        const menuSet = () => {
-
-            // 清空菜单列表
-            // menuList.length = 0;
-            pushToolList.length = 0;
-
-            // 组合菜单权限
-            user.menuCodeList.forEach((item) => {
-                for (const key in $M_Menus) {
-                    // 设备管理M1,设备查看M2,订单数据M4,任务消息M6,销售排行M11,数据概览M14,订单退款M16,系统脱机M17 这些图标不用加载到底部菜单上
-                    if (
-                        item === key &&
-                        (
-                            item !== "M1" &&
-                            item !== "M2" && // M2本身就无logo
-                            item !== "M4" &&
-                            item !== "M6" &&
-                            item !== "M11" &&
-                            item !== "M14" &&
-                            item !== "M16" &&
-                            item !== "M17"
-                        )
-                    ) {
-                        menuList.push({
-                            label: $M_Menus[key],
-                            value: item
-                        });
-                    }
-                }
-
-            });
-
-
-            // 赋值菜单
-            // pushToolList.value = menuList;
-            pushToolList.push(...menuList);
-
-
-            // 查询是否有账号权限
-            const isAccount = user.menuCodeList.some((item) => {
-                return item === "M8";
-            });
-
-            // 如果没有账号权限
-            if (!isAccount) {
-                //  如果是公司人type=0,要把账号权限添加上去
-                if (user.type === 0) {
-                    pushToolList.push({
-                        label: t("home.accountPermission"),
-                        value: "M8"
-                    });
-                }
-            }
-
-        };
+        const pushToolList = ref([]);
+        const menuList = ref([]);
 
         // 常用操作跳转页面
         const pushToolPage = (index) => {
-            // console.log("常用操作跳转页面menuList >>> " + menuList);
             switch (index) {
-                case "M1":
-                    router.push({ path: "/device" }); // 设备管理
-                    break;
                 case "M3":
-                    // router.push({ path: "/distributionSet" });
                     router.push({ path: "/accountOperation" }); // 账户操作
                     break;
-                case "M4":
-                    router.push({ path: "/orderCenter" }); // 订单数据
-                    break;
                 case "M5":
                     router.push({ path: "/advertManage" }); // 广告管理
                     break;
-                case "M6":
-                    router.push({ path: "/taskMessage" });
-                    break;
                 case "M7":
                     router.push({ path: "/discountCode" });
                     break;
-                case "M8":
-                    router.push({ path: "/accountPer" });
-                    break;
                 case "M9":
                     router.push({ path: "/orderExport" });
                     break;
-                case "M10":
-                    router.push({ path: "/subLedgerManage" });
-                    break;
-                case "M11":
-                    router.push({ path: "/robotranking" });
-                    break;
-                case "M12":
-                    router.push({ path: "/joinpayMch" });
-                    break;
-                case "M13":
-                    router.push({ path: "/shandeMch" });
-                    break;
                 case "M15":
                     router.push({ path: "/alarmHistory" }); // 报警历史
                     break;
-                case "M18":
-                    router.push({ path: "/labelMan" });
-                    break;
                 case "M19":
                     router.push({ path: "/apkManage" });
                     break;
-                case "M20":
-                    router.push({ path: "/merchantManage" });
-                    break;
                 case "M22":
                     router.push({ path: "/terminal" });
                     break;
@@ -162,8 +69,62 @@ export default {
         onMounted(async () => {
             styleUrl('commonTools');
             // 设置菜单权限
-            menuSet();
+            // menuSet();
+            getAccountPer();
         });
+
+
+        // 解析 + 排序计算属性
+        const sortedArray = (value) => {
+            try {
+                // 1. 安全解析JSON
+                const parsed = JSON.parse(value)
+                if (!Array.isArray(parsed)) return []
+
+                // 2. 自定义排序函数
+                const getNumber = str => parseInt(str.match(/\d+/)?.[0] || 0)
+
+                // 3. 执行排序(数字从小到大)
+                return [...parsed].sort((a, b) => {
+                    return getNumber(a) - getNumber(b)
+                })
+
+            } catch (error) {
+                console.error('解析失败:', error)
+                return []
+            }
+        };
+
+        // 获取账号权限
+        const getAccountPer = async () => {
+            const { data } = await getAdminRole({ adminId: user.id });
+            if (data.code === '00000') {
+                if (data.data.menuCodesJson !== null) {
+                    menuList.value = sortedArray(data.data.menuCodesJson);
+                }
+                // 组合菜单权限
+                menuList.value.forEach((item) => {
+                    // 设备管理M1,设备查看M2,订单数据M4,任务消息M6,销售排行M11,数据概览M14,订单退款M16,系统脱机M17 这些图标不用加载到底部菜单上
+                    if (
+                        item !== "M1" &&
+                        item !== "M2" && // M2本身就无logo
+                        item !== "M4" &&
+                        item !== "M6" &&
+                        item !== "M8" &&
+                        item !== "M11" &&
+                        item !== "M12" &&
+                        item !== "M13" &&
+                        item !== "M18" &&
+                        item !== "M14" &&
+                        item !== "M16" &&
+                        item !== "M17" &&
+                        item !== "M20"
+                    ) {
+                        pushToolList.value.push(item);
+                    }
+                });
+            }
+        }
         const pushPageList = (url) => {
             router.push(url);
         }
@@ -172,7 +133,6 @@ export default {
             pushPageList,
             user,
             pushToolPage,
-            // showDataDiv,
             showLogo,
         };
     }

+ 52 - 35
src/views/device/deviceOper.vue

@@ -15,7 +15,7 @@
     <div class="group4 flex-col"></div>
     <div class="operBox">
       <!-- 重启炉头 -->
-      <div class="operItem" @click="restartFurnace()">
+      <div v-if="controlList.includes('C1')" class="operItem" @click="restartFurnace()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/restart.png" />
         </div>
@@ -24,7 +24,7 @@
         <div v-else class="operText">{{ $t("device.restart") }}</div>
       </div>
       <!-- 睡眠 -->
-      <div class="operItem" @click="sleepEquipmentFun()">
+      <div v-if="controlList.includes('C2')" class="operItem" @click="sleepEquipmentFun()">
         <div class="operIcon">
           <img v-if="sleepIcon === '0'" class="operImg" src="../../assets/device/operIcon/awake.png" />
           <img v-else class="operImg" src="../../assets/device/operIcon/offSleep.png" />
@@ -32,7 +32,7 @@
         <div class="operText">{{ sleepTitle }}</div>
       </div>
       <!-- 开启炉头/设备 -->
-      <div class="operItem" @click="openOffFurnace(1)">
+      <div v-if="controlList.includes('C3') && device.machineType != '2'" class="operItem" @click="openOffFurnace(1)">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/onOff.png" />
         </div>
@@ -41,7 +41,7 @@
         <div v-else class="operText">{{ $t("device.openDevice") }}</div>
       </div>
       <!-- 关闭炉头/设备 -->
-      <div class="operItem" @click="openOffFurnace(0)">
+      <div v-if="controlList.includes('C4')" class="operItem" @click="openOffFurnace(0)">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/onOff.png" />
         </div>
@@ -50,28 +50,32 @@
         <div v-else class="operText">{{ $t("device.closeDevice") }}</div>
       </div>
       <!-- 雪糕机制冷模式 -->
-      <div v-if="device.machineType == '2'" class="operItem" @click="updateWorkingMode(0)">
+      <div v-if="device.machineType == '2' && controlList.includes('C24')" class="operItem"
+        @click="updateWorkingMode(0)">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/refrigeration.png" />
         </div>
         <div class="operText">{{ $t("device.refrigeration") }}</div>
       </div>
       <!-- 雪糕机搅拌模式 -->
-      <div v-if="device.machineType == '2'" class="operItem" @click="updateWorkingMode(1)">
+      <div v-if="device.machineType == '2' && controlList.includes('C25')" class="operItem"
+        @click="updateWorkingMode(1)">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/stir.png" />
         </div>
         <div class="operText">{{ $t("device.stir") }}</div>
       </div>
       <!-- 雪糕机保鲜模式 -->
-      <div v-if="device.machineType == '2'" class="operItem" @click="updateWorkingMode(2)">
+      <div v-if="device.machineType == '2' && controlList.includes('C26')" class="operItem"
+        @click="updateWorkingMode(2)">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/keepFresh.png" />
         </div>
         <div class="operText">{{ $t("device.keepFresh") }}</div>
       </div>
       <!-- 雪糕机解冻模式 -->
-      <div v-if="device.machineType == '2'" class="operItem" @click="updateWorkingMode(3)">
+      <div v-if="device.machineType == '2' && controlList.includes('C27')" class="operItem"
+        @click="updateWorkingMode(3)">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/thaw.png" />
         </div>
@@ -88,14 +92,14 @@
             </div> -->
 
       <!-- 远程开门 -->
-      <div class="operItem" @click="openDoorFun()">
+      <div v-if="controlList.includes('C5')" class="operItem" @click="openDoorFun()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/openDoor.png" />
         </div>
         <div class="operText">{{ $t("device.remoteDoorOpening") }}</div>
       </div>
       <!-- 远程做糖 -->
-      <div class="operItem" @click="doSugar()">
+      <div v-if="controlList.includes('C6')" class="operItem" @click="doSugar()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/doSugar.png" />
         </div>
@@ -104,14 +108,14 @@
         <div v-else class="operText">{{ $t("device.remoteProduction") }}</div>
       </div>
       <!-- 定时开关 -->
-      <div class="operItem" @click="alarmClock()" v-if="device.equimentType != 'SI320'">
+      <div v-if="controlList.includes('C7') && device.equimentType != 'SI320'" class="operItem" @click="alarmClock()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/alarmClock.png" />
         </div>
         <div class="operText">{{ $t("device.timeSwitch") }}</div>
       </div>
       <!-- 音量调节 -->
-      <div class="operItem" @click="modulation()">
+      <div v-if="controlList.includes('C8')" class="operItem" @click="modulation()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/modulation.png" />
         </div>
@@ -123,34 +127,36 @@
         </div>
         <div class="operText">{{ $t("device.equipmentRecharge") }}</div>
       </div> -->
-      <div class="operItem" @click="paramSetPush()" v-if="device.equimentType != 'SI320' && device.machineType != '2'">
+      <div v-if="device.equimentType != 'SI320' && device.machineType != '2' && controlList.includes('C9')"
+        class="operItem" @click="paramSetPush()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/paramsSet.png" />
         </div>
         <div class="operText">{{ $t("device.parameterSetting") }}</div>
       </div>
-      <div v-if="user.ifForeign == '0'" class="operItem" @click="saveProportionPush()">
+      <div v-if=" user.ifForeign == '0' && controlList.includes('C10') " class="operItem" @click="saveProportionPush()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/saveProportion.png" />
         </div>
         <div class="operText">{{ $t("device.addDistributor") }}</div>
       </div>
-      <div v-if="isRole('M17')" class="operItem" @click="tuojiEquipmentFun()">
+      <div v-if=" controlList.includes('C19') " class="operItem" @click="tuojiEquipmentFun()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/tuoji.png" />
         </div>
         <div class="operText">{{ $t("device.sysOffline") }}</div>
       </div>
-      <div class="operItem" @click="modifyPriceClk()">
+      <div v-if=" controlList.includes('C11') " class="operItem" @click="modifyPriceClk()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/editPrice.png" />
         </div>
         <div class="operText">{{ $t("device.modifyPrice") }}</div>
       </div>
       <!-- 物料监控 -->
-      <div class="operItem" @click="materialMonitorClk()" v-if="device.equimentType != 'SI320'">
+      <div v-if=" device.equimentType != 'SI320' && controlList.includes('C12') " class="operItem"
+        @click="materialMonitorClk()">
         <div class="operIcon">
-          <img v-if="materialIcon === '0'" class="operImg" src="../../assets/device/operIcon/materialMonitor.png" />
+          <img v-if=" materialIcon === '0' " class="operImg" src="../../assets/device/operIcon/materialMonitor.png" />
           <img v-else class="operImg" src="../../assets/device/operIcon/materialMonitorOff.png" />
           <!-- <img class="operImg" src="../../assets/device/operIcon/materialMonitor.png" /> -->
         </div>
@@ -158,57 +164,58 @@
         <div class="operText">{{ materialTitle }}</div>
       </div>
       <!-- 屏蔽/展示商品 -->
-      <div class="operItem" @click="showGoodsClk()" v-if="device.equimentType != 'SI320'">
+      <div v-if=" device.equimentType != 'SI320' && controlList.includes('C13') " class="operItem"
+        @click="showGoodsClk()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/showGoods.png" />
         </div>
         <div class="operText">{{ $t("device.showGoods") }}</div>
       </div>
       <!-- DIY花型 -->
-      <div class="operItem" v-if="device.equimentType == 'MG320' || device.equimentType == 'MG330'"
-        @click="diyFlowerClk()">
+      <div v-if=" (device.equimentType == 'MG320' || device.equimentType == 'MG330') && controlList.includes('C15') "
+        class="operItem" @click="diyFlowerClk()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/diyFlower.png" />
         </div>
         <div class="operText">{{ $t("device.diyFlower") }}</div>
       </div>
       <!-- 修改机器密码 -->
-      <div v-if="user.type < 3" class="operItem" @click="changePasswordClk()">
+      <div v-if=" controlList.includes('C14') " class="operItem" @click="changePasswordClk()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/password.png" />
         </div>
         <div class="operText">{{ $t("device.changePassword") }}</div>
       </div>
       <!-- 删除设备 -->
-      <div v-if="user.type < 2" class="operItem" @click="deleteDevice()">
+      <div v-if=" controlList.includes('C21') " class="operItem" @click="deleteDevice()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/deletedevice.png" />
         </div>
         <div class="operText">{{ $t("device.deleteDevice") }}</div>
       </div>
       <!-- 日志功能 -->
-      <div v-if="user.type < 2" class="operItem" @click="viewLogs()">
+      <div v-if=" controlList.includes('C20') " class="operItem" @click="viewLogs()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/viewLogs.png" />
         </div>
         <div class="operText">{{ $t("device.viewLogs") }}</div>
       </div>
       <!-- 定制logo -->
-      <div v-if="user.type < 2" class="operItem" @click="customLogo()">
+      <div v-if=" controlList.includes('C22') " class="operItem" @click="customLogo()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/customLogo.png" />
         </div>
         <div class="operText">{{ $t("device.customLogo.customLogo") }}</div>
       </div>
       <!-- 远程锁机 -->
-      <div v-if="user.type < 1" class="operItem" @click="lockDevice()">
+      <div v-if=" controlList.includes('C23') " class="operItem" @click="lockDevice()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/lock.png" />
         </div>
         <div class="operText">{{ $t("device.lockDevice") }}</div>
       </div>
       <!-- 远程重启触摸屏 -->
-      <div class="operItem" @click="restartAndroid()">
+      <div v-if=" controlList.includes('C16') " class="operItem" @click="restartAndroid()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/restartAndroid.png" />
         </div>
@@ -222,14 +229,16 @@
         <div class="operText">{{ $t("device.viewPositioning") }}</div>
       </div> -->
       <!-- 设备清洗提醒 -->
-      <div class="operItem" v-if="device.machineType === '0' || device.machineType === null" @click="alramCleanClk()">
+      <div class="operItem"
+        v-if=" (device.machineType === '0' || device.machineType === null) && controlList.includes('C17') "
+        @click="alramCleanClk()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/alramClean.png" />
         </div>
         <div class="operText">{{ $t("device.alramClean") }}</div>
       </div>
       <div class="operItem"
-        v-if="(device.machineType === '0' || device.machineType === null) && (user.ifForeign == 1 || user.type == 0)"
+        v-if=" (device.machineType === '0' || device.machineType === null) && (user.ifForeign == 1 || user.type == 0) && controlList.includes('C18') "
         @click="returnCoinClk()">
         <div class="operIcon">
           <img class="operImg" src="../../assets/device/operIcon/coin.png" />
@@ -239,13 +248,13 @@
 
     </div>
   </van-dialog>
-  <van-dialog v-model:show="operCheckShow" :title="$t('device.operationConfirmation')" :showConfirmButton="false"
-    :showCancelButton="false">
+  <van-dialog v-model:show=" operCheckShow " :title=" $t('device.operationConfirmation') " :showConfirmButton=" false "
+    :showCancelButton=" false ">
     <div class="operCheckBox">
       <div class="block5 flex-col"></div>
       <span class="word10">{{
-    $t("device.pleaseConfirmAgainWhetherToOperate")
-  }}</span>
+        $t("device.pleaseConfirmAgainWhetherToOperate")
+        }}</span>
       <div class="block6 flex-row justify-between">
         <div class="mod7 flex-col" @click="operCheckClear()">
           <span class="info5">{{ $t("device.IllThinkAboutItAgain") }}</span>
@@ -279,6 +288,7 @@ import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { getLoginUser, styleUrl } from "@/common/js/utils";
 
+
 export default {
   emits: ['operfinish'],
   setup(props, { emit }) {
@@ -337,6 +347,8 @@ export default {
     const materialIcon = ref('0');
     // 工作模式
     const workMode = ref(null);
+    // 操作权限
+    const controlList = ref([]);
 
     // 初始化页面获取列表
     onMounted(async () => {
@@ -344,7 +356,9 @@ export default {
       styleUrl('deviceOper');
     });
     // 当前弹窗父组件触发
-    const showOper = (e) => {
+    const showOper = (e, value) => {
+      controlList.value = value;
+      console.log('controlList', controlList.value);
       device.value = e;
       show.value = true;
       // isSleep=true,机器处于睡眠状态,按钮是关闭睡眠
@@ -414,7 +428,7 @@ export default {
     };
     // 远程做糖
     const doSugar = () => {
-      router.push({ path: "doSugar", query: { deviceId: device.value.id, machineType: device.value.machineType } });
+      router.push({ path: "doSugar", query: { deviceId: device.value.id, machineType: device.value.machineType, } });
     };
     // 定时开关
     const alarmClock = () => {
@@ -645,6 +659,8 @@ export default {
       return user.menuCodeList.filter((type) => key === type).length > 0;
     };
 
+
+
     return {
       user,
       device,
@@ -684,6 +700,7 @@ export default {
       materialIcon,
       materialMonitorClk,
       updateWorkingMode,
+      controlList,
     };
   },
   components: {},

+ 1 - 1
src/views/device/doSugar.vue

@@ -293,7 +293,7 @@ export default {
     };
 
     const pushToDaySugarList = async () => {
-      router.push({ path: 'toDaySugarList', query: { deviceId: deviceId } })
+      router.push({ path: 'toDaySugarList', query: { deviceId: deviceId, clientId: deviceDetal.value.clientId } })
     }
 
     return {

+ 17 - 7
src/views/device/index.vue

@@ -237,7 +237,7 @@
                     </div>
 
                     <!-- 远程操作 -->
-                    <div class="l-flex-RC" @click="deviceOprShow(item)">
+                    <div v-if="controlList.length > 0" class="l-flex-RC" @click="deviceOprShow(item, controlList)">
                       <span class="c-text-12">{{
         $t("device.commonOperations")
       }}</span>
@@ -291,6 +291,7 @@ import { onBeforeRouteLeave, useRouter } from "vue-router";
 import dateUtil from "../../utils/dateUtil";
 import { useI18n } from "vue-i18n";
 import { Api_getLabelList } from "../../service/labelMan";
+import { getAdminRole } from "@/service/user";
 
 
 export default {
@@ -360,10 +361,22 @@ export default {
     });
     // 初始化页面获取列表
     const showAlert = ref(false);
+    // 远程操作权限
+    const controlList = ref([]);
+    // 获取账号权限
+    const getAccountPer = async () => {
+      const { data } = await getAdminRole({ adminId: user.id });
+      if (data.code === '00000') {
+        if (data.data.controlCodesJson !== null) {
+          controlList.value = JSON.parse(data.data.controlCodesJson);
+        }
+      }
+    }
     onMounted(() => {
       sleepDescBoxShow.value = true;
       init();
       updateDataInterval();
+      getAccountPer();
       // window.addEventListener('scroll', handleScroll);
       // 加载样式
       styleUrl('device');
@@ -384,16 +397,12 @@ export default {
       searchParams.current = 1;
       if (user) {
         searchParams.id = user.id;
-        // if (user.isAdmined) {
-        //   searchParams.adminName = user.username;
-        // }
         getList();
       }
     };
 
     // 获取设备标签
     const getLabelList = async () => {
-      // console.log("adminId>>>>", user.id);
       Api_getLabelList({
         adminId: user.id,
         type: "1"
@@ -457,8 +466,8 @@ export default {
       router.push({ path: "deviceSet", query: { deviceId: e.id } });
     };
     // 常用操作弹窗展示触发
-    const deviceOprShow = (e) => {
-      oprRef.value.showOper(e);
+    const deviceOprShow = (e, value) => {
+      oprRef.value.showOper(e, value);
     };
     // 消除报警
     const clearAlarm = async (e, e1, e2) => {
@@ -746,6 +755,7 @@ export default {
       selectLabel,
       restartHead,
       openCloseHead,
+      controlList,
     };
   },
 };

+ 656 - 28
src/views/device/toDaySugarList.vue

@@ -1,15 +1,137 @@
 <template>
-  <!-- 今日做糖列表 -->
-  <div class="sugarPage flex-col">
+  <!-- 做糖列表 -->
+  <div class="sugar-page">
     <s-header :name="$t('device.todaysSugarList')" :noback="false"></s-header>
-    <div class="box1 flex-col">
-      <div class="block2 flex-row justify-between">
-        <div class="block3 flex-col"></div>
-        <span class="info2">{{ $t('device.equipmentName') }}:{{ deviceDetal ? deviceDetal.name : '' }}</span>
+
+    <div class="content-container">
+      <!-- 设备信息栏 -->
+      <div class="device-header" v-if="deviceDetal">
+        <van-icon name="box" class="device-icon" />
+        <span class="device-name">{{ $t('device.equipmentName') }}:{{ deviceDetal?.name || '' }}</span>
+      </div>
+
+      <!-- 统计卡片 -->
+      <div class="stats-card">
+        <div class="stats-content">
+          <div class="stats-icon-box">
+            <van-icon name="bar-chart-o" class="stats-icon" />
+          </div>
+          <div class="stats-text">
+            <div class="stats-title">{{ $t('device.makeRecord.title') }}</div>
+            <div class="stats-info">
+              <span class="stats-number">{{ total }}</span>
+              <span class="stats-unit">{{ $t('device.makeRecord.total') }}</span>
+            </div>
+          </div>
+          <!-- 右侧操作按钮 -->
+          <div class="action-buttons">
+            <van-icon name="filter-o" class="action-icon" @click="showFilter = true" />
+            <van-icon name="down" class="action-icon" @click="showExport = true" />
+          </div>
+        </div>
       </div>
-      <van-list class="o-pt-20" v-model:loading="loading" :finished="finished" :finished-text="$t('public.noMore')"
-        @load="selectSugarListFun">
-        <van-cell v-for="(item, index) in list" :key="index" :title="showDoList(item)" />
+      <!-- 筛选弹窗 -->
+      <van-popup v-model:show="showFilter" position="right" class="filter-popup">
+        <div class="popup-header">
+          <h3>{{ $t('device.makeRecord.condition') }}</h3>
+          <van-icon name="cross" @click="showFilter = false" />
+        </div>
+
+        <div class="filter-content">
+          <!-- 设备编号输入 -->
+          <div class="filter-item" v-if="deviceDetal == null">
+            <label>{{ $t('device.equipmentCodeLabel') }}</label>
+            <van-field v-model="searchParams.clientId" :placeholder="$t('device.equipmentCodePlaceholder')" clearable />
+          </div>
+
+          <!-- 制作状态单选 -->
+          <div class="filter-item">
+            <label>{{ $t('device.makeRecord.status') }}</label>
+            <van-radio-group v-model="searchParams.status">
+              <van-cell-group>
+                <van-cell v-for="status in statusOptions" :key="status.value" clickable
+                  @click="searchParams.status = status.value">
+                  <template #title>
+                    <span class="status-label">{{ status.label }}</span>
+                  </template>
+                  <template #right-icon>
+                    <van-radio :name="status.value" />
+                  </template>
+                </van-cell>
+              </van-cell-group>
+            </van-radio-group>
+          </div>
+
+          <!-- 时间范围 -->
+          <div class="filter-item">
+            <label>{{ $t('device.makeRecord.time') }}</label>
+            <div class="date-picker">
+              <van-field v-model="searchParams.startTime" :placeholder="$t('device.makeRecord.startTime')" @click="showStartPicker = true" />
+              <van-field v-model="searchParams.endTime" :placeholder="$t('device.makeRecord.endTime')" @click="showEndPicker = true" />
+            </div>
+          </div>
+        </div>
+
+        <!-- 底部操作 -->
+        <div class="popup-footer">
+          <van-button type="default" @click="handleReset">重置</van-button>
+          <van-button type="primary" @click="handleConfirmFilter">筛选</van-button>
+        </div>
+      </van-popup>
+
+      <!-- 时间选择器 -->
+      <van-popup v-model:show="showStartPicker" position="bottom">
+        <van-date-picker :columns-type="columnsType" v-model="startDate" @confirm="handleStartConfirm"
+          @cancel="showStartPicker = false" />
+      </van-popup>
+      <van-popup v-model:show="showEndPicker" position="bottom">
+        <van-date-picker :columns-type="columnsType" v-model="endDate" @confirm="handleEndConfirm"
+          @cancel="showEndPicker = false" />
+      </van-popup>
+
+      <!-- 导出弹窗 -->
+      <van-dialog v-model:show="showExport" :title="$t('device.makeRecord.export')" :message="$t('device.makeRecord.exportTips')" show-cancel-button
+        @confirm="handleExport">
+      </van-dialog>
+
+      <!-- 做糖列表 -->
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('public.noMore')" @load="onLoad">
+        <van-cell v-for="(item, index) in list" :key="index" offset="200" class="sugar-item"
+          @click="handleItemClick(item)">
+          <template #title>
+            <div class="item-content">
+              <!-- 商品信息 -->
+              <div class="product-info">
+                <h4 class="product-name">{{ item.productName }}</h4>
+
+                <!-- 设备编号 -->
+                <div class="device-info" v-if="deviceDetal == null">
+                  <span class="device-label">{{ $t('device.equipmentCodeLabel') }}:</span>
+                  <van-tag plain type="primary" class="device-id">
+                    {{ item.clientId.slice(-6) }}
+                  </van-tag>
+                </div>
+                <div class="time-info">
+                  <van-icon name="clock" class="time-icon" />
+                  <span class="time-text">{{ showDateTime(item.createDate) }}</span>
+                </div>
+              </div>
+
+              <!-- 状态标识 -->
+              <div class="status-box" :class="statusClass(item.status)">
+                <van-icon :name="statusIcon(item.status)" class="status-icon" />
+                <span>{{ statusText(item.status) }}</span>
+              </div>
+            </div>
+          </template>
+        </van-cell>
+        <!-- 失败原因弹窗 -->
+        <van-dialog v-model:show="showReasonDialog" :title="$t('device.makeRecord.reason')" confirm-button-text="">
+          <div class="reason-content">
+            <van-icon name="warning" class="reason-icon" />
+            <p>{{ selectedItem?.note || $t('device.unknownError') }}</p>
+          </div>
+        </van-dialog>
       </van-list>
     </div>
   </div>
@@ -17,19 +139,22 @@
 <script>
 import {
   onMounted,
+  reactive,
+  computed,
   ref
 } from "vue";
 import sHeader from "@/components/SimpleHeader";
 import {
   getDeviceDetal,
-  selectSugarList
+  selectMakeList,
+  exportMakeList
 } from "@/service/device";
 import {
   showFailToast
 } from "vant";
 import {
   getLoginUser,
-  styleUrl
+  $M_ExportFile
 } from "@/common/js/utils";
 import {
   useRoute
@@ -44,21 +169,57 @@ export default {
     const { t } = useI18n();
     const route = useRoute();
     const deviceId = route.query.deviceId;
+    const clientId = route.query.clientId;
     const deviceDetal = ref(null);
     const loading = ref(false);
     const finished = ref(false);
     const user = getLoginUser();
     const list = ref([]);
+    const total = ref(0);
+    const showReasonDialog = ref(false);
+    const selectedItem = ref(null);
+    const showFilter = ref(false);
+    const showExport = ref(false);
+
+    // 时间选择
+    const columnsType = ref(["year", "month", "day"]);
+    const showStartPicker = ref(false);
+    const showEndPicker = ref(false);
+    const startDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]);
+    const endDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]);
+    const statusOptions = computed(() => [{
+      value: "",
+      label: t('device.makeRecord.all')
+    },
+    {
+      value: "0",
+      label: t('device.makeRecord.waiting')
+    }, {
+      value: "1",
+      label: t('device.makeRecord.success')
+    }, {
+      value: "2",
+      label: t('device.makeRecord.warning')
+    }]);
+    // 页面列表查询参数
+    const searchParams = reactive({
+      adminId: user.type == 2 ? user.id : '', // 用户账户id
+      status: "", // 制作状态 1:制作中 2:制作失败 0:未制作
+      clientId: clientId,
+      current: 0, // 页数
+      size: 10, // 页大小
+      startTime: "", // 开始时间
+      endTime: "", // 结束时间
+    });
+
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
-      styleUrl('doSugar');
-      getDeviceDetalFun();
-      selectSugarListFun();
-      // console.log(deviceDetal);
+      if (deviceId != null) {
+        getDeviceDetalFun();
+      }
     });
 
-
     // 获取设备数据
     const getDeviceDetalFun = async () => {
       const {
@@ -75,22 +236,28 @@ export default {
 
     // 获取做糖列表数据
     const selectSugarListFun = async () => {
-      // console.log(user);
-      const {
-        data
-      } = await selectSugarList({
-        adminId: user.id
-      });
+      finished.value = false;
+      const { data } = await selectMakeList(searchParams);
       if (data.code) {
-        // list.value = data.data.map((item) => {
-        //   return item.productName;
-        // });
-        list.value = data.data;
-        finished.value = true;
+        list.value = list.value.concat(
+          data.data.records
+        );
+        total.value = data.data.total;
+        if (list.value.length === data.data.total) {
+          finished.value = true;
+        }
+        loading.value = false;
       } else {
         showFailToast(data.message);
       }
     };
+    // 滚动加载
+    const onLoad = () => {
+      if (!finished.value) {
+        searchParams.current = searchParams.current + 1;
+        selectSugarListFun();
+      }
+    };
     const showDoList = (item) => {
       if (!item) {
         return "";
@@ -99,13 +266,119 @@ export default {
       return t('device.goods') + item.productName + t('device.doTime') + currentDate + t('device.byDeice') + item.clientId.slice(-6);
     }
 
+    const showDateTime = (date) => {
+      if (!date) {
+        return "";
+      }
+      const currentDate = new Date(dateUtil.formateDate(new Date(date), "yyyy/MM/dd hh:mm:ss"));
+      return dateUtil.timeZoneDate(currentDate);
+    };
+
+    const statusClass = (status) => {
+      return {
+        success: status === '1',
+        processing: status === '0',
+        failed: status === '2'
+      }
+    }
+
+    const statusText = (status) => {
+      switch (status) {
+        case '1':
+          return t('device.makeRecord.success');
+        case '2':
+          return t('device.makeRecord.warning');
+        default:
+          return t('device.makeRecord.waiting');
+      }
+    }
+
+    const statusIcon = (status) => {
+      switch (status) {
+        case '1':
+          return 'checked';
+        case '2':
+          return 'warning';
+        default:
+          return 'clock';
+      }
+    }
+
+    // 时间选择
+    const handleStartConfirm = ({ selectedValues }) => {
+      console.log(selectedValues.join('/'));
+      searchParams.startTime = selectedValues.join('/');
+      showStartPicker.value = false;
+    }
+
+    // 时间选择
+    const handleEndConfirm = ({ selectedValues }) => {
+      console.log(selectedValues.join('/'));
+      searchParams.endTime = selectedValues.join('/');
+      showEndPicker.value = false;
+    }
+
+    // 制作失败原因
+    const handleItemClick = (item) => {
+      if (item.status === '2') { // 仅失败状态可点击
+        selectedItem.value = item;
+        showReasonDialog.value = true;
+      }
+    }
+
+    // 重置
+    const handleReset = () => {
+      searchParams.status = "";
+      searchParams.startTime = "";
+      searchParams.endTime = "";
+      searchParams.clientId = clientId;
+    }
+
+    // 确认
+    const handleConfirmFilter = () => {
+      showFilter.value = false;
+      searchParams.current = 1;
+      list.value = [];
+      selectSugarListFun();
+      console.log(finished.value);
+    }
+
+    // 导出
+    const handleExport = async () => {
+      const { headers, data } = await exportMakeList(Object.assign({}, searchParams));
+      $M_ExportFile(data, headers);
+    }
+
     return {
+      searchParams,
       list,
       deviceDetal,
       loading,
       finished,
       selectSugarListFun,
-      showDoList
+      showDoList,
+      showDateTime,
+      statusClass,
+      statusText,
+      statusIcon,
+      total,
+      onLoad,
+      showReasonDialog,
+      selectedItem,
+      handleItemClick,
+      showFilter,
+      statusOptions,
+      showExport,
+      showStartPicker,
+      showEndPicker,
+      startDate,
+      endDate,
+      columnsType,
+      handleReset,
+      handleConfirmFilter,
+      handleStartConfirm,
+      handleEndConfirm,
+      handleExport,
     };
   },
   components: {
@@ -115,4 +388,359 @@ export default {
 </script>
 <style lang="less" scoped>
 @import "../../common/style/common";
+
+.sugar-page {
+  background: #f5f6fa;
+  overflow-y: auto;
+
+  .content-container {
+    padding: 16px;
+  }
+
+  .device-header {
+    display: flex;
+    align-items: center;
+    padding: 12px;
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
+
+    .device-icon {
+      font-size: 18px;
+      color: #409eff;
+      margin-right: 8px;
+    }
+
+    .device-name {
+      font-size: 14px;
+      color: #333;
+    }
+  }
+
+  .stats-card {
+    background: #ffffff;
+    border-radius: 12px;
+    padding: 18px;
+    margin: 16px 0;
+    position: relative;
+    overflow: hidden;
+
+    &::before {
+      content: "";
+      position: absolute;
+      top: -20px;
+      right: -20px;
+      width: 80px;
+      height: 80px;
+      background: #f0f3ff;
+      border-radius: 50%;
+    }
+
+    .stats-content {
+      display: flex;
+      align-items: center;
+      position: relative;
+      z-index: 1;
+    }
+
+    .stats-icon-box {
+      width: 36px; // 容器尺寸缩小
+      height: 36px; // 容器尺寸缩小
+      background: linear-gradient(135deg, #4e6bdd 0%, #6b8eff 100%);
+      border-radius: 8px; // 圆角缩小
+      margin-right: 12px; // 右边距缩小
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .stats-icon {
+        font-size: 18px;
+        color: white;
+      }
+    }
+
+    .stats-text {
+      .stats-title {
+        color: #666;
+        font-size: 12px;
+        margin-bottom: 4px;
+      }
+
+      .stats-info {
+        display: flex;
+        align-items: baseline;
+
+        .stats-number {
+          color: #4e6bdd;
+          font-size: 22px;
+          font-weight: 600;
+          margin-right: 6px;
+        }
+
+        .stats-unit {
+          color: #999;
+          font-size: 12px;
+        }
+      }
+    }
+
+    .action-buttons {
+      display: flex;
+      gap: 16px; // 增大按钮间距
+      margin-left: auto; // 确保右对齐
+
+      .action-icon {
+        font-size: 24px; // 增大图标尺寸
+        color: #ffffff; // 白色图标
+        padding: 8px; // 增大点击区域
+        background: #4e6bdd; // 主题色背景
+        border-radius: 8px; // 圆角匹配整体风格
+        box-shadow: 0 2px 6px rgba(78, 107, 221, 0.3); // 添加投影
+        transition: all 0.2s ease;
+
+        // 悬停效果
+        &:active {
+          transform: scale(0.95);
+          background: #3b5ab3;
+          box-shadow: 0 1px 3px rgba(78, 107, 221, 0.2);
+        }
+
+      }
+
+      // 单独设置导出按钮颜色
+      .van-icon-down {
+        background: #66bb6a; // 绿色表示导出操作
+
+        &:active {
+          background: #4caf50;
+        }
+      }
+    }
+  }
+
+  .sugar-item {
+    margin-top: 12px;
+    border-radius: 8px;
+    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
+    cursor: pointer;
+
+    :deep(.van-cell__title) {
+      overflow: visible;
+    }
+
+    .item-content {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 12px 0;
+    }
+
+    .product-info {
+      position: relative;
+      flex: 1;
+
+      .product-name {
+        margin: 0 0 8px 0;
+        font-size: 15px;
+        color: #333;
+      }
+
+      // 设备编号样式
+      .device-info {
+        margin: 6px 0;
+        display: flex;
+        align-items: center;
+
+        .device-label {
+          color: #888;
+          font-size: 12px;
+          margin-right: 4px;
+        }
+
+        .device-id {
+          font-size: 12px;
+          padding: 2px 6px;
+          background: #f0f7ff;
+        }
+      }
+
+      .time-info {
+        display: flex;
+        align-items: center;
+
+        .time-icon {
+          font-size: 14px;
+          color: #999;
+          margin-right: 4px;
+        }
+
+        .time-text {
+          font-size: 12px;
+          color: #999;
+        }
+      }
+    }
+
+
+    .status-box {
+      padding: 4px 10px;
+      border-radius: 14px;
+      font-size: 12px;
+      display: flex;
+      align-items: center;
+
+      &.success {
+        background: #e8f5e9;
+        color: #4caf50;
+      }
+
+      &.processing {
+        background: #ebf3ff;
+        color: #2196f3;
+      }
+
+      &.failed {
+        background: #fff0f0;
+        cursor: pointer;
+        color: #ff4444;
+        transition: opacity 0.3s;
+
+        &:active {
+          opacity: 0.8;
+        }
+      }
+
+      .status-icon {
+        margin-right: 4px;
+        font-size: 14px;
+      }
+    }
+
+    &:active {
+      background: #f8f9ff;
+    }
+  }
+
+  .reason-content {
+    padding: 16px;
+    display: flex;
+    align-items: flex-start;
+
+    .reason-icon {
+      color: #ff4444;
+      font-size: 18px;
+      margin-right: 12px;
+      flex-shrink: 0;
+    }
+
+    p {
+      margin: 0;
+      color: #666;
+      font-size: 14px;
+      line-height: 1.6;
+      white-space: pre-wrap;
+    }
+  }
+
+  .filter-popup {
+    width: 85%;
+    height: 100%;
+
+    .popup-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 16px;
+      border-bottom: 1px solid #eee;
+
+      h3 {
+        margin: 0;
+        font-size: 18px;
+      }
+
+      .van-icon {
+        font-size: 20px;
+        color: #999;
+      }
+    }
+
+    .filter-content {
+      padding: 16px;
+
+      .filter-item {
+        margin-bottom: 20px;
+
+        label {
+          font-size: 14px;
+          color: #606266;
+          margin-bottom: 8px;
+          display: block;
+        }
+
+        // 设备编号输入框样式
+        .van-field {
+          background: #f5f7fa;
+          border-radius: 4px;
+          padding: 10px 12px;
+
+          &::placeholder {
+            color: #c0c4cc;
+          }
+        }
+
+        .van-radio-group {
+          width: 100%;
+        }
+
+        .van-cell {
+
+          // 选中状态样式
+          &--selected {
+            background-color: #f0f3ff;
+
+            .status-label {
+              color: #4e6bdd;
+            }
+          }
+        }
+
+        .van-radio__icon--checked .van-icon {
+          background-color: #4e6bdd;
+          border-color: #4e6bdd;
+        }
+
+        .date-picker {
+          display: grid;
+          grid-template-columns: 1fr 1fr;
+          gap: 8px;
+        }
+      }
+    }
+
+    .popup-footer {
+      position: fixed;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      display: flex;
+      gap: 12px;
+      padding: 16px;
+      background: #fff;
+      border-top: 1px solid #eee;
+
+      .van-button {
+        flex: 1;
+      }
+    }
+  }
+
+  .export-content {
+    padding: 16px;
+
+    p {
+      color: #666;
+      margin: 0 0 12px 0;
+    }
+  }
+}
 </style>

+ 63 - 103
src/views/home/index.vue

@@ -2,7 +2,8 @@
   <!-- 主页 -->
   <div class="homePage flex-col">
     <div class="homeBox">
-      <s-header :name="sys ? sys.title : (sysTitle == 'AETI GLOBAL' ? sysTitle : $t('public.sysName'))" :noback="true" :isFixed="false"></s-header>
+      <s-header :name="sys ? sys.title : (sysTitle == 'AETI GLOBAL' ? sysTitle : $t('public.sysName'))" :noback="true"
+        :isFixed="false"></s-header>
       <!-- 留言滚动条 -->
       <template v-if="noticeContent.title">
         <van-notice-bar @click="noticeClk" mode="link" :scrollable="true" color="rgba(64,77,116,1)" background="#fff"
@@ -78,10 +79,10 @@
         </div>
         <div class="outer11 flex-row">
           <div class="main25 flex-col" v-for="(item, index) in pushToolList" :key="index"
-            @click="pushToolPage(item.value)">
-            <img class="mod7 flex-col" :src="showLogo(item.value)" />
+            @click="pushToolPage(item)">
+            <img class="mod7 flex-col" :src="showLogo(item)" />
             <div class="TextGroup14 flex-col">
-              <span class="info15">{{ $t("permission."+ item.value) }}</span>
+              <span class="info15">{{ $t("permission." + item) }}</span>
             </div>
           </div>
         </div>
@@ -161,9 +162,8 @@ import kDialog from "../../components/commom/kDialog/index.vue";
 import { onMounted, ref, nextTick } from "vue";
 import sHeader from "../../components/SimpleHeader";
 import dateSelectList from "../../components/dateSelectList";
-// import typeSelectList from "../../components/typeSelectList";
 import typeDownMenu from "../../components/typeDownMenu";
-import { getLoginUser, $M_Menus, Format_time, styleUrl } from "../../common/js/utils";
+import { getLoginUser, Format_time, styleUrl } from "../../common/js/utils";
 import { useRouter } from "vue-router";
 import {
   getStatistics,
@@ -173,6 +173,7 @@ import {
   getIsAlarm,
   Api_getEquipmentPageStatistics,
 } from "../../service/home";
+import { getAdminRole } from "@/service/user";
 import dateUtil from "../../utils/dateUtil";
 import { useI18n } from "vue-i18n";
 import { showFailToast, showToast } from "vant";
@@ -183,13 +184,10 @@ export default {
   name: "home",
   components: {
     sHeader,
-    // navBar,
     dateSelectList,
-    // typeSelectList,
     typeDownMenu,
     kDialog,
     kNoData,
-    // [Popup.name]: Popup,
   },
   setup() {
     // 是否显示机器人
@@ -437,7 +435,7 @@ export default {
       }
       firstLogin.value = localStorage.getItem('firstLogin');
       // 设置菜单权限
-      menuSet();
+      // menuSet();
       // 设置菜单权限, 只执行一次
       // once(menuSet);
       dateSelect.value = {
@@ -451,6 +449,8 @@ export default {
           "yyyy-MM-dd hh:mm:ss"
         ),
       };
+      // 获取账号权限
+      getAccountPer();
       typeSelectData = {
         userName: user.username,
         clientId: null,
@@ -563,110 +563,70 @@ export default {
 
     const menuList = [];
 
-    const showDataDiv = ref(false);
+    const showDataDiv = ref(true);
 
-    const isOrderData = ref(false);
+    const isOrderData = ref(true);
 
-    // 设置菜单权限
-    const menuSet = () => {
-
-      // 清空菜单列表
-      menuList.length = 0;
-
-      // 组合菜单权限
-      user.menuCodeList.forEach((item) => {
-        for (const key in $M_Menus) {
+    // 获取账号权限
+    const getAccountPer = async () => {
+      const { data } = await getAdminRole({ adminId: user.id });
+      if (data.code === '00000') {
+        if (data.data.menuCodesJson !== null) {
+          menuList.value = sortedArray(data.data.menuCodesJson);
+        }
+        // 子商家以上级别,默认可以看到订单详情
+        if (user.type === 3 && !menuList.value.includes("M4")) {
+          // 如果有数据概览权限 和 订单数据权限
+          if (!menuList.value.includes("M4")) {
+            showDataDiv.value = false;
+          }
+          if (!menuList.value.includes("M14")) {
+            isOrderData.value = false;
+          }
+        }
+        // 组合菜单权限
+        menuList.value.forEach((item) => {
           // 设备管理M1,设备查看M2,订单数据M4,任务消息M6,销售排行M11,数据概览M14,订单退款M16,系统脱机M17 这些图标不用加载到底部菜单上
           if (
-            item === key &&
-            (
-              item !== "M1" &&
-              item !== "M2" && // M2本身就无logo
-              item !== "M4" &&
-              item !== "M6" &&
-              item !== "M11" &&
-              item !== "M14" &&
-              item !== "M16" &&
-              item !== "M17"
-            )
+            item !== "M1" &&
+            item !== "M2" && // M2本身就无logo
+            item !== "M4" &&
+            item !== "M6" &&
+            item !== "M8" &&
+            item !== "M11" &&
+            item !== "M12" &&
+            item !== "M13" &&
+            item !== "M18" &&
+            item !== "M14" &&
+            item !== "M16" &&
+            item !== "M17" &&
+            item !== "M20"
           ) {
-            menuList.push({
-              label: $M_Menus[key],
-              value: item
-            });
+            pushToolList.value.push(item);
           }
-        }
-
-      });
-
-      // 赋值菜单
-      pushToolList.value = menuList;
-
-      // 查询是否有apk管理权限
-      const isApkMan = user.menuCodeList.some((item) => {
-        return item === "M19";
-      });
-
-      // 查询是否有账号权限
-      const isAccount = user.menuCodeList.some((item) => {
-        return item === "M8";
-      });
-
-      // 查询是否有订单数据权限
-      isOrderData.value = user.menuCodeList.some((item) => {
-        return item === "M4";
-      })
-
-      // 查询是否有数据概览权限
-      const isDataOverview = user.menuCodeList.some((item) => {
-        return item === "M14";
-      })
-
-      // 如果没有apk管理
-      if (!isApkMan) {
-        // 如果是公司人,要把apk添加上去
-        // if (user.type === 0 || user.type === 1) {
-        //   pushToolList.value.push({
-        //     label: t("home.apkMan"),
-        //     value: "M19"
-        //   });
-        // }
+        });
       }
+    }
 
-      if (user.type === 0) {
-        pushToolList.value.push(
-          {
-            label: t("permission.M21"),
-            value: "M21"
-          },
-          {
-            label: t("permission.M22"),
-            value: "M22"
-          }
-        );
-      }
+    // 解析 + 排序计算属性
+    const sortedArray = (value) => {
+      try {
+        // 1. 安全解析JSON
+        const parsed = JSON.parse(value)
+        if (!Array.isArray(parsed)) return []
 
-      // 如果没有账号权限
-      if (!isAccount) {
-        //  如果是公司人type=0,要把账号权限添加上去
-        if (user.type === 0) {
-          pushToolList.value.push({
-            label: t("home.accountPermission"),
-            value: "M8"
-          });
-        }
-      }
+        // 2. 自定义排序函数
+        const getNumber = str => parseInt(str.match(/\d+/)?.[0] || 0)
 
-      // 子商家以上级别,默认可以看到订单详情
-      if (user.type === 0 || user.type === 2) {
-        showDataDiv.value = true;
-      } else {
-        // 如果有数据概览权限 和 订单数据权限
-        if (isDataOverview) {
-          showDataDiv.value = true;
-        }
-      }
+        // 3. 执行排序(数字从小到大)
+        return [...parsed].sort((a, b) => {
+          return getNumber(a) - getNumber(b)
+        })
 
+      } catch (error) {
+        console.error('解析失败:', error)
+        return []
+      }
     };
 
     // 常用操作跳转页面

+ 45 - 7
src/views/purse/index.vue

@@ -51,7 +51,7 @@
                             <h3>今日收款余额(元)</h3>
                         </div>
                     </div>
-                    <div class="amount">8,888.00</div>
+                    <div class="amount">{{ formatAmount(receivableBalance) }}</div>
                     <van-button icon="cash-back-record" @click="handleRecharge">
                         立即充值
                     </van-button>
@@ -69,7 +69,7 @@
                             <h3>可提现余额(元)</h3>
                         </div>
                     </div>
-                    <div class="amount">6,666.00</div>
+                    <div class="amount">{{ formatAmount(withdrawableBalance) }}</div>
                     <van-button icon="cash-on-deliver" @click="handleWithdraw">
                         立即提现
                     </van-button>
@@ -156,12 +156,17 @@
 
 <script>
 import sHeader from "../../components/SimpleHeader";
+import { getBalance } from "../../service/purse/index"
+import { onMounted, ref } from "vue";
+import { getLoginUser } from "../../common/js/utils";
 
 export default {
     components: { sHeader },
     setup() {
 
-
+        const user = getLoginUser();
+        const receivableBalance = ref(0.00); // 今日收款余额
+        const withdrawableBalance = ref(0.00); // 可提现余额
         const handleRecharge = () => {
             console.log('handleRecharge');
         };
@@ -177,12 +182,44 @@ export default {
         const goToManualRecord = () => {
             console.log('goToManualRecord');
         }
+        onMounted(async () => {
+            queryBalance();
+        });
+
+        // 查询余额
+        const queryBalance = async () => {
+            try {
+                const { data } = await getBalance({ adminId: user.id });
+                console.log('data', data);
+                if (data.code === "00000") {
+                    if (data.data.receivableBalance != null) {
+                        receivableBalance.value = data.data.receivableBalance;
+                    }
+                    if (data.data.withdrawableBalance != null) {
+                        withdrawableBalance.value = data.data.withdrawableBalance;
+                    }
+                }
+            } catch (error) {
+                console.log('error', error);
+            }
+        };
+
+        // 格式化金额
+        const formatAmount = (amount) => {
+            return new Intl.NumberFormat('en-US', {
+                minimumFractionDigits: 2,
+                maximumFractionDigits: 2
+            }).format(Number(amount))
+        };
 
         return {
             handleRecharge,
             handleWithdraw,
             goToAutoRecord,
-            goToManualRecord
+            goToManualRecord,
+            receivableBalance,
+            withdrawableBalance,
+            formatAmount
         };
     }
 };
@@ -560,8 +597,9 @@ export default {
 
 /* 占位元素样式 */
 .bottom-spacer {
-  height: 50px;
-  height: calc(60px + env(safe-area-inset-bottom));
-  pointer-events: none; /* 防止遮挡点击 */
+    height: 50px;
+    height: calc(60px + env(safe-area-inset-bottom));
+    pointer-events: none;
+    /* 防止遮挡点击 */
 }
 </style>

+ 313 - 101
src/views/role/add.vue

@@ -1,47 +1,77 @@
 <template>
   <!-- 角色权限 - 添加角色 -->
-  <div class="rolePage flex-col">
+  <div class="role-container">
     <s-header :name="pageTitle" :noback="false"></s-header>
-    <div class="accountPerBox flex-col">
+    <div class="permission-wrapper">
       <van-form @submit="onSubmit">
-        <van-field v-model="roleName" name="roleName" :label="$t('role.roleNameLabel')"
-          :placeholder="$t('role.roleNamePlaceholder')"
-          :rules="[{ required: true, message: $t('role.roleNamePlaceholder') }]" />
-        <van-checkbox-group v-model="roleMenuCode" style="
-            display: flex;
-            flex-wrap: wrap;
-            align-items: center;
-            justify-content: flex-start;
-          ">
-          <van-checkbox v-for="(item, index) in menuCodeList" :key="index" :name="item.value"
-            style="width: 160px; padding: 10px">{{ item.label }}</van-checkbox>
-        </van-checkbox-group>
-        <!-- 操作 -->
-        <van-row justify="space-around" style="padding: 1em">
-          <van-button span="5" round type="primary" style="height: 2em; padding: 0 2em" native-type="submit">
+        <!-- 角色名称输入 -->
+        <van-cell-group class="input-card">
+          <van-field v-model="roleName" name="roleName" :label="$t('role.roleNameLabel')"
+            :placeholder="$t('role.roleNamePlaceholder')"
+            :rules="[{ required: true, message: $t('role.roleNamePlaceholder') }]" clearable />
+        </van-cell-group>
+
+        <!-- 全局操作按钮 -->
+        <!-- <div class="bulk-actions">
+          <van-button size="small" @click="toggleAll(true)">全选</van-button>
+          <van-button size="small" @click="toggleAll(false)">反选</van-button>
+        </div> -->
+
+        <!-- 权限分类 -->
+        <div class="permission-section">
+          <!-- 常用操作权限 -->
+          <div class="permission-card common-card">
+            <div class="card-header">
+              <div class="header-left">
+                <van-icon name="todo-list" class="card-icon" />
+                <h3 class="card-title">{{ $t('role.commonPermission') }}</h3>
+              </div>
+              <div class="header-actions">
+                <van-button size="mini" @click="toggleCategory('common', true)" class="action-btn">{{ $t('role.toggleAll') }}</van-button>
+                <van-button size="mini" @click="toggleCategory('common', false)" class="action-btn">{{ $t('role.toggleNoAll') }}</van-button>
+              </div>
+            </div>
+            <van-checkbox-group v-model="roleMenuCode">
+              <div class="checkbox-grid">
+                <van-checkbox v-for="(item, index) in menuCodeList" :key="'common-' + index" :name="item"
+                  shape="square" icon-size="18" class="perm-item">
+                  <span class="perm-label">{{ $t("permission." + item) }}</span>
+                </van-checkbox>
+              </div>
+            </van-checkbox-group>
+          </div>
+
+          <!-- 远程操作权限 -->
+          <div class="permission-card remote-card" v-if="roleMenuCode.includes('M1')">
+            <div class="card-header">
+              <div class="header-left">
+                <van-icon name="setting" class="card-icon warn-icon" />
+                <h3 class="card-title">{{ $t('role.remotePermission') }}</h3>
+              </div>
+              <div class="header-actions">
+                <van-button size="mini" @click="toggleCategory('remote', true)" class="action-btn">{{ $t('role.toggleAll') }}</van-button>
+                <van-button size="mini" @click="toggleCategory('remote', false)" class="action-btn">{{ $t('role.toggleNoAll') }}</van-button>
+              </div>
+            </div>
+            <van-checkbox-group v-model="controlCode">
+              <div class="checkbox-grid">
+                <van-checkbox v-for="(item, index) in remoteCodeList" :key="'remote-' + index" :name="item"
+                  shape="square" icon-size="18" class="perm-item danger-item">
+                  <span class="perm-label">{{ $t("remote." + item) }}</span>
+                </van-checkbox>
+              </div>
+            </van-checkbox-group>
+          </div>
+        </div>
+
+        <!-- 提交按钮 -->
+        <div class="submit-footer">
+          <van-button round block type="primary" native-type="submit" class="submit-btn">
             {{ pageTitle }}
           </van-button>
-        </van-row>
+        </div>
       </van-form>
     </div>
-    <!-- <van-popup v-model:show="roleShow" round position="bottom">
-      <van-cascader
-          v-model="roleList"
-          :title="$t('role.pleaseSelectARole')"
-          :options="roleOptions"
-          @close="roleShow = false"
-          @finish="onRoleFinish"
-      />
-    </van-popup>
-    <van-popup v-model:show="equipmentIdShow" round position="bottom">
-      <van-cascader
-          v-model="equipmentIds"
-          :title="$t('role.pleaseSelectAPattern')"
-          :options="equipmentIdsOptions"
-          @close="equipmentIdShow = false"
-          @finish="onEquipmentIdFinish"
-      />
-    </van-popup> -->
   </div>
 </template>
 
@@ -49,9 +79,8 @@
 import { onMounted, reactive, toRefs, ref } from "vue";
 import sHeader from "../../components/SimpleHeader";
 import { showFailToast, showSuccessToast } from "vant";
-// import { getSysRoleList, getEquipmentList } from "../../service/accountPar/index";
 import { addSysRole, updateSysRole } from "../../service/role/index";
-import { getLoginUser, $M_Menus, styleUrl } from "../../common/js/utils";
+import { getLoginUser, $M_Menus, remoteMenus } from "../../common/js/utils";
 import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 
@@ -66,100 +95,105 @@ export default {
     const roleName = ref("");
     // 设置的权限
     const menuCodeList = ref([]);
+    const remoteCodeList = ref([]);
+
     const roleMenuCode = ref([]);
-    // const roleShow = ref(false);
+    const controlCode = ref([]);
+
     const roleText = ref("");
-    // const roleOptions = ref([]);
-    // const onRoleFinish = ({ selectedOptions }) => {
-    //   roleShow.value = false;
-    //   roleText.value = selectedOptions[0].text;
-    // };
-    // const equipmentIdShow = ref(false);
     const equipmentIdText = ref("");
     const equipmentIdsOptions = ref([]);
-    // const onEquipmentIdFinish = ({ selectedOptions }) => {
-    //   equipmentIdShow.value = false;
-    //   equipmentIdText.value = selectedOptions[0].text;
-    // };
+
+    // 全选/反选逻辑
+    const toggleAll = (checked) => {
+      const allCommon = menuCodeList.value.map(i => i)
+      const allRemote = remoteCodeList.value.map(i => i)
+
+      roleMenuCode.value = checked ? allCommon : []
+      controlCode.value = checked ? allRemote : []
+    }
+
+    // 分类全选
+    const toggleCategory = (type, checked) => {
+      const values = type === 'common'
+        ? menuCodeList.value.map(i => i)
+        : remoteCodeList.value.map(i => i)
+
+      if (type === 'common') {
+        roleMenuCode.value = checked ? values : []
+      } else {
+        controlCode.value = checked ? values : []
+      }
+    }
+
     let addParams = reactive({
       username: "",
       password: "",
       name: "",
       phone: "",
       isEnabled: true,
-      // roleList: "",
       equipmentIds: "",
     });
     let roleItem = null;
     onMounted(async () => {
       // 加载样式
-      styleUrl('role');
       roleItem = localStorage.getItem("roleItem");
       if (roleItem) {
         roleItem = JSON.parse(roleItem);
         roleName.value = roleItem.roleName;
         roleMenuCode.value = roleItem.menuCodesJson;
+        controlCode.value = roleItem.controlCodesJson;
         pageTitle.value = t("role.modifyRole");
       } else {
         pageTitle.value = t("role.addRole");
       }
-      // 设置菜单权限
       menuSet();
-      // getSysRoleListFun();
-      // getEquipmentListFun();
     });
     // 设置菜单权限
     const menuSet = () => {
-      // 如果不是admin管理员权限type=0,要删除 广告管理M5,杉德分账M10,系统脱机M17,apk管理M19
       if (user.type !== 0) {
         delete $M_Menus['M5'];
         delete $M_Menus['M10'];
         delete $M_Menus['M17'];
         delete $M_Menus['M19'];
+        delete $M_Menus['M21'];
+
+
+
+        delete remoteMenus['C19'];
+        delete remoteMenus['C20'];
+        delete remoteMenus['C21'];
+        delete remoteMenus['C22'];
+        delete remoteMenus['C23'];
       }
       // 如果是商家,进一步删除 账户操作M3,订单导出M9,
       if (user.type === 2) {
         delete $M_Menus['M3'];
         delete $M_Menus['M9'];
+        delete $M_Menus['M22'];
       }
 
-      for (const key in $M_Menus) {
-        console.log(key);
-        if (key === "M21") {
-          continue;
+      if (user.type === 2) {
+        if (user.ifForeign === "1") {
+          // 国外账号
+          delete remoteMenus['C10'];
+        }
+        if (user.ifForeign === "0") {
+
+          // 国内账号
+          delete delete remoteMenus['C18'];
         }
-        menuCodeList.value.push({
-          label: $M_Menus[key],
-          value: key
-        });
       }
-    };
-    // 获取角色下拉列表
-    // const getSysRoleListFun = async () => {
-    //   const { data } = await getSysRoleList();
-    //   if (data.code === "00000") {
-    //     roleOptions.value = data.data.map((item) => {
-    //       return { text: item.label, value: item.value };
-    //     });
-    //   } else {
-    //     showFailToast(data.message);
-    //   }
-    // };
-    // 获取机器下拉
-    // const getEquipmentListFun = async () => {
-    //   const { data } = await getEquipmentList({ adminId: user.id });
-    //   if (data.code === "00000") {
-    //     equipmentIdsOptions.value = data.data.map((item) => {
-    //       return { text: item.name, value: item.id };
-    //     });
-    //     equipmentIdsOptions.value.unshift({
-    //       text: t("role.whole"),
-    //       value: "all",
-    //     });
-    //   } else {
-    //     showFailToast(data.message);
-    //   }
-    // };
+
+      for (const key in $M_Menus) {
+        menuCodeList.value.push(key);
+      }
+
+      for (const key in remoteMenus) {
+        remoteCodeList.value.push(key);
+      }
+      
+    }
 
     const onSubmit = async () => {
       if (roleItem) {
@@ -173,16 +207,17 @@ export default {
         adminId: user.id,
         roleName: roleName.value,
         menuCodeList: roleMenuCode.value,
+        controlCodeList: controlCode.value
       };
       const { data } = await addSysRole(params);
       if (data.code === "00000") {
         showSuccessToast(t("role.roleAddedSuccessfully"));
-        // router.push("/role");
-        router.go(-1);
+        setTimeout(() => {
+          router.go(-1);
+        }, 1000);
       } else {
         showFailToast(`${t("role.failedToAddRole")} ${data.message}`);
       }
-      console.log("onSubmit", data);
     };
     const updateSysRoleFun = async () => {
       const params = {
@@ -190,32 +225,33 @@ export default {
         roleId: roleItem.roleId,
         roleName: roleName.value,
         menuCodeList: roleMenuCode.value,
+        controlCodeList: controlCode.value
       };
       const { data } = await updateSysRole(params);
       if (data.code === "00000") {
         showSuccessToast(t("role.successfullyModifiedRole"));
-        // router.go(-1);
-        router.replace("/role");
+        setTimeout(() => {
+          router.go(-1);
+        }, 1000);
       } else {
         showFailToast(`${t("role.failedToModifyRole")} ${data.message}`);
       }
-      console.log("onSubmit", data);
     };
     return {
       pageTitle,
       roleName,
       menuCodeList,
+      remoteCodeList,
       roleMenuCode,
-      // roleShow,
       roleText,
-      // roleOptions,
-      // onRoleFinish,
-      // equipmentIdShow,
+      controlCode,
       equipmentIdText,
       equipmentIdsOptions,
-      // onEquipmentIdFinish,
       ...toRefs(addParams),
       onSubmit,
+      menuSet,
+      toggleAll,
+      toggleCategory,
     };
   },
 };
@@ -223,4 +259,180 @@ export default {
 
 <style lang="less" scoped>
 @import "../../common/style/common.less";
+
+.role-container {
+  background: #f8faff;
+  min-height: 100vh;
+
+  .bulk-actions {
+    padding: 0 16px 12px;
+    display: flex;
+    gap: 8px;
+
+    .van-button {
+      flex: 1;
+      border-radius: 14px;
+      background: #f0f3ff;
+      color: #4e6bdd;
+      border: none;
+    }
+  }
+
+  .input-card {
+    margin: 16px;
+    border-radius: 12px;
+    overflow: hidden;
+    box-shadow: 0 2px 8px rgba(78, 107, 221, 0.08);
+  }
+
+  .permission-section {
+    padding: 0 16px;
+
+    .permission-card {
+      background: #fff;
+      border-radius: 12px;
+      margin-bottom: 20px;
+      box-shadow: 0 2px 12px rgba(78, 107, 221, 0.1);
+
+      .card-header {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 16px;
+        border-bottom: 1px solid #f0f3ff;
+
+        .header-left {
+          display: flex;
+          align-items: center;
+          flex-grow: 1; // 占据剩余空间
+
+          .card-icon {
+            font-size: 20px;
+            margin-right: 12px;
+            color: #4e6bdd;
+            flex-shrink: 0; // 防止图标被压缩
+
+            &.warn-icon {
+              color: #ff5252;
+            }
+          }
+
+          .card-title {
+            margin: 0;
+            font-size: 16px;
+            font-weight: 600;
+            color: #2c3e50;
+            line-height: 1.4;
+            position: relative;
+
+            &::after {
+              content: '';
+              position: absolute;
+              bottom: -4px;
+              left: 0;
+              width: 30px;
+              height: 2px;
+              background: #4e6bdd;
+              border-radius: 1px;
+            }
+          }
+        }
+
+        .header-actions {
+          display: flex;
+          gap: 6px;
+
+          .action-btn {
+            height: 24px;
+            padding: 0 8px;
+            border-radius: 12px;
+            background: rgba(255, 255, 255, 0.9);
+            border: 1px solid #eee;
+            font-size: 12px;
+
+            &:active {
+              opacity: 0.8;
+            }
+          }
+        }
+      }
+
+      .checkbox-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+        gap: 12px;
+        padding: 16px;
+
+        .perm-item {
+          margin: 0;
+          padding: 12px;
+          border: 1px solid #f0f3ff;
+          border-radius: 8px;
+          transition: all 0.2s;
+
+          &:hover {
+            border-color: #4e6bdd;
+            box-shadow: 0 2px 6px rgba(78, 107, 221, 0.1);
+          }
+
+          &.danger-item {
+            border-color: #ffeaea;
+            background: #fff5f5;
+
+            &:hover {
+              border-color: #ff5252;
+            }
+          }
+
+          :deep(.van-checkbox__label) {
+            flex-direction: column;
+            align-items: flex-start;
+          }
+
+          .perm-label {
+            font-size: 14px;
+            color: #34495e;
+            line-height: 1.4;
+          }
+
+          .perm-desc {
+            font-size: 12px;
+            color: #909399;
+            margin-top: 4px;
+          }
+
+          .perm-warn {
+            display: flex;
+            align-items: center;
+            font-size: 12px;
+            color: #ff5252;
+            margin-top: 6px;
+
+            .warn-icon {
+              margin-right: 4px;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .submit-footer {
+    padding: 24px 16px;
+
+    .submit-btn {
+      height: 44px;
+      font-size: 16px;
+      font-weight: 500;
+      background: linear-gradient(135deg, #4e6bdd, #3b5ab3);
+      border: none;
+      box-shadow: 0 4px 12px rgba(78, 107, 221, 0.2);
+
+      &:active {
+        opacity: 0.9;
+        transform: scale(0.98);
+      }
+    }
+  }
+}
 </style>

+ 226 - 109
src/views/role/index.vue

@@ -1,69 +1,73 @@
 <template>
   <!-- 角色权限设置 -->
-  <div class="rolePage flex-col">
+  <div class="role-management-page">
     <s-header :name="$t('role.rolePermissionSettings')" :noback="false"></s-header>
-    <!-- <s-header :name="$t('role.rolePermissionSettings')" :noback="false" targetPath="/accountPer"></s-header> -->
-    <!-- <van-nav-bar :title="$t('role.rolePermissionSettings')" left-text="" left-arrow @click-left="onClickLeft" /> -->
-
-    <div class="accountPerBox flex-col">
-      <div class="searchRow flex-row justify-between">
-        <div class="flex-col">
-          <div class="flex-row justify-between bd3">
-            <div class="flex-col outer4"></div>
-            <span class="flex-col txt2">{{ $t('role.total') }}<span class="discountNumber">{{ accountPerList.length
-            }}</span>{{ $t('role.inTotal') }}</span>
-          </div>
-        </div>
-        <div class="flex-col">
-          <div class="operBox">
-            <div class="addBox" @click="pushAdd()">
-              <img class="iconImg" src="../../assets/accountPer/addicon.png" />
-              <span>{{ $t('role.addTo') }}</span>
-            </div>
-          </div>
+
+    <div class="management-container">
+      <!-- 头部操作栏 -->
+      <div class="action-header">
+        <div class="total-count">
+          <span class="count-text">{{ $t('role.total') }}</span>
+          <van-tag type="primary" class="count-badge">{{ accountPerList.length }}</van-tag>
+          <span class="count-text">{{ $t('role.inTotal') }}</span>
         </div>
+        <van-button type="primary" icon="plus" class="add-btn" @click="pushAdd">
+          {{ $t('role.addTo') }}
+        </van-button>
       </div>
-      <div class="listBox">
-        <div v-for="(item, index) in accountPerList" :key="index" class="listItem">
-          <div class="itemBox">
-            <div class="roleBox">
-              <div class="itemRow">{{ item.roleName }}</div>
-              <div class="itemRow" style="color: #8787a6;">
-                <span v-for="(role, rindex) in item.menuCodesJson" :key="rindex" class="roleItem">{{ showRole(role)
-                }}</span>
+
+      <!-- 角色列表 -->
+      <div class="role-list">
+        <div v-for="(item, index) in accountPerList" :key="index" class="role-card">
+          <div class="card-content">
+            <!-- 角色信息 -->
+            <div class="role-info">
+              <h3 class="role-name">{{ item.roleName }}</h3>
+
+              <!-- 权限分类展示 -->
+              <div class="permission-section">
+                <div class="permission-group">
+                  <div class="group-header">
+                    <van-icon name="todo-list" class="group-icon" />
+                    <span>{{ $t('role.commonPermission') }}</span>
+                  </div>
+                  <div class="permission-tags">
+                    <van-tag v-for="(role, rindex) in item.menuCodesJson" :key="'common-' + rindex" type="primary"
+                      class="permission-tag">
+                      {{ $t("permission." + role) }}
+                    </van-tag>
+                  </div>
+                </div>
+
+                <div class="permission-group" v-if="item.menuCodesJson.includes('M1')">
+                  <div class="group-header">
+                    <van-icon name="setting" class="group-icon warn-icon" />
+                    <span>{{ $t('role.remotePermission') }}</span>
+                  </div>
+                  <div class="permission-tags">
+                    <van-tag v-for="(role, rindex) in item.controlCodesJson" :key="'remote-' + rindex" type="warning"
+                      class="permission-tag">
+                      {{ $t("remote." + role) }}
+                    </van-tag>
+                  </div>
+                </div>
               </div>
             </div>
-            <div class="operBox">
-              <div class="operSet" @click="roleEdit(item)">
-                <img class="iconImg" src="../../assets/edit.png" />
-                <span class="operText">{{ $t('role.edit') }}</span>
-              </div>
-              <div class="operDelter" @click="roleDelete(item)">
-                <img class="iconImg" src="../../assets/delete.png" />
-                <span class="operText">{{ $t('role.delete') }}</span>
-              </div>
+
+            <!-- 操作按钮 -->
+            <div class="action-buttons">
+              <van-button icon="edit" size="small" class="edit-btn vertical-btn" @click="roleEdit(item)">
+                {{ $t('role.edit') }}
+              </van-button>
+              <van-button icon="delete" size="small" type="danger" class="delete-btn vertical-btn"
+                @click="confirmDelete(item)">
+                {{ $t('role.delete') }}
+              </van-button>
             </div>
           </div>
-          <!-- <div class="tipBox" v-if="item.isUse === '0'">已生效</div>
-          <div class="tipBox isUseTip" v-if="item.isUse === '1'">已暂停</div> -->
         </div>
       </div>
     </div>
-    <van-dialog v-model:show="operCheckShow" :title="$t('role.operationConfirmation')" :showConfirmButton="false"
-      :showCancelButton="false">
-      <div class="operCheckBox">
-        <div class="block5 flex-col"></div>
-        <span class="word10">{{ $t('role.pleaseConfirmAgainWhetherToOperate') }}</span>
-        <div class="block6 flex-row justify-between">
-          <div class="mod7 flex-col" @click="operCheckClear()">
-            <span class="info5">{{ $t('role.illThinkAboutItAgain') }}</span>
-          </div>
-          <div class="mod8 flex-col" @click="operCheck()">
-            <span class="info6">{{ $t('role.confirm') }}</span>
-          </div>
-        </div>
-      </div>
-    </van-dialog>
   </div>
 </template>
 
@@ -71,10 +75,10 @@
 import { onMounted, reactive, toRefs, ref } from 'vue';
 import sHeader from "../../components/SimpleHeader";
 import { getSysRoleAdminList, deleteSysRole } from '../../service/role/index';
-import { showFailToast } from "vant";
-import { getLoginUser, styleUrl } from "../../common/js/utils";
+import { showConfirmDialog, showFailToast, showSuccessToast } from "vant";
+import { getLoginUser } from "../../common/js/utils";
 import { useRouter } from "vue-router";
-import { useI18n } from 'vue-i18n';
+import { useI18n } from "vue-i18n";
 
 export default {
   name: "role",
@@ -86,37 +90,10 @@ export default {
     const router = useRouter();
     const searchRef = ref(null);
     const accountPerList = ref([]); // 列表集合
-    const operCheckShow = ref(false);
-    const menuCodeList = [
-      { label: t('role.deviceManagement'), value: 'M1' }, //设备管理
-      { label: t('role.deviceView'), value: 'M2' },
-      // { label: t('role.distributionSettings'), value: 'M3' }, //分销设置
-      { label: t('role.accountOperation'), value: 'M3' }, //账户操作
-      // { label: t('role.alarmHistory'), value: 'M4' },
-      { label: t('role.orderData'), value: 'M4' }, //订单数据
-      { label: t('role.advertisingManagement'), value: 'M5' }, //广告管理
-      { label: t('role.taskMessage'), value: 'M6' }, //任务消息
-      { label: t('role.discountCode'), value: 'M7' }, //优惠码
-      { label: t('role.accountPermission'), value: 'M8' }, //账号权限
-      { label: t('role.orderExport'), value: 'M9' }, //订单导出
-      { label: t('role.shandeSubLedger'), value: 'M10' }, //杉德分账
-      { label: t('role.salesRanking'), value: 'M11' }, //销售排行
-      { label: t('role.withdrawalAccountNo'), value: 'M12' }, //提现账号
-      { label: t('role.standbyWithdrawalAccountNo'), value: 'M13' }, //备用提现账号
-      { label: t('role.dataOverview'), value: 'M14' }, //数据概览
-      // { label: t('role.orderData'), value: 'M15' },
-      { label: t('role.alarmHistory'), value: 'M15' }, //报警历史
-      { label: t('role.orderRefund'), value: 'M16' }, //订单退款
-      { label: t('role.systemOffline'), value: 'M17' }, //系统脱机
-      { label: t('role.labelMan'), value: 'M18' }, //标签管理
-      { label: t('role.apkMan'), value: 'M19' } //apk管理
-      // { label: t('role.merchantMan'), value: 'M20' } //商户管理
-    ];
     let searchParams = reactive({ adminId: user.id });
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
-      styleUrl('role');
       searchGetList();
     });
     // 搜索点击
@@ -137,32 +114,31 @@ export default {
         accountPerList.value = accountPerList.value.concat(data.data.map(item => {
           return {
             ...item,
-            menuCodesJson: JSON.parse(item.menuCodesJson)
+            menuCodesJson: JSON.parse(item.menuCodesJson),
+            controlCodesJson: JSON.parse(item.controlCodesJson)
           }
         }));
         console.log(accountPerList.value);
       } else { showFailToast(data.message); }
-    };
-    const showRole = (name) => {
-      const menuCode = menuCodeList.filter(item => item.value === name);
-      return (menuCode.length > 0) ? menuCode[0].label : name;
-    }
-    let deleteRoleId = '';
-    const roleDelete = (item) => { console.log('roleDelete', item.roleId); deleteRoleId = item.roleId; operCheckShow.value = true; }
-    const operCheckClear = () => { operCheckShow.value = false; }
-    const operCheck = async () => {
-      const { data } = await deleteSysRole({ adminId: user.id, roleId: deleteRoleId });
-      if (data.code === '00000') {
-        operCheckShow.value = false;
-        searchGetList();
-      } else { showFailToast(data.message); }
     }
     const roleEdit = async (item) => { localStorage.setItem('roleItem', JSON.stringify(item)); router.push('/roleSet'); };
 
-    // // 回到上一页 账号权限页
-    // const onClickLeft = () => {
-    //   router.push('/accountPer');
-    // };
+    const confirmDelete = (item) => {
+      showConfirmDialog({
+        title: t('user.tips'),
+        message: t('role.deleteTip'),
+      }).then(async () => {
+        const { data } = await deleteSysRole({ adminId: user.id, roleId: item.roleId });
+        if (data.code === '00000') {
+          showSuccessToast(t('device.deletionSucceeded'));
+          setTimeout(() => {
+            searchGetList();
+          }, 1000);
+        } else { showFailToast(data.message); }
+      }).catch((error) => {
+        console.error(error);
+      })
+    }
 
     return {
       searchRef,
@@ -170,14 +146,9 @@ export default {
       pushAdd,
       search,
       accountPerList,
-      showRole,
-      roleDelete,
-      operCheckShow,
-      operCheckClear,
-      operCheck,
       roleEdit,
       ...toRefs(searchParams),
-      // onClickLeft,
+      confirmDelete,
     };
   },
 };
@@ -185,4 +156,150 @@ export default {
 
 <style lang="less" scoped>
 @import "../../common/style/common.less";
+
+.role-management-page {
+  background: #f8f9ff;
+  min-height: 100vh;
+
+  .management-container {
+    padding: 16px;
+
+    .action-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20px;
+      padding: 12px;
+      background: #fff;
+      border-radius: 8px;
+      box-shadow: 0 2px 8px rgba(78, 107, 221, 0.08);
+
+      .total-count {
+        display: flex;
+        align-items: center;
+
+        .count-text {
+          font-size: 14px;
+          color: #666;
+          margin-right: 8px;
+        }
+
+        .count-badge {
+          margin-right: 4px;
+        }
+      }
+
+      .add-btn {
+        height: 36px;
+        padding: 0 20px;
+        border-radius: 18px;
+      }
+    }
+
+    .role-list {
+      .role-card {
+        margin-bottom: 12px;
+        border-radius: 8px;
+        overflow: hidden;
+        box-shadow: 0 2px 8px rgba(78, 107, 221, 0.08);
+
+        .card-content {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          padding: 16px;
+          background: #fff;
+        }
+
+        .role-info {
+          flex: 1;
+
+          .role-name {
+            margin: 0 0 12px 0;
+            font-size: 16px;
+            color: #2c3e50;
+          }
+
+          .permission-section {
+            .permission-group {
+              margin-bottom: 12px;
+
+              &:last-child {
+                margin-bottom: 0;
+              }
+
+              .group-header {
+                display: flex;
+                align-items: center;
+                margin-bottom: 8px;
+
+                .group-icon {
+                  font-size: 16px;
+                  margin-right: 6px;
+                  color: #4e6bdd;
+
+                  &.warn-icon {
+                    color: #ff5252;
+                  }
+                }
+
+                span {
+                  font-size: 13px;
+                  color: #666;
+                }
+              }
+
+              .permission-tags {
+                display: flex;
+                flex-wrap: wrap;
+                gap: 6px;
+
+                .permission-tag {
+                  font-size: 12px;
+                  padding: 4px 8px;
+                  border-radius: 4px;
+                }
+              }
+            }
+          }
+        }
+
+        .action-buttons {
+          display: flex;
+          flex-direction: column;
+          justify-content: center; // 垂直居中
+          align-items: center; // 水平居中
+          min-width: 100px;
+          height: 100%; // 继承父容器高度
+          margin-left: 16px;
+
+          .vertical-btn {
+            width: 80px; // 固定按钮宽度
+            margin: 4px 0; // 垂直间距
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            // 图标文字间距
+            .van-button__icon+span {
+              margin-left: 4px;
+            }
+          }
+
+          .edit-btn {
+            background: #f0f3ff;
+            color: #4e6bdd;
+            border: 1px solid #d0d5ff;
+          }
+
+          .delete-btn {
+            background: #fff5f5;
+            color: #ff5252;
+            border: 1px solid #ffd0d0;
+          }
+        }
+      }
+    }
+  }
+}
 </style>

+ 4 - 4
src/views/taskMessage/index.vue

@@ -21,12 +21,12 @@
           <div class="taskTitle">{{ $t('taskMessage.refundRemind') }}</div>
         </div>
       </div>
-      <!-- <div class="taskListRow flex-col" @click="pushPageList('/taskJoinPayMchCheck')">
-        <div class="taskIcon withIcon"></div>
+      <div v-if="user.type == 0 || user.type == 2 || user.type == 3" class="taskListRow flex-col" @click="pushPageList('/toDaySugarList')">
+        <div class="taskIcon makeIcon"></div>
         <div class="taskRight">
-          <div class="taskTitle">{{ $t('taskMessage.withdrawalAccountApproval') }}</div>
+          <div class="taskTitle">{{ $t('device.todaysSugarList') }}</div>
         </div>
-      </div> -->
+      </div>
     </div>
   </div>
 </template>