Sfoglia il codice sorgente

feat:“同步2025.03.25“

soobin 5 mesi fa
parent
commit
3a086d3b5d
39 ha cambiato i file con 11810 aggiunte e 2201 eliminazioni
  1. BIN
      src/assets/home/M21.png
  2. BIN
      src/assets/home/M22.png
  3. 1471 1346
      src/assets/language/en.json
  4. 1758 0
      src/assets/language/es.json
  5. 1789 0
      src/assets/language/fr.json
  6. 100 23
      src/assets/language/ja.json
  7. 1758 0
      src/assets/language/pt.json
  8. 1789 0
      src/assets/language/ru.json
  9. 156 83
      src/assets/language/zh.json
  10. BIN
      src/assets/taskMessage/makeIcon.png
  11. 13 0
      src/common/js/countries-en.js
  12. 16 3
      src/common/js/countries.js
  13. 47 23
      src/common/js/utils.js
  14. 244 2
      src/components/SimpleHeader.vue
  15. 5 5
      src/components/dateOrderList/index.vue
  16. 30 26
      src/components/typeDownMenu/index.vue
  17. 5 3
      src/main.js
  18. 7 0
      src/router/index.js
  19. 22 8
      src/service/device/index.js
  20. 12 0
      src/service/purse/indes.js
  21. 5 0
      src/service/user.js
  22. 11 0
      src/styles/taskMessage/index.less
  23. 83 27
      src/utils/i18n.js
  24. 171 75
      src/views/accountPer/add.vue
  25. 218 48
      src/views/accountPer/index.vue
  26. 0 3
      src/views/accountPer/search.vue
  27. 43 30
      src/views/device/deviceOper.vue
  28. 154 64
      src/views/device/doSugar.vue
  29. 18 4
      src/views/device/index.vue
  30. 656 28
      src/views/device/toDaySugarList.vue
  31. 63 99
      src/views/home/index.vue
  32. 3 88
      src/views/login.vue
  33. 567 0
      src/views/purse/index.vue
  34. 10 2
      src/views/register.vue
  35. 1 1
      src/views/robotRanking.vue
  36. 313 74
      src/views/role/add.vue
  37. 228 101
      src/views/role/index.vue
  38. 4 4
      src/views/taskMessage/index.vue
  39. 40 31
      src/views/user.vue

BIN
src/assets/home/M21.png


BIN
src/assets/home/M22.png


File diff suppressed because it is too large
+ 1471 - 1346
src/assets/language/en.json


File diff suppressed because it is too large
+ 1758 - 0
src/assets/language/es.json


File diff suppressed because it is too large
+ 1789 - 0
src/assets/language/fr.json


+ 100 - 23
src/assets/language/ja.json

@@ -1,6 +1,6 @@
 {
     "public": {
-        "sysName": "自販機スマートシステム",
+        "sysName": "Sevencloud Technology",
         "requestFailed": "リクエストに失敗、再読み込み",
         "noMore": "なし"
     },
@@ -48,7 +48,7 @@
         "companies": "企業プラットフォーム",
         "machines": "マシンの種類",
         "sz": "SUNZEE",
-        "sc": "QIYUN",
+        "sc": "SEVENCLOUD",
         "MG": "わたあめ",
         "POP": "ポップコーン",
         "ICE": "アイスクリーム",
@@ -94,6 +94,7 @@
         "total": "合計",
         "permissions": "権限",
         "rolePermissions": "役割権限",
+        "chirdAccountTotal": "サブアカウント総数",
         "add": "サブアカウント",
         "screen": "フィルター",
         "fullName": "フルネーム",
@@ -269,7 +270,7 @@
         "messageReceiver1": "デバイスアラーム連絡先1",
         "messageReceiver2": "デバイスアラーム連絡先2",
         "messageReceiver3": "デバイスアラーム連絡先3",
-        "equipmentmessageReceiverPlaceholder": "電話番号を入力",
+        "equipmentmessageReceiverPlaceholder": "Email",
         "repetitionsLabel": "繰り返し回数",
         "repetitionsPlaceholder": "繰り返し回数を入力",
         "alarmTypeLabel": "アラームの種類",
@@ -301,7 +302,7 @@
         "to": "に",
         "attention": "(注意:桁数が正しくない場合、更新によりマシンに異常が発生する可能性があります)",
         "submitUpdates": "更新を提出",
-        "nameLength":"装置名は25文字以内です",
+        "nameLength": "装置名は25文字以内です",
         "modificationSucceeded": "変更に成功",
         "existsFailed": "デバイス名はすでに存在します",
         "editFailed": "変更に失敗",
@@ -409,7 +410,7 @@
         "popcorn": "ポップコーン",
         "iceCream": "アイスクリーム",
         "sz": "SUNZEE",
-        "sc": "QIYUN",
+        "sc": "SEVENCLOUD",
         "startUp": "電源ON",
         "shutdown": "電源OFF",
         "enable": "有効にする",
@@ -440,8 +441,11 @@
         "clickToSelectTaste": "クリックして味を選択",
         "pleaseSelectAPattern": "デザイン選択",
         "pleaseSelectTaste": "味を選択",
-        "todaysSugarList": "今日の綿菓子リスト",
-        "todaysMakeList": "今日の製造リスト",
+        "jam": "ジャム",
+        "nuts​": "砕いたナッツ",
+        "confirmMake": "リモート制作を確定します",
+        "todaysSugarList": "製造リスト",
+        "todaysMakeList": "製造リスト",
         "goods": "商品:",
         "doTime": "、時間:",
         "byDeice": "、機械:",
@@ -451,7 +455,7 @@
         "machineException": "マシンに異常があり。マシン状態を確認して",
         "netException": "ネットワーク異常",
         "receiveInstruction": "製糖指示受信完了",
-        "dataOverview": "デバイス管理",
+        "dataOverview": "デバイス",
         "search": "検索",
         "totalNumberOfRuns": "総実行数",
         "totalNumberOfEquipment": "総デバイス数",
@@ -481,8 +485,8 @@
         "editCheck": "変更を確認しますか?",
         "unknownError": "未知のエラー",
         "degree": "℃",
-        "humidity": "%RH",
         "weight": " kg",
+        "humidity": "%RH",
         "volume": "ボリューム",
         "alarmContent": "アラーム内容",
         "time": "時間",
@@ -554,7 +558,7 @@
             "guestPassword": "ゲストパスワード",
             "devicePwd": "機器パスワード",
             "checkDevicePwd": "パスワード確認",
-            "passwordPlaceholder": "6桁の数字からなるパスワードを入力します",
+            "passwordPlaceholder": "6桁以上のパスワードを入力してください",
             "passwordCheckRequired": "確認のため再度パスワードを入力してください",
             "passwordMatch": "パスワードが一致しません"
         },
@@ -567,7 +571,7 @@
         "position": "位置情報",
         "viewPositioning": "位置情報を見る",
         "restartAndroid": "画面を再起動",
-        "alarmClean": "機器洗浄",
+        "alramClean": "機器洗浄",
         "returnCoin": "硬貨を払い戻す",
         "returnCoinPage": {
             "amount": "数です",
@@ -618,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": "月",
@@ -732,6 +754,14 @@
         "numberOfOrders": "注文数",
         "salesAmount": "売上高",
         "commonTools": "一般ツール",
+        "coins": "コイン",
+        "bills": "紙幣",
+        "coinsBills": "現金",
+        "creditCard": "クレジットカード",
+        "electronicPayment": "オンライン支払い",
+        "allPayTypeTotal": "合計",
+        "migrationInstructions": "【常用ツール】は【個人センター】に移動しました。",
+        "machineSales": "機械販売",
         "deviceManagement": "装置管理",
         "discountCode": "割引コード",
         "distributionSettings": "分配設定",
@@ -997,8 +1027,8 @@
         "searchCriteria": "検索条件をお願いします。",
         "searchDate": "註文期日指定を入力してください,半年以内です",
         "searchDateTips": "期間はせいぜい半年以内です",
-        "orderDatePlaceholder": "をクリックして日付範囲を選択します",
         "orderDate": "注文日",
+        "orderDatePlaceholder": "をクリックして日付範囲を選択します",
         "startTime": "開始時間",
         "to": "まで",
         "endTime": "終了時間",
@@ -1046,7 +1076,7 @@
             "companyTypePlaceholder": "会社のプラットフォームを選んでください",
             "whole": "全体",
             "sz": "SUNZEE",
-            "sc": "QIYUN",
+            "sc": "SEVENCLOUD",
             "machineTypePlace": "機器タイプを選択",
             "MG": "綿菓子",
             "POP": "ポップコーン"
@@ -1080,6 +1110,7 @@
         "relationAdmin": "関連上位",
         "apkMan": "APK管理",
         "merchantMan": "加盟店管理",
+        "mqtt": "MQTT",
         "accountOperation": "アカウント操作",
         "modifyRole": "役割を変更",
         "whole": "全体",
@@ -1091,6 +1122,11 @@
         "total": "合計 ",
         "inTotal": " 個の役割",
         "addTo": "権限グループに追加",
+        "commonPermission": "常用権限",
+        "remotePermission": "遠隔権限",
+        "deleteTip": "このロールを削除しますか?この操作は元に戻せません!",
+        "toggleAll": "全選択",
+        "toggleNoAll": "選択反転",
         "edit": "編集",
         "delete": "削除",
         "operationConfirmation": "操作確認",
@@ -1371,7 +1407,7 @@
         "namePlaceholder": "ユーザー名を入力してください",
         "nameRequired": "ユーザー名を入力してください",
         "passwordLabel": "ログインパスワード",
-        "passwordPlaceholder": "10桁以上のパスワードを入力してください",
+        "passwordPlaceholder": "英数字を含む10文字以上のパスワードを入力してください",
         "passwordRequired": "10桁以上のパスワードを入力してください",
         "passwordPattern": "パスワードには英字と数字を含める必要があります",
         "passwordCheckLabel": "パスワード確認",
@@ -1403,7 +1439,7 @@
         "emailCodeLabel": "メール認証コード",
         "emailCodePlaceholder": "メール認証コードを入力してください",
         "emailCodeRequired": "メール認証コードを入力してください",
-        "invitationCode": "招待コードです(任意)",
+        "invitationCode": "招待コードです",
         "invitationCodePlaceholder": "招待コードの入力をお願いします",
         "registerButton": "登録する",
         "replaysInSeconds": "数秒後に再送信できます",
@@ -1567,6 +1603,7 @@
         "D418": "炉頭の作業温度",
         "D419": "キャビネット内の許容糖作り温度誤差",
         "D435": "キャビネット内の許容糖作り湿度誤差",
+        "D442": "そしてN秒後に低消費電力に入ります",
         "D444": "基準係数",
         "D445": "基準湿度",
         "D460": "低温環境設定(炉頭の昇温)",
@@ -1655,7 +1692,7 @@
         "content": "スイッチを切り替えますか?",
         "successfully": "送信に成功しました。数秒待ってから再入力してください"
     },
-    "alarmClean": {
+    "alramClean": {
         "openCleanFunction": "洗浄機能です:オン",
         "closeCleanFunction": "洗浄機能です:オフ",
         "openStatus": "装置のクリーニングリマインダースイッチの状態:オン",
@@ -1664,10 +1701,50 @@
         "content": "スイッチを切り替えますか?",
         "successfully": "送信成功、数秒後に再度確認してください"
     },
-    "账户操作": "アカウント操作",
-    "优惠码": "プロモーションコード",
-    "报警历史": "アラーム履歴",
-    "广告管理": "広告管理",
-    "订单导出": "注文のエクスポート",
-    "apk管理": "APK管理"
+    "permission": {
+        "M1": "デバイス",
+        "M3": "アカウント",
+        "M4": "注文",
+        "M5": "広告",
+        "M6": "タスク",
+        "M7": "クーポン",
+        "M9": "エクスポート",
+        "M11": "売上",
+        "M14": "分析",
+        "M15": "アラート",
+        "M16": "返金",
+        "M17": "オフライン",
+        "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": "解凍モード(アイスクリーム)"
+    }
 }

File diff suppressed because it is too large
+ 1758 - 0
src/assets/language/pt.json


File diff suppressed because it is too large
+ 1789 - 0
src/assets/language/ru.json


+ 156 - 83
src/assets/language/zh.json

@@ -77,7 +77,7 @@
     "rolePlaceholder": "请选择角色",
     "setUp": "设置",
     "startOrNot": "是否启动",
-    "manageMachinesLabel": "管理机器",
+    "manageMachinesLabel": "机器",
     "manageMachinesPlaceholder": "请选择管理机器",
     "submit": "提交",
     "modifyAccount": "修改账号",
@@ -95,6 +95,7 @@
     "total": "共 ",
     "permissions": " 条权限",
     "rolePermissions": "角色权限",
+    "chirdAccountTotal": "子账户总数",
     "add": "子商家",
     "screen": "筛选",
     "fullName": "姓名",
@@ -193,7 +194,7 @@
         "search": "搜索",
         "selectAd": "请选择广告",
         "pushMerchant": "推送商家",
-        "pushMerchantPlace": "用英文的逗号隔开,结尾也要有逗号",
+        "pushMerchantPlace": "用英文的逗号,隔开,不要出现空格",
         "screen": "筛选",
         "adScreen": "广告筛选",
         "adName": "广告名称",
@@ -287,8 +288,8 @@
     "cancel": "取消",
     "confirm": "确认",
     "allMachines": "全部机器",
-    "furnaceHeadOn": "炉头开启",
-    "furnaceHeadClosed": "炉头关闭",
+    "furnaceHeadOn": "设备(炉头开启",
+    "furnaceHeadClosed": "设备(炉头关闭",
     "startSleep": "开始休眠",
     "endSleep": "结束休眠",
     "successfullyAddedAlarmClock": "新增闹钟成功",
@@ -305,8 +306,8 @@
     "isUpdate": "是否更新",
     "to": "为",
     "attention": "(注意:位数不正确,更新可能会造成机器异常)",
-    "submitUpdates": "提交更新",
     "nameLength": "设备名称不超过25个字",
+    "submitUpdates": "提交更新",
     "modificationSucceeded": "修改成功",
     "existsFailed": "设备名称已存在",
     "editFailed": "修改失败",
@@ -400,12 +401,15 @@
     "equipmentTypePlaceholder": "请选择设备类型",
     "companyTypeLabel": "公司平台",
     "companyTypePlaceholder": "请选择公司平台",
+    "deviceGroupPlaceholder": "请选择设备分组",
     "equipmentModelLabel": "设备机型",
     "equipmentModelPlaceholder": "请选择设备机型",
     "powerOnStatus": "开机状态",
     "pleaseSelectThePowerOnStatus": "请选择开机状态",
+    "deviceGrouping": "设备分组",
     "equipmentStatus": "设备状态",
     "pleaseSelectTheDeviceStatus": "请选择设备状态",
+    "plzSelectDeviceGroup": "请选择设备分组",
     "emptyingConditions": "清空条件",
     "clickSearch": "点击搜索",
     "spunSugar": "棉花糖",
@@ -443,8 +447,11 @@
     "clickToSelectTaste": "点击选择口味",
     "pleaseSelectAPattern": "请选择花型",
     "pleaseSelectTaste": "请选择口味",
-    "todaysSugarList": "今日做糖列表",
-    "todaysMakeList": "今日制作列表",
+    "jam": "果酱",
+    "nuts​": "果碎",
+    "confirmMake": "确定远程制作",
+    "todaysSugarList": "远程制作记录",
+    "todaysMakeList": "远程制作记录",
     "goods": "商品:",
     "doTime": ",时间:",
     "byDeice": ",设备:",
@@ -466,7 +473,7 @@
     "SuspendBusiness": "暂停营业",
     "sleepDescPlace": "请输入睡眠描述",
     "lastRefreshTime": "最近刷新时间",
-    "machineUniqueCode": "机器唯一编码",
+    "machineUniqueCode": "设备编码",
     "furnaceHeadTemperature": "炉头温度",
     "temperatureInCabinet": "柜内温度",
     "humidityInCabinet": "柜内湿度",
@@ -480,12 +487,12 @@
     "outsidehd": "柜外湿度",
     "noData": "暂无数据",
     "showGoods": "屏蔽/显示商品",
+    "diyFlower": "DIY花型",
     "editCheck": "是否确认更改?",
     "unknownError": "未知错误!",
-    "diyFlower": "DIY花型",
     "degree": "℃",
-    "humidity": "%RH",
     "weight": " kg",
+    "humidity": "%RH",
     "volume": "音量",
     "time": "时间",
     "alarmContent": "报警内容",
@@ -557,7 +564,7 @@
       "guestPassword": "次管理员密码",
       "deivcePwd": "机器密码",
       "checkDeivcePwd": "确认密码",
-      "passwordPlaceholder": "请输入6位数字组成的密码",
+      "passwordPlaceholder": "请输入6位数以上密码",
       "passwordCheckRequired": "请再次输入密码进行确认",
       "passwordMatch": "两次密码不一致"
     },
@@ -570,7 +577,7 @@
     "position": "定位",
     "viewPositioning": "查看定位",
     "restartAndroid": "重启触摸屏",
-    "alarmClean": "设备清洗",
+    "alramClean": "设备清洗",
     "returnCoin": "远程退币",
     "returnCoinPage": {
       "amount": "退币数量",
@@ -599,8 +606,8 @@
     "water": "清水",
     "wasteWater": "废水",
     "sentSuccessfully": "发送请求成功",
-    "turnOffSleep": "关闭睡眠",
-    "turnOnSleep": "开启睡眠",
+    "turnOffSleep": "睡眠中zzZ",
+    "turnOnSleep": "睡眠",
     "materialMonitorOn": "物料监控已开启",
     "materialMonitorOff": "物料监控已关闭",
     "communiChannel": "通信通道",
@@ -621,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": "一",
@@ -742,6 +767,14 @@
     "numberOfOrders": "订单数",
     "salesAmount": "销售额",
     "commonTools": "常用工具",
+    "coins": "硬币",
+    "bills": "纸币",
+    "coinsBills": "现金",
+    "creditCard": "信用卡",
+    "electronicPayment": "电子支付",
+    "allPayTypeTotal": "总计",
+    "migrationInstructions": "【常用工具】已迁移至【个人中心】",
+    "machineSales": "机器销售额",
     "deviceManagement": "设备管理",
     "discountCode": "优惠码",
     "distributionSettings": "分销设置",
@@ -774,28 +807,11 @@
       "sun": "周日"
     }
   },
-  "airwallex": {
-    "sunzee": "申泽智能",
-    "checkout": "结算",
-    "airwallex": "空中云汇",
-    "wallet": "空中云汇钱包",
-    "amount": "钱包金额",
-    "payment": "付款转账",
-    "paymentDetail": "付款详情",
-    "payout": "付款单",
-    "beneficiary": "收款人",
-    "payForIt": "立即支付",
-    "cancelOrder": "取消订单",
-    "productName": "商品名称",
-    "productsNum": "商品数量",
-    "totalPrice": "合计金额",
-    "shoppingVoucher": "购物凭证"
-  },
   "joinpayMch": {
     "huifuAuditStatus": "汇付入驻状态",
     "joinpayAuditStatus": "汇聚入驻状态",
     "settlementAccount": "结算账号",
-    "withdrawalAccountNo": "备用提现账号",
+    "withdrawalAccountNo": "提现账号",
     "merchantNameLabel": "商户名称",
     "merchantNamePlaceholder": "请输入商户名称",
     "merchantNameTips": "必须与商家营业执照上的名称一致",
@@ -991,6 +1007,7 @@
     "paymentMethod": "支付方式",
     "creationTime": "创建时间",
     "paymentTime": "支付时间",
+    "refundTime": "退款时间",
     "initiateRefund": "发起退款",
     "toBePaid": "待支付",
     "onlinePayment": "线上支付",
@@ -1017,7 +1034,6 @@
     "userNameLabel": "用户名",
     "userNamePlaceholder": "请输入用户名",
     "orderNoPlaceholder": "请输入订单编号",
-    "refundTime": "退款时间",
     "orderSerialNumberLabel": "商户单号",
     "orderSerialNumberPlaceholder": "请输入商户单号",
     "equipmentNoPlaceholder": "请输入设备编号",
@@ -1059,10 +1075,10 @@
     "address": "地址",
     "searchPop": {
       "title": "根据以下条件筛选",
-      "orderNo": "订单编号",
-      "orderNoPlace": "请输入订单编号",
-      "busiName": "商户名称",
-      "busiNamePlace": "请输入商户名称",
+      "orderNo": "设备编号",
+      "orderNoPlace": "请输入设备编号",
+      "busiName": "商户账号",
+      "busiNamePlace": "请输入商户账号",
       "clearFilter": "清空条件",
       "filter": "筛选",
       "mainOver": "大陆/海外",
@@ -1086,6 +1102,8 @@
     "pleaseSelectARole": "请选择角色",
     "pleaseSelectAPattern": "请选择花型",
     "deviceManagement": "设备管理",
+    "mqtt": "MQTT管理",
+    "terminal": "终端管理",
     "deviceView": "设备查看",
     "distributionSettings": "分销设置",
     "alarmHistory": "报警历史",
@@ -1118,6 +1136,11 @@
     "total": "共 ",
     "inTotal": " 个角色",
     "addTo": "添加权限组",
+    "commonPermission": "常用权限",
+    "remotePermission": "远程权限",
+    "deleteTip": "确定要删除该角色吗?此操作不可恢复!",
+    "toggleAll": "全选",
+    "toggleNoAll": "反选",
     "edit": "编辑",
     "delete": "删除",
     "operationConfirmation": "操作确认",
@@ -1125,8 +1148,32 @@
     "illThinkAboutItAgain": "我再想想",
     "confirm": "确认"
   },
+  "mqtt": {
+    "title": "MQTT管理",
+    "message": "消息管理",
+    "topicManagement": "主题管理",
+    "sendMessage": "发送消息",
+    "topic": "主题名称",
+    "topicPlaceholder": "请输入主题名称",
+    "qos": "QoS等级",
+    "send": "发送",
+    "messageContent": "消息内容",
+    "messageContentPlaceholder": "请输入消息内容",
+    "sendSuccess": "发送成功",
+    "sendFailed": "发送失败",
+    "messageList": "消息列表",
+    "receiveTime": "接收时间",
+    "addTopic": "添加主题",
+    "topicDescription": "主题描述",
+    "topicDescriptionPlaceholder": "请输入主题描述",
+    "sumbit": "提交",
+    "addSuccessfully": "添加成功",
+    "addFailed": "添加失败",
+    "topicList": "已订阅主题列表",
+    "createTime": "创建时间"
+  },
   "huifuMch": {
-    "standbyWithdrawalAccountNo": "提现账号",
+    "standbyWithdrawalAccountNo": "备用提现账号",
     "merchantType": "商户类型",
     "personal": "个人",
     "enterprise": "企业",
@@ -1198,37 +1245,6 @@
     "update": "修改",
     "uploadPage": "重新上传图片"
   },
-  "subLedgerManage": {
-    "exportOfShandeLedgerData": "杉德分账数据导出",
-    "accountType": "账号类型",
-    "corporateAccountNo": "对公账号",
-    "personalAccount": "个人账号",
-    "filterAccounts": "筛选账户",
-    "inTotal": "共2条",
-    "exportToExcel": "导出excel",
-    "account": "账号",
-    "settAmount": "已结算金额",
-    "amouToBeSettled": "待结算金额",
-    "lastSettTime": "上次结算时间",
-    "search": {
-      "title": "根据以下条件筛选商户账户",
-      "screen": "筛选",
-      "business": "商家",
-      "account": "账户",
-      "busiPlaceholder": "请选择商家",
-      "accoPlaceholder": "请输入账号"
-    },
-    "settleProcess": {
-      "settleAmount": "结算金额",
-      "settleAmountPlaceholder": "请输入结算金额",
-      "notAccountNum": "不计算账号",
-      "notAccountNumPlaceholder": "请输入不计算账号",
-      "settleProcess": "结算处理",
-      "title": "请再次确认是否进行结算",
-      "cancelTxt": "再想想",
-      "confrimTxt": "进行结算"
-    }
-  },
   "taskMessage": {
     "equipmentInitializationApproval": "设备初始化审批",
     "total": "共 ",
@@ -1254,7 +1270,7 @@
     "owner": "拥有者",
     "platform": "平台",
     "machineOwner": "机主",
-    "platformAccount": "七云",
+    "platformAccount": "申泽",
     "partners": "合作商",
     "proportion": "比例",
     "fail": "不通过",
@@ -1398,7 +1414,7 @@
     "namePlaceholder": "请输入用户名称",
     "nameRequired": "请输入用户名称",
     "passwordLabel": "登录密码",
-    "passwordPlaceholder": "请输入10位以上密码",
+    "passwordPlaceholder": "请输入10位以上密码,包含字母和数字",
     "passwordRequired": "请输入密码",
     "passwordPattern": "密码必须包含字母与数字",
     "passwordCheckLabel": "确认密码",
@@ -1430,7 +1446,7 @@
     "emailCodeLabel": "验证码",
     "emailCodePlaceholder": "请输入邮箱验证码",
     "emailCodeRequired": "请输入邮箱验证码",
-    "invitationCode": "邀请码(选填)",
+    "invitationCode": "邀请码",
     "invitationCodePlaceholder": "请输入邀请码",
     "registerButton": "注册",
     "replaysInSeconds": "秒后可重发",
@@ -1504,6 +1520,7 @@
     "taskMessage": "任务消息",
     "settlementAccount": "结算账号",
     "withdrawalAccountNo": "提现账号",
+    "announcement": "公告编辑",
     "standbyWithdrawalAccountNo": "备用提现账号",
     "changePassword": "修改密码",
     "feedback": "意见反馈",
@@ -1512,15 +1529,19 @@
     "logOutTips": "提示",
     "cancelLog": "取消",
     "confirmLog": "确定",
-    "logOutContent": "确定需要退出登录吗",
+    "logOutContent": "确定退出系统吗",
     "corrEmailPlace": "请输入正确的邮箱",
     "corrPhonePlace": "请输入正确的手机号",
     "bindWechat": "绑定微信",
+    "syncOldData": "数据同步",
+    "modifyLoc": "修改定位",
     "airwallex": "空中云汇",
     "setTheTimezone": "设置时区",
     "setTheTimezonePlace": "请选择时区",
     "currencySymbol": "自定义货币",
     "currencySymbolPlace": "请输入自定义货币",
+    "diyPassword": "DIY使用密码",
+    "diyPasswordPlace": "请输入DIY使用密码",
     "szPayType": "申泽支付平台",
     "scPayType": "七云支付平台",
     "currentChoose": "当前选择",
@@ -1591,6 +1612,7 @@
     "D418": "炉头工作温度",
     "D419": "柜内允许做糖温度误差",
     "D435": "柜内允许做糖湿度误差",
+    "D442": "炉头N秒后进入低功耗",
     "D444": "基准系数",
     "D445": "基准湿度",
     "D460": "低温环境设定(炉头升柜温)",
@@ -1656,6 +1678,20 @@
     "merchantManagement": "商户管理",
     "labelMan": "分组管理"
   },
+  "wechat": {
+    "headerName": "绑定微信"
+  },
+  "announcement": {
+    "header": "公告编辑",
+    "title": "标题",
+    "successfully": "添加成功",
+    "failed": "添加失败"
+  },
+  "syncOldData": {
+    "header": "数据同步",
+    "title": "标题",
+    "syncing": "同步中"
+  },
   "DIYFlower": {
     "openStatus": "DIY花型功能开关状态:开启",
     "closeStatus": "DIY花型功能开关状态:关闭",
@@ -1665,7 +1701,7 @@
     "content": "是否切换开关?",
     "successfully": "发送成功,请等待几秒后重新进入查看"
   },
-  "alarmClean": {
+  "alramClean": {
     "openCleanFunction": "设备清洗功能:开启",
     "closeCleanFunction": "设备清洗功能:关闭",
     "openStatus": "设备清洗提醒开关状态:开启",
@@ -1674,13 +1710,50 @@
     "content": "是否切换开关?",
     "successfully": "发送成功,请等待几秒后重新进入查看"
   },
-  "wechat": {
-    "headerName": "绑定微信"
+  "permission": {
+    "M1": "设备管理",
+    "M3": "账户操作",
+    "M4": "订单数据",
+    "M5": "广告管理",
+    "M6": "任务消息",
+    "M7": "优惠码",
+    "M9": "订单导出",
+    "M11": "销售排行",
+    "M14": "数据概览",
+    "M15": "报警历史",
+    "M16": "订单退款",
+    "M17": "系统脱机",
+    "M19": "APK",
+    "M21": "MQTT",
+    "M22": "终端管理"
   },
-  "账户操作": "账户操作",
-  "优惠码": "优惠码",
-  "报警历史": "报警历史",
-  "广告管理": "广告管理",
-  "订单分析": "订单分析",
-  "apk管理": "apk管理"
+  "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": "解冻模式(冰淇淋)"
+  }
 }

BIN
src/assets/taskMessage/makeIcon.png


+ 13 - 0
src/common/js/countries-en.js

@@ -1343,6 +1343,19 @@ export const countriesDataEn = [
     ],
   },
   {
+    text: "Romania",
+    value: "RO",
+    children: [
+      { text: "Bucharest", value: "Bucharest" },
+      { text: "Cluj-Napoca", value: "Cluj-Napoca" },
+      { text: "Timișoara", value: "Timișoara" },
+      { text: "Iași", value: "Iași" },
+      { text: "Constanța", value: "Constanța" },
+      { text: "Brașov", value: "Brașov" },
+      { text: "Galați", value: "Galați" }
+    ],
+  },
+  {
     text: "Rwanda",
     value: "RW",
     children: [

+ 16 - 3
src/common/js/countries.js

@@ -1557,6 +1557,19 @@ export const countriesData = [
     ],
   },
   {
+    text: "罗马尼亚",
+    value: "RO",
+    children: [
+      { text: "布加勒斯特", value: "Bucharest" },
+      { text: "克卢日-纳波卡", value: "Cluj-Napoca" },
+      { text: "蒂米什瓦拉", value: "Timișoara" },
+      { text: "雅西", value: "Iași" },
+      { text: "康斯坦察", value: "Constanța" },
+      { text: "布拉索夫", value: "Brașov" },
+      { text: "加拉茨", value: "Galați" },
+    ],
+  },
+  {
     text: "挪威",
     value: "NO",
     children: [
@@ -2089,16 +2102,16 @@ export const countriesData = [
   {
     text: "中国香港",
     value: "HK",
-    children: [{ text: "中国香港", value: "Hong kong" }]
+    children: [{ text: "中国香港", value: "Hong kong" }],
   },
   {
     text: "中国澳门",
     value: "MO",
-    children: [{ text: "中国澳门", value: "Macao" }]
+    children: [{ text: "中国澳门", value: "Macao" }],
   },
   {
     text: "中国台湾",
     value: "TW",
-    children: [{ text: "中国台湾", value: "Taiwan" }]
+    children: [{ text: "中国台湾", value: "Taiwan" }],
   },
 ];

+ 47 - 23
src/common/js/utils.js

@@ -11,30 +11,54 @@ import i18n from '../../utils/i18n';
 const t = i18n.global.t;
 // 菜单标识
 export const $M_Menus = {
-  "M1": t("role.deviceManagement"),//设备管理
-  // "M2":t("role.deviceView"),//设备查看
-  // "M3":t("role.distributionSettings"),//分销设置
-  "M3": t("role.accountOperation"),//账户操作
-  // "M4":t("role.orderData"),//报警历史
-  "M4": t("role.orderData"),//订单数据
-  "M5": t("role.advertisingManagement"),//广告管理
-  "M6": t("role.taskMessage"),//任务消息
-  "M7": t("role.discountCode"),//优惠码
-  // "M8":t("role.accountPermission"),//账号权限
-  "M9": t("role.orderExport"),//订单导出
-  // "M10":t("role.shandeSubLedger"),//杉德分账
-  "M11":t("role.salesRanking"),//销售排行
-  // "M12":t("role.withdrawalAccountNo"),//提现账号
-  // "M13":t("role.standbyWithdrawalAccountNo"),//备用提现账号
-  "M14": t("role.dataOverview"),//数据概览
-  // "M15":t("role.alarmHistory"),//订单数据
-  "M15": t("role.alarmHistory"),//报警历史
-  "M16": t("role.orderRefund"),//订单退款
-  "M17": t("role.systemOffline"),//系统脱机
-  // "M18":t("role.labelMan"),//标签管理
-  "M19": t("role.apkMan"),//apk管理
-  // "M20":t("role.merchantMan"),//商户管理
+  "M1": t("permission.M1"),//设备管理
+  "M3": t("permission.M3"),//账户操作
+  "M4": t("permission.M4"),//订单数据
+  "M5": t("permission.M5"),//广告管理
+  "M6": t("permission.M6"),//任务消息
+  "M7": t("permission.M7"),//优惠码
+  "M9": t("permission.M9"),//订单导出
+  "M11":t("permission.M11"),//销售排行
+  "M14": t("permission.M14"),//数据概览
+  "M15": t("permission.M15"),//报警历史
+  "M16": t("permission.M16"),//订单退款
+  "M17": t("permission.M17"),//系统脱机
+  "M19": t("permission.M19"),
+  "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);

+ 244 - 2
src/components/SimpleHeader.vue

@@ -7,13 +7,83 @@
       <i class="nbicon nbmore moreIcon"></i>
     </header>
   </div> -->
-  <van-nav-bar v-if="!isback" :title="name" left-arrow @click-left="onClickLeft"/>
-  <van-nav-bar v-else :title="name" />
+  <van-nav-bar v-if="!isback" :title="name" :border="isBorder" left-arrow @click-left="onClickLeft" :style="barStyle">
+    <!-- 右侧语言切换 -->
+    <template #right>
+      <div class="nav-language">
+        <van-button round size="small" class="lang-trigger" @click="showPopover = true">
+          <div class="current-lang">
+            <img :src="currentLang.flag" class="current-flag" />
+            <span class="lang-code">{{ currentLang.code.toUpperCase() }}</span>
+          </div>
+        </van-button>
+
+        <van-popover v-model:show="showPopover" :offset="[0, 8]" placement="bottom-end" class="lang-popover">
+          <van-cell-group>
+            <van-cell v-for="lang in languages" :key="lang.code"
+              :class="['lang-item', { active: lang.code === currentLang.code }]" @click="handleChangeLanguage(lang)">
+              <template #icon>
+                <img :src="lang.flag" class="flag" :alt="lang.code">
+              </template>
+              <div class="lang-content">
+                <span class="lang-label">{{ lang.label }}</span>
+                <span class="lang-code">{{ lang.code.toUpperCase() }}</span>
+              </div>
+            </van-cell>
+          </van-cell-group>
+        </van-popover>
+      </div>
+    </template>
+  </van-nav-bar>
+  <van-nav-bar v-else :title="name">
+    <!-- 右侧语言切换 -->
+    <template #right>
+      <div class="nav-language">
+        <van-button round size="small" class="lang-trigger" @click="showPopover = true">
+          <div class="current-lang">
+            <img :src="currentLang.flag" class="current-flag" />
+            <span class="lang-code">{{ currentLang.code.toUpperCase() }}</span>
+          </div>
+        </van-button>
+
+        <van-popover v-model:show="showPopover" :offset="[0, 8]" placement="bottom-end" class="lang-popover">
+          <van-cell-group>
+            <van-cell v-for="lang in languages" :key="lang.code"
+              :class="['lang-item', { active: lang.code === currentLang.code }]" @click="handleChangeLanguage(lang)">
+              <template #icon>
+                <img :src="lang.flag" class="flag" :alt="lang.code">
+              </template>
+              <div class="lang-content">
+                <span class="lang-label">{{ lang.label }}</span>
+                <span class="lang-code">{{ lang.code.toUpperCase() }}</span>
+              </div>
+            </van-cell>
+          </van-cell-group>
+        </van-popover>
+      </div>
+    </template>
+  </van-nav-bar>
 </template>
 
 <script>
 import { ref } from 'vue'
 import { useRouter } from 'vue-router'
+import { useI18n } from 'vue-i18n';
+import { Locale } from 'vant';
+// 引入英文语言包
+import enUS from "vant/es/locale/lang/en-US";
+// 引入简体中文语言包
+import zhCN from "vant/es/locale/lang/zh-CN";
+import jaJP from "vant/es/locale/lang/ja-JP";
+// 引入俄语语言包
+import ruRU from "vant/es/locale/lang/ru-RU";
+// 引入法语语言包
+import frFR from "vant/es/locale/lang/fr-FR";
+// 引入西班牙语语言包
+import esES from "vant/es/locale/lang/es-ES";
+// 引入葡萄牙语语言包
+import ptBR from "vant/es/locale/lang/pt-BR";
+
 export default {
   props: {
     name: {
@@ -37,6 +107,15 @@ export default {
     targetPath: {
       type: String,
       default: ''
+    },
+    // 是否显示下边框
+    isBorder: {
+      type: Boolean,
+      default: true
+    },
+    barStyle: {
+      type: Object,
+      default: () => ({})
     }
   },
   emits: ['callback'],
@@ -53,12 +132,48 @@ export default {
       }
       ctx.emit('callback')
     }
+
+    const { locale } = useI18n();
+    // 可用语言列表
+    const languages = ref([
+      { code: 'zh', label: '简体中文', vant: zhCN, flag: 'https://flagcdn.com/cn.svg' },
+      { code: 'en', label: 'English', vant: enUS, flag: 'https://flagcdn.com/us.svg' },
+      { code: 'ja', label: '日本語', vant: jaJP, flag: 'https://flagcdn.com/jp.svg' },
+      { code: 'ru', label: 'Русский', vant: ruRU, flag: 'https://flagcdn.com/ru.svg' },
+      { code: 'fr', label: 'Français', vant: frFR, flag: 'https://flagcdn.com/fr.svg' },
+      { code: 'es', label: 'Español', vant: esES, flag: 'https://flagcdn.com/es.svg' },
+      { code: 'pt', label: 'Português', vant: ptBR, flag: 'https://flagcdn.com/pt.svg' },
+    ]);
+
+    // 当前语言
+    const currentLang = ref(languages.value.find(l => l.code === locale.value) || languages.value[0]);
+    const showPopover = ref(false);
+
+    // 切换语言
+    const handleChangeLanguage = async (lang) => {
+      currentLang.value = lang;
+      locale.value = lang.code;
+      // 1. 切换应用语言
+      locale.value = lang.code
+
+      // 2. 切换 Vant 语言
+      Locale.use(lang.code, lang.vant)
+      showPopover.value = false;
+
+      // 3. (可选) 持久化存储
+      localStorage.setItem('curLang', lang.code)
+    };
+
     const onClickLeft = () => history.back();
 
     return {
       goBack,
       isback,
       onClickLeft,
+      handleChangeLanguage,
+      showPopover,
+      languages,
+      currentLang
     }
   }
 }
@@ -95,4 +210,131 @@ export default {
 .moreIcon {
   visibility: hidden;
 }
+
+
+.current-lang {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 2px;
+}
+
+.current-flag {
+  width: 20px;
+  height: 15px;
+  border-radius: 2px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.lang-code {
+  font-size: 14px;
+  font-weight: 500;
+  color: #333;
+  margin-top: 1px;
+}
+
+.nav-language {
+  margin-right: -8px;
+}
+
+.lang-trigger {
+  padding: 4px 8px;
+  background: rgba(255, 255, 255, 0.9);
+  border: 1px solid #eee;
+  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+  border: 1px solid #eee;
+  /* 边框颜色 */
+  color: #2c87c8;
+  /* 文字颜色 */
+
+  &:hover {
+    border-color: #2c87c8;
+    color: #2c87c8;
+    background: rgba(77, 106, 221, 0.05);
+  }
+}
+
+
+/* 点击状态 */
+.lang-trigger:active {
+  border-color: #2c87c8;
+  color: #2c87c8;
+  background: rgba(77, 106, 221, 0.1);
+}
+
+.globe-icon {
+  font-size: 16px;
+  color: #666;
+  margin-right: 6px;
+}
+
+.lang-text {
+  font-size: 14px;
+  font-weight: 500;
+  color: #333;
+}
+
+.lang-popover {
+  --van-popover-arrow-size: 8px;
+}
+
+.lang-item {
+  padding: 10px 16px;
+
+  &.active {
+    background: #f5f8ff;
+
+    .lang-label {
+      color: #2c87c8;
+    }
+  }
+}
+
+/* 调整后的样式 */
+.van-cell {
+  --cell-vertical-padding: 12px;
+  /* 统一垂直间距 */
+  display: flex;
+  align-items: center;
+  /* 主轴线居中 */
+}
+
+.flag {
+  width: 22px;
+  height: 16px;
+  border-radius: 2px;
+  margin-right: 12px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+  vertical-align: middle;
+  /* 行内元素垂直对齐 */
+  position: relative;
+  top: -1px;
+  /* 微调视觉平衡 */
+}
+
+.lang-content {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.lang-label {
+  line-height: 24px;
+  font-size: 14px;
+  color: #333;
+  gap: 8px;
+}
+
+.lang-code {
+  font-size: 12px;
+  color: #2c87c8;
+  font-weight: 600;
+  /* 增加字重提升可读性 */
+  letter-spacing: 0.5px;
+}
+
+/* 国旗悬停动画 */
+.lang-trigger:hover .current-flag {
+  filter: brightness(0.9);
+}
 </style>

+ 5 - 5
src/components/dateOrderList/index.vue

@@ -24,13 +24,13 @@
         </van-popup>
         <!-- 按月统计 -->
         <van-popup v-model:show="monthDateShow" position="bottom">
-          <van-date-picker v-model="monthDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate" :max-date="maxDate" @confirm="monthDateConfirm" @cancel="monthDateCancel"
-            :columns-type="monthDateType" />
+          <van-date-picker v-model="monthDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate"
+            :max-date="maxDate" @confirm="monthDateConfirm" @cancel="monthDateCancel" :columns-type="monthDateType" />
         </van-popup>
         <!-- 按年统计 -->
         <van-popup v-model:show="yearDateShow" position="bottom">
-          <van-date-picker v-model="yearDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate" :max-date="maxDate" @confirm="yearDateConfirm" @cancel="yearDateCancel"
-            :columns-type="yearDateType" />
+          <van-date-picker v-model="yearDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate"
+            :max-date="maxDate" @confirm="yearDateConfirm" @cancel="yearDateCancel" :columns-type="yearDateType" />
         </van-popup>
       </div>
     </div>
@@ -136,7 +136,7 @@ export default {
             new Date(nowYear, nowMonth + 1, 0),
             "yyyy-MM-dd"
           ) + " 23:59:59";
-          params.dateType = "0";
+        params.dateType = "0";
       }
       // 其他时间
       if (timeType.value === "4") {

+ 30 - 26
src/components/typeDownMenu/index.vue

@@ -24,7 +24,7 @@
 </template>
 
 <script>
-import { onMounted, toRefs, reactive, ref } from "vue";
+import { onMounted, toRefs, reactive, computed, 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();
     }
@@ -146,17 +146,21 @@ export default {
     // 设备筛选
     const deviceType = ref(null);
     // 设备筛选标题
-    const deviceTypeTitle = ref(t('typeSelectList.allDevices'));
+    const deviceTypeTitle = computed(() =>(t('typeSelectList.allDevices')));
     // 设备列表
-    const deviceList = ref([]);
+    const deviceList = ref([
+      {
+        text: t('typeSelectList.allDevices'),
+        value: ''
+      }
+    ]);
     // 获取设备列表
     const getDeviceListFun = async () => {
       const { data } = await getEquipmentList({ adminId: user.id });
       if (data.code === '00000') {
-        deviceList.value = data.data.map(item => {
-          return { text: item.name != null ? item.name : item.clientId, value: item.id }
+        data.data.map(item => {
+          deviceList.value.push({ text: item.name != null ? item.name : item.clientId, value: item.id });
         });
-        deviceList.value.unshift({ text: t('typeSelectList.allDevices'), value: '' });
       }
     }
     // 设备选择
@@ -173,18 +177,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();
     }
@@ -209,7 +213,7 @@ export default {
       adminTypeTitle.value = adminList.value.find(adminList => adminList.value === value).text;
       searchParams.adminType = (adminTypeTitle.value === t('typeSelectList.allSuboUsers')) ? 'all' : '';
       searchParams.userName = (adminTypeTitle.value === t('typeSelectList.allSuboUsers')) ? 'all' : (adminTypeTitle.value === t('typeSelectList.thisMerchant')) ? user.username : adminTypeTitle.value;
-      searchParams.adminId = (value ===  ('this' || 'all') ? null : value);
+      searchParams.adminId = (value === ('this' || 'all') ? null : value);
       searchParams.ifForeign = adminList.value.find(adminList => adminList.value === value).ifForeign;
       if (searchParams.ifForeign == "0") {
         payList.value = [

+ 5 - 3
src/main.js

@@ -4,8 +4,8 @@ import {
   AddressList, Field, CellGroup, Cell, SwipeCell, Icon, Stepper, Card, Checkbox, CheckboxGroup, Button, Swipe, SwipeItem,
   PullRefresh, List, Tab, Tabs, SubmitBar, Toast, Skeleton, RadioGroup, Radio, NoticeBar, ActionSheet, Cascader, Col, Row,
   Slider, DatePicker, Switch, Calendar, Picker, Uploader, Tag, DropdownMenu, DropdownItem, Notify, ConfigProvider, NavBar,
-  Area, Popover, Collapse, CollapseItem, PickerGroup, TimePicker, BackTop, Progress, Tabbar, TabbarItem, Search, TextEllipsis
-
+  Area, Popover, Collapse, CollapseItem, PickerGroup, TimePicker, BackTop, Progress, Tabbar, TabbarItem, Search, TextEllipsis, 
+  Step, Steps, Sticky
 } from 'vant';
 import { Image as VanImage } from 'vant';
 import App from './App.vue'
@@ -93,7 +93,9 @@ app.use(ActionBarButton)
   .use(TabbarItem)
   .use(Search)
   .use(TextEllipsis)
-
+  .use(Step)
+  .use(Steps)
+  .use(Sticky)
 
 app.use(router)
 app.use(store)

+ 7 - 0
src/router/index.js

@@ -511,6 +511,13 @@ const router = createRouter({
       component: () => import("@/views/popPayment"),
       meta: { index: 1, noLogin: true },
     },
+    // 钱包
+    {
+      path: "/purse",
+      name: "purse",
+      component: () => import("@/views/purse/index"),
+      meta: { index: 1 },
+    },
     // apk管理,广告管理,订单导出 不是所有的帐号能看到
   ],
 });

+ 22 - 8
src/service/device/index.js

@@ -19,10 +19,7 @@ export function getDeviceDetal(params) {
 
 // 修改设备详情
 export function updateDevice(params) {
-  return axios.post(
-    `/SZWL-SERVER/tEquipment/update`,
-    params
-  );
+  return axios.post(`/SZWL-SERVER/tEquipment/update`, params);
 }
 
 // 获取花型种类下拉列表
@@ -119,6 +116,18 @@ export function selectSugarList(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)}`);
@@ -299,20 +308,25 @@ export function changeWorkingMode(params) {
 
 // 修改清洗提醒
 export function changeCleanFunction(params) {
-  return axios.get(`/SZWL-SERVER/tEquipmentDesc/updateCleanFunction`, { params })
+  return axios.get(`/SZWL-SERVER/tEquipmentDesc/updateCleanFunction`, {
+    params,
+  });
 }
 
 // 获取机器开关机状态
 export function getOnOffStatus(params) {
-  return axios.post(`/SZWL-SERVER/tEquipment/onOffStatus?${stringToUrl(params)}`, params)
+  return axios.post(
+    `/SZWL-SERVER/tEquipment/onOffStatus?${stringToUrl(params)}`,
+    params
+  );
 }
 
 // 获取当天远程退币申请记录
 export function getReturnCoinList(params) {
-  return axios.get(`/SZWL-SERVER/returnCoinRecord/list`, { params })
+  return axios.get(`/SZWL-SERVER/returnCoinRecord/list`, { params });
 }
 
 // 提交远程退币申请
 export function applyReturnCoin(params) {
-  return axios.post(`/SZWL-SERVER/returnCoinRecord/apply`, params)
+  return axios.post(`/SZWL-SERVER/returnCoinRecord/apply`, params);
 }

+ 12 - 0
src/service/purse/indes.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

@@ -52,4 +52,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

@@ -50,6 +50,17 @@
           right: 0.2rem;
           top: 0.4rem;
         }
+
+        &.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 {

+ 83 - 27
src/utils/i18n.js

@@ -1,41 +1,97 @@
 import { createI18n } from "vue-i18n";
 import { navigatorLanguage } from "../common/js/utils";
+import { Locale } from "vant";
 // 引入英文语言包
 import enUS from "vant/es/locale/lang/en-US";
 // 引入简体中文语言包
 import zhCN from "vant/es/locale/lang/zh-CN";
 import jaJP from "vant/es/locale/lang/ja-JP";
-let language = "en";
+// 引入俄语语言包
+import ruRU from "vant/es/locale/lang/ru-RU";
+// 引入法语语言包
+import frFR from "vant/es/locale/lang/fr-FR";
+// 引入西班牙语语言包
+import esES from "vant/es/locale/lang/es-ES";
+// 引入葡萄牙语语言包
+import ptBR from "vant/es/locale/lang/pt-BR";
+
+const curLang = localStorage.getItem('curLang');
+let language = 'en';
 // 储存语言,方便中英切换
-const curLang = localStorage.getItem("curLang");
+
 if (curLang) {
-  language = curLang;
-  localStorage.setItem("curLang", curLang);
+    language = curLang;
+    localStorage.setItem('curLang', curLang);
 } else {
-  language = navigatorLanguage();
-  localStorage.setItem("curLang", navigatorLanguage());
+    language = navigatorLanguage();
+    localStorage.setItem('curLang', navigatorLanguage());
 }
-// vant组件的中英文切换
-import { Locale } from "vant";
+
+const i18n = createI18n({
+    //多语言实例
+    legacy: false,
+    globalInjection: true,
+    // 默认语言
+    locale: localStorage.getItem("curLang")
+      ? localStorage.getItem("curLang")
+      : "en",
+    // 关闭控制台警告
+    silentFallbackWarn: true,
+    messages: {
+      en: require("../assets/language/en.json"), //英文包
+      zh: require("../assets/language/zh.json"), //中文包
+      ja: require("../assets/language/ja.json"), //日文包
+      ru: require("../assets/language/ru.json"), //俄语包
+      fr: require("../assets/language/fr.json"), //法语包
+      es: require("../assets/language/es.json"), //西班牙语包
+      pt: require("../assets/language/pt.json"), //葡萄牙语包
+    },
+  });
+
 // 判断中英文,切换vant语言包
-if (language === "en") {
-  Locale.use("en-US", enUS);
-} else if (language === "ja") {
-  Locale.use("ja-JP", jaJP);
+if (language === 'en') {
+    Locale.use('en-US', enUS);
+} else if (language === 'ja') {
+    Locale.use('ja-JP', jaJP);
+} else if (language === 'ru') {
+    Locale.use('ru-RU', ruRU);
+} else if (language === 'fr') {
+    Locale.use('fr-FR', frFR);
+} else if (language === 'es') {
+    Locale.use('es-ES', esES);
+} else if (language === 'pt') {
+    Locale.use('pt-BR', ptBR);
 } else {
-  Locale.use("zh-CN", zhCN);
+    Locale.use('zh-CN', zhCN);
 }
-export default createI18n({
-  //多语言实例
-  legacy: false,
-  globalInjection: true,
-  // 默认语言
-  locale: localStorage.getItem('curLang') ? localStorage.getItem('curLang') : 'en',
-  // 关闭控制台警告
-  silentFallbackWarn: true,
-  messages: {
-    'en': require('../assets/language/en.json'), //英文包
-    'zh': require('../assets/language/zh.json'), //中文包
-    'ja': require('../assets/language/ja.json'), //日文包
-  },
-});
+
+// 监听语言切换
+i18n.global.onBeforeLanguageSwitch = (lang) => {
+  // 同步更新 Vant 语言
+  switch (lang) {
+    case "en-US":
+      Locale.use("en-US", enUS);
+      break;
+    case "zh-CN":
+      Locale.use("zh-CN", zhCN);
+      break;
+    case "ja-JP":
+      Locale.use("ja-JP", jaJP);
+      break;
+    case "fr-FR":
+      Locale.use("fr-FR", frFR);
+      break;
+    case "ru-RU":
+      Locale.use("ru-RU", ruRU);
+      break;
+    case "es-ES":
+      Locale.use("es-ES", esES);
+      break;
+    case "pt-BR":
+      Locale.use("pt-BR", ptBR);
+      break;
+  }
+};
+
+export default i18n;
+

+ 171 - 75
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="[
-            { required: true, message: $t('accountPer.usernamePlaceholder') },
-          ]" />
-        <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">
@@ -71,8 +71,8 @@
         @close="roleShow = false" @finish="onRoleFinish" />
     </van-popup>
     <!-- 机器弹窗 -->
-    <kSelectPop @selconfirm="selEquipConfirm" ref="kSelectPopEquipRef" :selOptions="selEquipOptions" @searchData="searchEquData"
-      :selTitleAndName="['name', 'id']"></kSelectPop>
+    <kSelectPop @selconfirm="selEquipConfirm" ref="kSelectPopEquipRef" :selOptions="selEquipOptions"
+      @searchData="searchEquData" :selTitleAndName="['name', 'id']"></kSelectPop>
   </div>
 </template>
 
@@ -81,7 +81,7 @@
 import kSelectPop from "../../components/commom/kSelectPop/index.vue";
 import { onMounted, reactive, toRefs, ref } from "vue";
 import sHeader from "../../components/SimpleHeader";
-import { showFailToast, showSuccessToast, showToast } from 'vant';
+import { showFailToast, showSuccessToast, showToast, showNotify } from 'vant';
 import {
   getSysRoleList,
   getEquipmentList,
@@ -115,7 +115,7 @@ export default {
       equipmentIdText.value = selectedOptions[0].text;
     };
     const passwordPlaceholder = ref("");
-    const passwordPattern = ref(/^$/);
+    const passwordPattern = ref(/^(?=.*[a-zA-Z])(?=.*\d).{10,}$/);
     const havePassword = ref(true);
     const isUserName = ref(false);
     let addParams = reactive({
@@ -127,7 +127,7 @@ export default {
       isEnabled: true,
       roleList: "",
       equipmentIds: "",
-      companyType: "1",
+      companyType: "0",
     });
     const cofficentForm = ref({
       equipmentNames: "",
@@ -149,12 +149,10 @@ export default {
         addParams.roleList = accoutPerSet.roleList[0];
         addParams.equipmentIds = accoutPerSet.equipmentIds;
         havePassword.value = false;
-        passwordPlaceholder.value = t('accountPer.editPasswordPlaceholder');
-        passwordPattern.value = /^$/;
+        passwordPlaceholder.value = t('accountPer.editPasswordPlaceholder')
       } else {
         pageTitle.value = t("accountPer.addAccount");
         passwordPlaceholder.value = t('register.passwordPlaceholder');
-        passwordPattern.value = /^(?=.*[a-zA-Z])(?=.*\d).{10,}$/;
         havePassword.value = true;
       }
       getSysRoleListFun();
@@ -164,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
@@ -176,6 +177,7 @@ export default {
         showFailToast(data.message);
       }
     };
+
     const searchEquData = (e) => {
       selEquipOptions.value = e;
     }
@@ -210,35 +212,42 @@ export default {
 
     const onSubmit = async () => {
       let params = { adminId: user.id };
-      if (addParams.username ==='admin') {
-        showFailToast(t('register.A0201'));
+      if (addParams.username === 'admin') {
+        showNotify({ type: 'danger', message: t('register.A0201') });
         return false;
       }
+      if (addParams.password != "") {
+        if (!passwordPattern.value.test(addParams.password)) {
+          showNotify(t('register.passwordPlaceholder'));
+          return false;
+        }
+      }
       params = Object.assign({ adminId: user.id }, addParams);
       if (params.password !== "") {
         params.password = md5(params.password);
       }
       params.roleList = [params.roleList];
       if (pageTitle.value === t("accountPer.modifyAccount")) {
+        // delete params.password;
         const { data } = await updataLoginUser(params);
         if (data.code === "00000") {
           showSuccessToast(t("accountPer.successfullyModifiedTheAccount"));
+          // router.push("/accountPer");
           router.replace("/accountPer");
-        } else if (data.code === 'A0201') {
-          showToast(t("accountPer.accountNameExists"));
         } else {
           showFailToast(t("accountPer.failedToModifyTheAccount"), data.message);
         }
-        console.log("onSubmit", data);
       } else {
         const { data } = await addLoginUser(params);
         if (data.code === "00000") {
           showSuccessToast(t("accountPer.accountAddedSuccessfully"));
+          // router.push("/accountPer");
           router.replace("/accountPer");
+        } else if (data.code === 'A0201') {
+          showToast(t("accountPer.accountNameExists"));
         } else {
           showFailToast(t("accountPer.failedToAddAccount"), data.message);
         }
-        console.log("onSubmit", data);
       }
     };
     const deleteLoginUserFun = async () => {
@@ -258,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();
@@ -269,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 = '';
@@ -313,4 +317,96 @@ export default {
 
 <style lang="less" scoped>
 @import "../../common/style/common.less";
+@primary-color: #2c87c8;
+@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: #2c87c8;
+          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, #2c87c8, #2c87c8);
+            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: #2c87c8;
+            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">{{

+ 43 - 30
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,14 +24,14 @@
         <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 class="operImg" src="../../assets/device/operIcon/sleep.png" />
         </div>
         <div class="operText">{{ sleepTitle }}</div>
       </div>
       <!-- 开启炉头/设备 -->
-      <div v-if="device.machineType != '2'" 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>
@@ -40,7 +40,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>
@@ -49,28 +49,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>
@@ -86,14 +90,14 @@
               <div class="operText">{{ $t("device.remoteDoorOpening") }}</div>
             </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>
@@ -102,14 +106,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>
@@ -121,32 +125,34 @@
         </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-else class="operImg" src="../../assets/device/operIcon/materialMonitorOff.png" />
@@ -156,50 +162,51 @@
         <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花型 -->
-      <!-- v-if="device.equimentType == 'MG320' || device.equimentType == 'MG330'" -->
-      <div class="operItem" v-if="user.type < 1" @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 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>
@@ -213,15 +220,17 @@
         <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/alarmClean.png" />
         </div>
-        <div class="operText">{{ $t("device.alarmClean") }}</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" />
@@ -327,13 +336,16 @@ export default {
     const materialIcon = ref('0');
     // 工作模式
     const workMode = ref(null);
+    // 操作权限
+    const controlList = ref([]);
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
       styleUrl('deviceOper');
     });
     // 当前弹窗父组件触发
-    const showOper = (e) => {
+    const showOper = (e, value) => {
+      controlList.value = value;
       device.value = e;
       show.value = true;
       // isSleep=true,机器属于休眠状态,按钮是关闭休眠
@@ -401,7 +413,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 = () => {
@@ -651,6 +663,7 @@ export default {
       updateWorkingMode,
       alarmCleanClk,
       returnCoinClk,
+      controlList,
     };
   },
   components: {},

+ 154 - 64
src/views/device/doSugar.vue

@@ -25,7 +25,7 @@
           <span @click="pushToDaySugarList">{{ $t('device.todaysSugarList') }}>></span>
         </div>
         <div v-if="!doSugartData" class="block5 flex-col" @click="submitDoSugar"><span class="txt3">{{
-          $t('device.submitToMakeSugar') }}</span></div>
+      $t('device.submitToMakeSugar') }}</span></div>
         <van-button v-if="doSugartData" style="padding: 1em;" round type="primary" class="block5 flex-col"
           :disabled="doSugartType" @click="checkData()">{{ $t('device.viewResults') }}</van-button>
       </div>
@@ -47,31 +47,43 @@
           <span @click="pushToDaySugarList">{{ $t('device.todaysMakeList') }}>></span>
         </div>
         <div v-if="!doSugartData" class="block5 flex-col" @click="submitDoSugar"><span class="txt3">{{
-          $t('device.submitToMakeSugar') }}</span></div>
-        <van-button v-if="doSugartData" round type="primary" class="volumeChangeButton" :disabled="doSugartType"
-          @click="checkData()">{{ $t('device.viewResults') }}</van-button>
+      $t('device.submitToMakeSugar') }}</span></div>
+        <van-button v-if="doSugartData" style="padding: 1em;" round type="primary" class="block5 flex-col"
+          :disabled="doSugartType" @click="checkData()">{{ $t('device.viewResults') }}</van-button>
       </div>
       <div v-if="machineType === '2'">
-        <van-field v-model="fieldValue" is-link readonly :label="$t('device.clickToSelectTaste')"
-          :placeholder="$t('device.pleaseSelectTaste')" @click="show = true" />
-        <van-popup v-model:show="show" round position="bottom">
-          <van-cascader v-model="cascaderValue" :title="$t('device.pleaseSelectTaste')" :options="options"
-            @close="show = false" @finish="onFinish">
-            <template #option="{ option }">
-              <div class="cascader-item">
-                <van-image :src="option.imgUrl" width="55px" height="55px"></van-image>
-                <div class="cascader-label">{{ option.value }}</div>
-              </div>
-            </template>
-          </van-cascader>
-        </van-popup>
+        <van-collapse v-model="activeNames">
+          <van-collapse-item :title="$t('device.jam')" name="1" size="large" title-style="color: #404d74;">
+            <van-row class="goods">
+              <van-col v-for="(item, index) in jamData" :key="index" class="goodsCon o-mlr-5 o-mb-20" span="11">
+                <div class="l-flex-RC">
+                  <van-image width="50" height="50" fit="contain" :src="showPopPhoto(item)" />
+                  <span class="o-ml-10" style="color: #000;word-wrap: break-word; width: 50px;">{{ item.name }}</span>
+                  <van-checkbox class="o-ml-10" shape="square" v-model="item.checked" />
+                </div>
+              </van-col>
+            </van-row>
+          </van-collapse-item>
+          <van-collapse-item :title="$t('device.nuts​')" name="2" size="large" title-style="color: #404d74;">
+            <van-row class="goods">
+              <van-col v-for="(item, index) in crushData" :key="index" class="goodsCon o-mlr-5 o-mb-20" span="11">
+                <div class="l-flex-RC">
+                  <van-image width="50" height="50" fit="contain" :src="showPopPhoto(item)" />
+                  <span class="o-ml-10" style="color: #000;word-wrap: break-word; width: 50px;">{{ item.name }}</span>
+                  <van-checkbox class="o-ml-10" shape="square" v-model="item.checked" />
+                </div>
+              </van-col>
+            </van-row>
+          </van-collapse-item>
+        </van-collapse>
+        <br>
         <div class="textRow o-pr-20">
           <span @click="pushToDaySugarList">{{ $t('device.todaysMakeList') }}>></span>
         </div>
         <div v-if="!doSugartData" class="block5 flex-col" @click="submitDoSugar"><span class="txt3">{{
-          $t('device.submitToMakeSugar') }}</span></div>
-        <van-button v-if="doSugartData" round type="primary" class="volumeChangeButton" :disabled="doSugartType"
-          @click="checkData()">{{ $t('device.viewResults') }}</van-button>
+      $t('device.submitToMakeSugar') }}</span></div>
+        <van-button v-if="doSugartData" style="padding: 1em;" round type="primary" class="block5 flex-col"
+          :disabled="doSugartType" @click="checkData()">{{ $t('device.viewResults') }}</van-button>
       </div>
     </div>
   </div>
@@ -80,8 +92,12 @@
 import { onMounted, ref } from 'vue';
 import sHeader from "@/components/SimpleHeader";
 import { useRoute, useRouter } from 'vue-router';
-import { getDeviceDetal, selectProducts, doSugar, selectSugarStatus } from '@/service/device'
-import { showFailToast, showSuccessToast, showToast } from 'vant';
+import {
+  getDeviceDetal, selectProducts,
+  doSugar,
+  selectSugarStatus
+} from '@/service/device'
+import { showConfirmDialog, showFailToast, showSuccessToast, showToast } from 'vant';
 import { useI18n } from 'vue-i18n';
 import { styleUrl } from "../../common/js/utils";
 
@@ -97,10 +113,25 @@ export default {
     const fieldValue = ref('');
     const cascaderValue = ref('');
     const options = ref([]);
+    const activeNames = ref(['1', '2']);
+
+    // 果酱数据
+    const iceName = ref();
+    const jamData = ref([]);
+    const crushData = ref([]);
+
+    // 商品图片
+    const showPopPhoto = (row) => {
+      let imgId = row.no;
+      if (imgId) {
+        return require(`../../assets/order/spunSugar/goods/${imgId}.png`);
+      }
+      return imgId;
+    };
     const onFinish = ({ selectedOptions }) => {
+      console.log('onFinish', selectedOptions);
       show.value = false;
       fieldValue.value = selectedOptions[0].value
-      console.log('onFinish', selectedOptions[0].value);
     }
     const doSugartData = ref(null);
     const doSugartType = ref(true);
@@ -123,11 +154,11 @@ export default {
         getProduct();
       } else { showFailToast(data.message); }
     }
-       // 获取花形下拉列表
-       const getProduct = async () => {
+    // 获取花形下拉列表
+    const getProduct = async () => {
       const { data } = await selectProducts({ equipmentId: deviceId });
       if (data.code) {
-        if(machineType != '2') {
+        if (machineType != '2') {
           options.value = data.data.map(item => {
             return {
               text: item.productName,
@@ -137,51 +168,106 @@ export default {
           })
         } else {
           data.data.forEach(item => {
-            if (item.no == 'I01') {
-              options.value.push(
-                {
-                  text: item.productName,
-                  value: item.productName,
-                  imgUrl: showSugarPhoto(item.no),
-                  children: []
-                }
-              )
-            } else if (item.no.includes('J')) {
-              options.value[0].children.push(
-                {
-                  text: item.productName,
-                  value: item.productName,
-                  imgUrl: showSugarPhoto(item.no),
-                  children: [],
-                }
-              )
-            } else if (item.no.includes('C')) {
-              options.value[0].children.forEach(item1 => {
-                item1.children.push(
-                  {
-                    text: item.productName,
-                    value: item.productName,
-                    imgUrl: showSugarPhoto(item.no),
-                  }
-                )
-              })
+            if (item.no.includes('J01')) {
+              // 果酱1
+              jamData.value.push({
+                name: item.productName,
+                no: item.no,
+                value: 1,
+                checked: false
+              });
+            } else if (item.no.includes('J02')) {
+              // 果酱2
+              jamData.value.push({
+                name: item.productName,
+                no: item.no,
+                value: 2,
+                checked: false
+              });
+            } else if (item.no.includes('J03')) {
+              // 果酱3
+              jamData.value.push({
+                name: item.productName,
+                no: item.no,
+                value: 3,
+                checked: false
+              });
+            } else if (item.no.includes('C01')) {
+              // 果碎1
+              crushData.value.push({
+                name: item.productName,
+                no: item.no,
+                value: 1,
+                checked: false
+              });
+            } else if (item.no.includes('C02')) {
+              // 果碎2
+              crushData.value.push({
+                name: item.productName,
+                no: item.no,
+                value: 2,
+                checked: false
+              });
+            } else if (item.no.includes('I01')) {
+              // 雪糕
+              iceName.value = item.productName;
             }
           })
         }
       } else { showFailToast(data.message); }
-      console.log(options.value);
     }
     const submitDoSugar = async () => {
+      const makeCodes = ref([1, 0, 0]);
       doSugartData.value = null;
       doSugartType.value = true;
+
+      if (machineType == '2') {
+        fieldValue.value = '';
+        let jamCount = 0;
+        let crushCount = 0;
+        // 如果是冰淇淋机器
+        jamData.value.forEach(item => {
+          if (item.checked) {
+            fieldValue.value += item.name + ',';
+            makeCodes.value[1] += item.value;
+            jamCount++;
+          }
+        });
+        crushData.value.forEach(item => {
+          if (item.checked) {
+            fieldValue.value += item.name + ',';
+            makeCodes.value[2] += item.value;
+            crushCount++;
+          }
+        });
+        if (jamCount > 1) {
+          makeCodes.value[1] += 1;
+        }
+        if (crushCount > 1) {
+          makeCodes.value[2] += 1;
+        }
+        if (fieldValue.value === '') {
+          fieldValue.value = iceName.value;
+        } else {
+          fieldValue.value = fieldValue.value.substring(0, fieldValue.value.length - 1);
+          fieldValue.value = iceName.value + "(" + fieldValue.value + ")";
+        }
+      }
       if (fieldValue.value === '') { showFailToast(t('device.pleaseSelectAPattern')); return; }
-      const { data } = await doSugar({ equipmentId: deviceId, productName: fieldValue.value });
-      if (data.code) {
-        doSugartData.value = data.data;
-        setTimeout(() => {
-          doSugartType.value = false;
-        }, 5000);
-      } else { showFailToast(data.message); }
+      showConfirmDialog({
+        title: t('user.tips'),
+        message: t('device.confirmMake')+ fieldValue.value +'?',
+      }).then(async() => {
+        const { data } = await doSugar({ equipmentId: deviceId, productName: fieldValue.value, makeCodes: makeCodes.value });
+        if (data.code) {
+          doSugartData.value = data.data;
+          setTimeout(() => {
+            doSugartType.value = false;
+          }, 5000);
+        } else { showFailToast(data.message); }
+      }).catch((error) => {
+        console.error(error);
+      })
     }
     const checkData = async () => {
       const { data } = await selectSugarStatus({ no: doSugartData.value.no });
@@ -207,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 {
@@ -223,7 +309,11 @@ export default {
       checkData,
       pushToDaySugarList,
       showSugarPhoto,
-      machineType
+      machineType,
+      activeNames,
+      jamData,
+      crushData,
+      showPopPhoto
     };
   },
   components: { sHeader },

+ 18 - 4
src/views/device/index.vue

@@ -231,10 +231,10 @@
       }}</span>
                       <div class="box2 flex-col"></div>
                     </div>
-                    <div class="l-flex-RC o-ml-30" @click="deviceOprShow(item)">
+                    <div v-if="controlList.length > 0" class="l-flex-RC o-ml-30" @click="deviceOprShow(item, controlList)">
                       <span class="c-text-12">{{
         $t("device.commonOperations")
-      }}</span>
+                        }}</span>
                       <div class="box2 flex-col"></div>
                     </div>
                   </div>
@@ -286,6 +286,7 @@ import dateUtil from "../../utils/dateUtil";
 import { useI18n } from "vue-i18n";
 import { onBeforeUnmount } from "vue";
 import { Api_getLabelList } from "../../service/labelMan";
+import { getAdminRole } from "@/service/user";
 
 export default {
   name: "device",
@@ -352,10 +353,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');
@@ -446,8 +459,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) => {
@@ -727,6 +740,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 @@
 	  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 @@
   
 	  // 获取做糖列表数据
 	  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 @@
 		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,5 +388,360 @@
   </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, #2c87c8 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: #2c87c8;
+			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: #2c87c8; // 主题色背景
+		  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: #2c87c8;
+			  }
+			}
+		  }
+  
+		  .van-radio__icon--checked .van-icon {
+			background-color: #2c87c8;
+			border-color: #2c87c8;
+		  }
+  
+		  .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 - 99
src/views/home/index.vue

@@ -2,7 +2,7 @@
   <!-- 主页 -->
   <div class="homePage flex-col">
     <div class="homeBox">
-      <s-header :name="sys ? sys.title : sysTitle" :noback="true" :isFixed="false"></s-header>
+      <s-header :name="sys ? sys.title : $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"
@@ -98,12 +98,10 @@
         </div>
       </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)" />
+        <div class="main25 flex-col" v-for="(item, index) in pushToolList" :key="index" @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(item.label) }}</span>
+            <span class="info15">{{ $t("permission." + item) }}</span>
           </div>
         </div>
       </div>
@@ -134,7 +132,7 @@ import { onMounted, ref, nextTick } from "vue";
 import sHeader from "../../components/SimpleHeader";
 import dateSelectList from "../../components/dateSelectList";
 import typeDownMenu from "../../components/typeDownMenu";
-import { getLoginUser, $M_Menus, Format_time, styleUrl, getLocal, setLocal, navigatorLanguage } from "../../common/js/utils";
+import { getLoginUser, Format_time, styleUrl, getLocal, setLocal, navigatorLanguage } from "../../common/js/utils";
 import { useRouter } from "vue-router";
 import {
   getStatistics,
@@ -143,6 +141,7 @@ import {
   Api_getUpdateNotice,
   getIsAlarm,
 } from "../../service/home";
+import { getAdminRole } from "@/service/user";
 import dateUtil from "../../utils/dateUtil";
 import { useI18n } from "vue-i18n";
 import { showToast } from "vant";
@@ -394,7 +393,7 @@ export default {
       }
       firstLogin.value = localStorage.getItem('firstLogin');
       // 设置菜单权限
-      menuSet();
+      // menuSet();
       // 设置菜单权限, 只执行一次
       // once(menuSet);
       dateSelect.value = {
@@ -408,6 +407,8 @@ export default {
           "yyyy-MM-dd hh:mm:ss"
         ),
       };
+      // 获取账号权限
+      getAccountPer();
       typeSelectData = {
         userName: user.username,
         clientId: null,
@@ -443,7 +444,7 @@ export default {
         case currentDomain.includes('sskj.qxfoodom'):  // 食神科技 sskj.qxfoodom.com
           sysTitle.value = '食神科技';
           break;
-        default: 
+        default:
           sysTitle.value = t('public.sysName');
       }
     }
@@ -454,103 +455,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 isOrderExport = user.menuCodeList.some((item) => {
-      //   return item === "M9";
-      // })
+        });
+      }
+    }
 
-      // 查询是否有数据概览权限
-      const isDataOverview = user.menuCodeList.some((item) => {
-        return item === "M14";
-      })
+    // 解析 + 排序计算属性
+    const sortedArray = (value) => {
+      try {
+        // 1. 安全解析JSON
+        const parsed = JSON.parse(value)
+        if (!Array.isArray(parsed)) return []
 
-      // 如果没有apk管理
-      if (!isApkMan) {
-        // 如果是公司人,要把apk添加上去
-        // if (user.type === 0 || user.type === 1) {
-        //   pushToolList.value.push({
-        //     label: t("home.apkMan"),
-        //     value: "M19"
-        //   });
-        // }
-      }
+        // 2. 自定义排序函数
+        const getNumber = str => parseInt(str.match(/\d+/)?.[0] || 0)
 
-      // 如果没有账号权限
-      if (!isAccount) {
-        //  如果是公司人type=0,要把账号权限添加上去
-        if (user.type === 0) {
-          pushToolList.value.push({
-            label: t("home.accountPermission"),
-            value: "M8"
-          });
-        }
-      }
+        // 3. 执行排序(数字从小到大)
+        return [...parsed].sort((a, b) => {
+          return getNumber(a) - getNumber(b)
+        })
 
-      // 子商家以上级别,默认可以看到订单详情
-      if (user.type === 0 || user.type === 2) {
-        showDataDiv.value = true;
-        isOrderData.value = true;
-      } else {
-        // 如果有数据概览权限 和 订单数据权限
-        if (isDataOverview) {
-          showDataDiv.value = true;
-        }
+      } catch (error) {
+        console.error('解析失败:', error)
+        return []
       }
-
     };
 
     // 常用操作跳转页面
@@ -561,7 +529,6 @@ export default {
           router.push({ path: "/device" }); // 设备管理
           break;
         case "M3":
-          // router.push({ path: "/distributionSet" });
           router.push({ path: "/accountOperation" }); // 账户操作
           break;
         case "M4":
@@ -582,9 +549,6 @@ export default {
         case "M9":
           router.push({ path: "/orderExport" });
           break;
-        // case "M10":
-        //   router.push({ path: "/subLedgerManage" });
-        //   break;
         case "M11":
           router.push({ path: "/robotranking" });
           break;

+ 3 - 88
src/views/login.vue

@@ -1,24 +1,11 @@
 <template>
   <!-- 登录 -->
   <div class="login">
-    <s-header :name="sys ? sys.title : sysTitle" :noback="true"></s-header>
-    <div class="loginLogoBox l-re">
+    <s-header :name="sys ? sys.title : $t('public.sysName')" :noback="true"></s-header>
+    <div class="loginLogoBox">
       <div class="loginLogo">
         <img :src="logoName" alt="Logo">
       </div>
-      <div class="l-ab pointer languageCon">
-        <!-- <div class="c-text-b">
-          {{ compLang }}
-        </div> -->
-        <van-popover :offset="[0, 8]" theme="dark" v-model:show="showPopover" :actions="actions" @select="onSelect">
-          <template #reference>
-            <van-button style="height: auto; color: #4d6add;" type="primary">{{ currentLan }}</van-button>
-          </template>
-        </van-popover>
-      </div>
-    </div>
-    <div class="loginTitleBox l-flex-center">
-      <span class="loginTitle">{{ $t("login.title") }}</span>
     </div>
     <div class="loginFormBox">
       <van-form @submit="onSubmit">
@@ -81,44 +68,13 @@ import {setLocal, getLocal, navigatorLanguage, styleUrl} from "../common/js/util
 import sHeader from "../components/SimpleHeader";
 import {useRoute, useRouter} from "vue-router";
 import {useI18n} from "vue-i18n";
-// vant 组件的中英文切换
-import {Locale} from "vant";
-// 引入英文语言包
-import enUS from "vant/es/locale/lang/en-US";
-// 引入简体中文语言包
-import zhCN from "vant/es/locale/lang/zh-CN";
-// 引入日文语言包
-import jaJP from 'vant/es/locale/lang/ja-JP';
 import defaultLogo from '../assets/login/logo.png';
 import shishenLogo from '../assets/login/shishenLogo.png';
 
 export default {
   setup() {
     let languageName = ref(getLocal("curLang"));
-    const {locale, t} = useI18n();
-    // 语言点击
-    // const languageClk = () => {
-    //   if (languageName.value === "zh") {
-    //     locale.value = "en";
-    //     languageName.value = "en";
-    //     setLocal("curLang", "en");
-    //     // 切换vant语言包
-    //     Locale.use("en-US", enUS);
-    //   } else {
-    //     locale.value = "zh";
-    //     languageName.value = "zh";
-    //     setLocal("curLang", "zh");
-    //     // 切换vant语言包
-    //     Locale.use("zh-CN", zhCN);
-    //   }
-    // };
-    // const compLang = computed(() => {
-    //   if (languageName.value === "en") {
-    //     return "中文";
-    //   } else {
-    //     return "English";
-    //   }
-    // });
+    const { t } = useI18n();
     const checked = ref(false); // 是否记住密码状态
     const userName = ref("");
     const userPwd = ref("");
@@ -133,7 +89,6 @@ export default {
     onMounted(() => {
       // 加载样式
       styleUrl('login');
-      // localStorage.clear();
       // 如果没有语言缓存
       if (!getLocal("curLang")) {
         //  根据浏览器语言重新缓存到localstorage
@@ -165,7 +120,6 @@ export default {
       const currentDomain = window.location.href;
       console.log("href >>>", currentDomain);
       switch (true) {
-        // case currentDomain.includes('//localhost'):
         case currentDomain.includes('sskj.qxfoodom'):  // 食神科技 sskj.qxfoodom.com
           logoName.value = shishenLogo;
           sysTitle.value = '食神科技';
@@ -186,40 +140,6 @@ export default {
       }
     };
 
-
-    const showPopover = ref(false);
-
-    // 通过 actions 属性来定义菜单选项
-    const actions = [
-      { text: '中文' },
-      { text: 'English' },
-      { text: '日本語' },
-    ];
-    // 切换语言
-    const onSelect = (action) => {
-      // showToast(action.text);
-      currentLan.value = action.text;
-      if (action.text === "中文") {
-        locale.value = "zh";
-        languageName.value = "zh";
-        setLocal("curLang", "zh");
-        // 切换vant语言包
-        Locale.use("zh-CN", zhCN);
-      } else if (action.text === "English") {
-        locale.value = "en";
-        languageName.value = "en";
-        setLocal("curLang", "en");
-        // 切换vant语言包
-        Locale.use("en-US", enUS);
-      } else if (action.text === "日本語") {
-        locale.value = "ja";
-        languageName.value = "ja";
-        setLocal("curLang", "ja");
-        // 切换vant语言包
-        Locale.use("ja-JP", jaJP);
-      }
-    }
-
     // 登录
     const onSubmit = async (values) => {
       const {data} = await login({
@@ -302,15 +222,10 @@ export default {
       registerClick,
       forgetPassword,
       sys,
-      // compLang,
-      // languageClk,
       isInWeChat,
       state,
       wxLoginHandler,
       getOpenid,
-      actions,
-      onSelect,
-      showPopover,
       currentLan,
       logoName,
       sysTitle,

+ 567 - 0
src/views/purse/index.vue

@@ -0,0 +1,567 @@
+<template>
+    <div class="wallet-page">
+        <!-- 导航栏 -->
+        <s-header name="我的钱包" :noback="false" :isBorder="false"></s-header>
+
+        <!-- 新版余额卡片 -->
+        <!-- <div class="new-balance-card">
+            <div class="card-bg">
+                <div class="wave"></div>
+                <div class="deco-circle"></div>
+            </div>
+
+            <div class="balance-content">
+                <div class="balance-header">
+                    <van-icon name="balance-pay" color="rgba(255,255,255,0.9)" size="20" />
+                    <span class="balance-label">可用余额(元)</span>
+                </div>
+
+                <div class="balance-main">
+                    <span class="currency">¥</span>
+                    <span class="amount">8,888.00</span>
+                </div>
+
+                <div class="action-buttons">
+                    <van-button round class="recharge-btn" @click="handleRecharge">
+                        <van-icon name="cash-back-record" size="16" />
+                        <span>立即充值</span>
+                    </van-button>
+
+                    <van-button round plain class="withdraw-btn" @click="handleWithdraw">
+                        <van-icon name="cash-on-deliver" size="16" />
+                        <span>申请提现</span>
+                    </van-button>
+                </div>
+            </div>
+        </div> -->
+
+        <!-- 余额概览 -->
+        <div class="balance-section">
+            <div class="section-title">
+                <van-icon name="balance-list" color="#4d6add" size="18" />
+                <span>资金账户</span>
+            </div>
+
+            <div class="balance-cards">
+                <!-- 今日收款卡片 -->
+                <div class="balance-card today-card">
+                    <div class="card-header">
+                        <van-icon name="cash-back-record" color="white" size="18" />
+                        <div class="card-title">
+                            <h3>今日收款余额(元)</h3>
+                        </div>
+                    </div>
+                    <div class="amount">8,888.00</div>
+                    <van-button icon="cash-back-record" @click="handleRecharge">
+                        立即充值
+                    </van-button>
+                    <!-- <div class="hint-text">
+                        <van-icon name="info" size="12" />
+                        <span>T+1自动转入可提现余额</span>
+                    </div> -->
+                </div>
+
+                <!-- 可提现卡片 -->
+                <div class="balance-card withdraw-card">
+                    <div class="card-header">
+                        <van-icon name="cash-on-deliver" color="white" size="18" />
+                        <div class="card-title">
+                            <h3>可提现余额(元)</h3>
+                        </div>
+                    </div>
+                    <div class="amount">6,666.00</div>
+                    <van-button icon="cash-on-deliver" @click="handleWithdraw">
+                        立即提现
+                    </van-button>
+                </div>
+            </div>
+        </div>
+
+        <!-- 记录入口 -->
+        <div class="record-section">
+            <div class="section-title">
+                <van-icon name="records" color="#4d6add" size="18" />
+                <span>交易记录</span>
+            </div>
+            <div class="record-cards">
+                <div class="record-card" @click="goToAutoRecord">
+                    <div class="card-icon auto">
+                        <van-icon name="passed" size="24" />
+                    </div>
+                    <div class="card-content">
+                        <h3>自动结算</h3>
+                        <p>查看系统自动结算记录</p>
+                    </div>
+                    <van-icon name="arrow" size="16" color="#ccc" />
+                </div>
+
+                <div class="record-card" @click="goToManualRecord">
+                    <div class="card-icon manual">
+                        <van-icon name="cash-on-deliver" size="24" />
+                    </div>
+                    <div class="card-content">
+                        <h3>手动提现</h3>
+                        <p>查看手动提现操作记录</p>
+                    </div>
+                    <van-icon name="arrow" size="16" color="#ccc" />
+                </div>
+            </div>
+        </div>
+
+        <!-- 银行卡管理 -->
+        <div class="bank-section">
+            <div class="section-title">
+                <van-icon name="credit-pay" color="#4d6add" size="18" />
+                <span>银行卡管理</span>
+            </div>
+
+            <div class="bank-cards">
+                <!-- 已绑卡状态 -->
+                <div class="bank-card" v-if="hasBankCard">
+                    <div class="card-header">
+                        <van-icon name="card" size="20" />
+                        <span>储蓄卡</span>
+                    </div>
+                    <div class="card-number">****​ ​****​ ​****​ 1234</div>
+                    <div class="card-footer">
+                        <span>中国工商银行</span>
+                        <van-icon name="ellipsis" size="16" />
+                    </div>
+                    <div class="auto-settle-hint" v-if="autoSettle">
+                        <van-icon name="checked" color="#4d6add" size="18" />
+                        <span>本卡已启用自动结算</span>
+                    </div>
+                    <div class="auto-settle-hint" v-if="!autoSettle">
+                        <van-icon name="circle" color="#4d6add" size="18" />
+                        <span>本卡已关闭自动结算</span>
+                    </div>
+                </div>
+
+                <!-- 未绑卡状态 -->
+                <div class="bank-card empty-state" v-else>
+                    <div class="empty-content">
+                        <p class="empty-title">尚未绑定银行卡</p>
+                        <p class="empty-desc">绑定后可享受快捷支付服务</p>
+                        <van-button type="primary" round size="small" @click="handleBindCard" class="bind-btn">
+                            <van-icon name="plus" size="16" />
+                            立即绑卡
+                        </van-button>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="bottom-spacer"></div>
+    </div>
+</template>
+
+<script>
+import sHeader from "../../components/SimpleHeader";
+
+export default {
+    components: { sHeader },
+    setup() {
+
+
+        const handleRecharge = () => {
+            console.log('handleRecharge');
+        };
+
+        const handleWithdraw = () => {
+            console.log('handleWithdraw');
+        };
+
+        const goToAutoRecord = () => {
+            console.log('goToAutoRecord');
+        };
+
+        const goToManualRecord = () => {
+            console.log('goToManualRecord');
+        }
+
+        return {
+            handleRecharge,
+            handleWithdraw,
+            goToAutoRecord,
+            goToManualRecord
+        };
+    }
+};
+</script>
+
+<style lang="less" scoped>
+/* 公共样式 */
+.wallet-page {
+    background: #f8f9ff;
+}
+
+.section-title {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    padding: 10px 10px;
+    font-size: 18px;
+    color: #4d6add;
+    font-weight: 700;
+}
+
+/**
+余额卡片
+ */
+.balance-section {
+    margin: 10px 0px;
+}
+
+.balance-cards {
+    display: grid;
+    gap: 15px;
+    padding: 0 12px;
+}
+
+/* 基础卡片样式 */
+.balance-card {
+    border-radius: 16px;
+    padding: 15px;
+    position: relative;
+    overflow: hidden;
+    box-shadow: 0 8px 24px -6px rgba(0, 0, 0, 0.12);
+    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+    min-height: 180px;
+}
+
+/* 卡片悬停动效 */
+.balance-card:hover {
+    transform: translateY(-5px);
+}
+
+/* 今日收款卡片 */
+.today-card {
+    background: linear-gradient(135deg,
+            #4f6cdf 0%,
+            #6b8cff 100%);
+    color: white;
+    border: 1px solid rgba(255, 255, 255, 0.15);
+    --start-color: #4f6cdf;
+    --end-color: #6b8cff;
+}
+
+/* 可提现卡片 */
+.withdraw-card {
+    background: linear-gradient(135deg,
+            #38b48d 0%,
+            #5cd4b0 100%);
+    color: white;
+    border: 1px solid rgba(255, 255, 255, 0.15);
+    --start-color: #38b48d;
+    --end-color: #5cd4b0;
+}
+
+/* 装饰性光斑 */
+.balance-card::after {
+    content: "";
+    position: absolute;
+    width: 200px;
+    height: 200px;
+    background: radial-gradient(circle,
+            rgba(255, 255, 255, 0.15) 0%,
+            rgba(255, 255, 255, 0) 70%);
+    top: -50px;
+    right: -50px;
+}
+
+/* 卡片内容样式 */
+.card-header {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    margin-bottom: 10px;
+    position: relative;
+    z-index: 1;
+}
+
+.card-header .van-icon {
+    background: rgba(255, 255, 255, 0.15);
+    padding: 8px;
+    border-radius: 10px;
+}
+
+.card-title h3 {
+    margin: 0;
+    font-size: 15px;
+    font-weight: 500;
+    letter-spacing: 0.5px;
+}
+
+.amount {
+    font-size: 30px;
+    font-weight: 600;
+    margin: 10px 0;
+    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+    position: relative;
+    z-index: 1;
+}
+
+/* 按钮样式 */
+/* 限定在卡片内的按钮 */
+.balance-card .van-button {
+    backdrop-filter: blur(8px);
+    background: rgba(255, 255, 255, 0.15) !important;
+    border: 1px solid rgba(255, 255, 255, 0.3) !important;
+    color: white !important;
+    border-radius: 20px;
+    transition: all 0.3s ease;
+    width: auto
+}
+
+/* 仅影响卡片内的按钮悬停状态 */
+.balance-card .van-button:hover {
+    background: rgba(255, 255, 255, 0.25) !important;
+    transform: scale(1.05);
+}
+
+
+/* 提示文字 */
+.hint-text {
+    font-size: 12px;
+    opacity: 0.9;
+    margin-top: 12px;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+}
+
+/* 响应式调整 */
+@media (max-width: 480px) {
+    .amount {
+        font-size: 28px;
+    }
+
+    .balance-card {
+        min-height: 140px;
+        padding: 16px;
+    }
+}
+
+/* 动态光效增强 */
+@keyframes glow {
+    0% {
+        opacity: 0.5;
+    }
+
+    50% {
+        opacity: 0.8;
+    }
+
+    100% {
+        opacity: 0.5;
+    }
+}
+
+.balance-card::before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: linear-gradient(45deg,
+            transparent 40%,
+            rgba(255, 255, 255, 0.1) 50%,
+            transparent 60%);
+    animation: glow 3s infinite;
+    pointer-events: none;
+}
+
+
+
+/* 记录入口 */
+.record-section {
+    margin-top: 5px;
+}
+
+.record-cards {
+    display: grid;
+    gap: 12px;
+}
+
+.record-card {
+    background: white;
+    border-radius: 12px;
+    padding: 0 10px;
+    margin: 0 10px;
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    box-shadow: 0 2px 8px rgba(77, 106, 221, 0.05);
+    transition: transform 0.2s ease;
+
+    &:active {
+        transform: scale(0.98);
+    }
+}
+
+.card-icon {
+    width: 44px;
+    height: 44px;
+    border-radius: 10px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    &.auto {
+        background: rgba(77, 106, 221, 0.1);
+        color: #4d6add;
+    }
+
+    &.manual {
+        background: rgba(255, 152, 0, 0.1);
+        color: #ff9500;
+    }
+}
+
+.card-content {
+    flex: 1;
+
+    h3 {
+        font-size: 15px;
+        color: #333;
+        margin-bottom: 4px;
+    }
+
+    p {
+        font-size: 12px;
+        color: #999;
+    }
+}
+
+/* 银行卡管理 */
+.bank-section {
+    margin-top: 5px;
+}
+
+.bank-cards {
+    display: grid;
+    gap: 12px;
+}
+
+.bank-card {
+    background: white;
+    border-radius: 12px;
+    padding: 10px 5px;
+    margin: 0 10px;
+    // box-shadow: 0 2px 8px rgba(77, 106, 221, 0.05);
+    box-shadow: 0 4px 12px rgba(77, 106, 221, 0.08);
+    position: relative;
+    overflow: hidden;
+
+    &::before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 0;
+        bottom: 0;
+        width: 4px;
+        background: #4d6add;
+    }
+
+    &.backup::before {
+        background: #6a82ee;
+    }
+}
+
+.card-header {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin-bottom: 12px;
+
+    span {
+        font-size: 14px;
+        color: #666;
+    }
+
+    .van-tag {
+        transform: scale(0.9);
+    }
+}
+
+.card-number {
+    font-family: monospace;
+    font-size: 18px;
+    color: #333;
+    letter-spacing: 2px;
+    margin: 16px 0;
+}
+
+.card-footer {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #999;
+    font-size: 12px;
+}
+
+/* 统一按钮样式 */
+:deep(.van-button) {
+    font-weight: 500;
+    letter-spacing: 0.5px;
+}
+
+.auto-settle-hint {
+    margin-top: 12px;
+    padding-top: 12px;
+    border-top: 1px dashed #eee;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    color: #666;
+    font-size: 13px;
+}
+
+/* 未绑卡状态样式 */
+.bank-card.empty-state {
+    background: linear-gradient(135deg, #ffffff 0%, #f6f7fc 100%);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.empty-content {
+    text-align: center;
+    padding: 10px;
+}
+
+.empty-icon {
+    margin-bottom: 12px;
+    filter: drop-shadow(0 2px 4px rgba(77, 106, 221, 0.1));
+}
+
+.empty-title {
+    color: #2d3447;
+    font-size: 16px;
+    margin-bottom: 8px;
+}
+
+.empty-desc {
+    color: #a3aac7;
+    font-size: 12px;
+    margin-bottom: 16px;
+}
+
+.bind-btn {
+    background: #4d6add;
+    padding: 8px 24px;
+    box-shadow: 0 3px 8px rgba(77, 106, 221, 0.2);
+}
+
+.bind-btn .van-icon {
+    margin-right: 6px;
+    vertical-align: -2px;
+}
+
+/* 占位元素样式 */
+.bottom-spacer {
+  height: 50px;
+  height: calc(60px + env(safe-area-inset-bottom));
+  pointer-events: none; /* 防止遮挡点击 */
+}
+</style>

+ 10 - 2
src/views/register.vue

@@ -143,7 +143,7 @@
 </template>
 <script>
 import md5 from 'js-md5';
-import { ref, onMounted, reactive, toRefs } from 'vue';
+import { ref, onMounted, reactive, toRefs, watch } from 'vue';
 import { showFailToast, showToast } from 'vant';
 import { sentRegisterCode, tAdminSave } from '@/service/register';
 import sHeader from '@/components/SimpleHeader';
@@ -159,7 +159,7 @@ import { useCascaderAreaData } from '@vant/area-data';
 export default {
   setup() {
     // 引入语言
-    let languageName = ref(getLocal("curLang"));
+    const languageName = ref(getLocal("curLang"));
     const { t } = useI18n();
     const active = ref(0);
     const username = ref('');
@@ -187,6 +187,14 @@ export default {
     const cityValue = ref('');
     const countryOptions = ref(languageName.value == 'zh' ? countriesData : countriesDataEn);
 
+    const { locale } = useI18n()
+    watch(locale, (newValue) => {
+      if (newValue == 'zh') {
+        countryOptions.value = countriesData;
+      } else {
+        countryOptions.value = countriesDataEn;
+      }
+    });
     const onConfirmCountry = ({ selectedOptions }) => {
       cityValue.value = selectedOptions.map((option) => option.text).join('/');
       citiesValue.value = selectedOptions[1]?.value;

+ 1 - 1
src/views/robotRanking.vue

@@ -58,7 +58,7 @@
       </div>
     </div>
     <!-- 筛选弹窗 -->
-    <kDialog :dialogTitle="$t('subLedgerManage.search.title')" :confirmBtnTxt="$t('subLedgerManage.search.screen')"
+    <kDialog :dialogTitle="$t('orderExport.searchPop.title')" :confirmBtnTxt="$t('orderExport.searchPop.filter')"
       ref="kDialogRef" @confirmclk="confirmClk">
       <template #content>
         <div class="cust_vantBorder">

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

@@ -1,47 +1,77 @@
 <template>
   <!-- 角色权限 - 添加角色 -->
-  <div class="accountPerPage 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: 50%; padding: 1em">{{ 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,73 +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) {
-        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
-        });
       }
-    };
+
+      for (const key in $M_Menus) {
+        menuCodeList.value.push(key);
+      }
+
+      for (const key in remoteMenus) {
+        remoteCodeList.value.push(key);
+      }
+      
+    }
 
     const onSubmit = async () => {
       if (roleItem) {
@@ -146,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 = {
@@ -163,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,
     };
   },
 };
@@ -196,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: #2c87c8;
+      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: #2c87c8;
+            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: #2c87c8;
+              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: #2c87c8;
+            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, #2c87c8, #2c87c8);
+      border: none;
+      box-shadow: 0 4px 12px rgba(78, 107, 221, 0.2);
+
+      &:active {
+        opacity: 0.9;
+        transform: scale(0.98);
+      }
+    }
+  }
+}
 </style>

+ 228 - 101
src/views/role/index.vue

@@ -1,66 +1,73 @@
 <template>
   <!-- 角色权限设置 -->
-  <div class="rolePage flex-col">
+  <div class="role-management-page">
     <s-header :name="$t('role.rolePermissionSettings')" :noback="false"></s-header>
-    <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>
 
@@ -68,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",
@@ -83,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();
     });
     // 搜索点击
@@ -134,40 +114,41 @@ 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 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);
+      })
     }
-    const roleEdit = async (item) => { localStorage.setItem('roleItem', JSON.stringify(item)); router.push('/roleSet'); }
+
     return {
       searchRef,
       searchClick,
       pushAdd,
       search,
       accountPerList,
-      showRole,
-      roleDelete,
-      operCheckShow,
-      operCheckClear,
-      operCheck,
       roleEdit,
       ...toRefs(searchParams),
+      confirmDelete,
     };
   },
 };
@@ -175,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: #2c87c8;
+
+                  &.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: #2c87c8;
+            border: 1px solid #d0d5ff;
+          }
+
+          .delete-btn {
+            background: #fff5f5;
+            color: #ff5252;
+            border: 1px solid #ffd0d0;
+          }
+        }
+      }
+    }
+  }
+}
 </style>

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

@@ -15,12 +15,12 @@
           <div class="taskTitle">{{ $t('taskMessage.distributionApplicationApproval') }}</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>

+ 40 - 31
src/views/user.vue

@@ -198,7 +198,8 @@
           </div>
 
           <!-- 提现帐号 -->
-          <div v-if="isInland && user.type == '2'" class="taskListRow l-flex-RC" @click="pushPageList('/settlement')">
+          <div v-if="isInland && (user.type == 2 || user.type == 3)" class="taskListRow l-flex-RC"
+            @click="pushPageList('/settlement')">
             <div class="taskIcon joinPayMchIcon"></div>
             <div class="taskRight">
               <div class="taskTitle">{{ $t("user.settlementAccount") }}</div>
@@ -747,44 +748,52 @@ export default {
     const name = ref("");
     // 国内省市
     const getArea = async () => {
-      const { data } = await getAreaById({
-        areaId: areaId.value,
-      });
-      if (data.code === "00000") {
-        fullName.value = data.data.fullName;
-        name.value = data.data.name;
-        if (fullName.value != name.value) {
-          fullName.value = fullName.value.replace(name.value, "").trim();
-          fullName.value = fullName.value + "/" + name.value;
+      try {
+        const { data } = await getAreaById({
+          areaId: areaId.value,
+        });
+        if (data.code === "00000") {
+          fullName.value = data.data.fullName;
+          name.value = data.data.name;
+          if (fullName.value != name.value) {
+            fullName.value = fullName.value.replace(name.value, "").trim();
+            fullName.value = fullName.value + "/" + name.value;
+          }
+          fieldValue.value = fullName.value;
         }
-        fieldValue.value = fullName.value;
+      } catch (error) {
+        console.error(error);
       }
     };
     const countryValue = ref("");
     const cityValue = ref("");
     // 国外城市
     const getCities = async () => {
-      const { data } = await getCitiesById({
-        areaId: areaId.value,
-      });
-      if (data.data != "") {
-        const citiesCode = data.data.split('/')[0];
-        const countriesCode = data.data.split('/')[1];
-        countryOptions.value.find(item => {
-          if (item.value === countriesCode) {
-            countryValue.value = item.text;
-            item.children.find(item => {
-              if (item.value === citiesCode) {
-                cityValue.value = item.text;
-              }
-            });
-          }
+      try {
+        const { data } = await getCitiesById({
+          areaId: areaId.value,
         });
-        if (countryValue.value == cityValue.value) {
-          fieldValue.value = cityValue.value;
-        } else {
-          fieldValue.value = countryValue.value + "/" + cityValue.value;
+        if (data.data != "") {
+          const citiesCode = data.data.split('/')[0];
+          const countriesCode = data.data.split('/')[1];
+          countryOptions.value.find(item => {
+            if (item.value === countriesCode) {
+              countryValue.value = item.text;
+              item.children.find(item => {
+                if (item.value === citiesCode) {
+                  cityValue.value = item.text;
+                }
+              });
+            }
+          });
+          if (countryValue.value == cityValue.value) {
+            fieldValue.value = cityValue.value;
+          } else {
+            fieldValue.value = countryValue.value + "/" + cityValue.value;
+          }
         }
+      } catch (error) {
+        console.error(error);
       }
     };
     // 地区弹窗
@@ -909,7 +918,7 @@ export default {
       tzFieldValue.value = e;
     };
 
-    
+
     // 搜索关键词
     const searchValue = ref('');
     const countryData = ref(languageName.value == 'zh' ? countriesData : countriesDataEn);