Browse Source

feat:"增加维护记录打卡功能"

soobin 2 months ago
parent
commit
1cf08c00d5
86 changed files with 8393 additions and 24824 deletions
  1. 2 0
      package.json
  2. 0 1
      public/tinymce/langs/zh-Hans.js
  3. 0 462
      public/tinymce/langs/zh_CN.js
  4. 0 66
      public/tinymce/skins/content/dark/content.css
  5. 0 1
      public/tinymce/skins/content/dark/content.min.css
  6. 0 61
      public/tinymce/skins/content/default/content.css
  7. 0 1
      public/tinymce/skins/content/default/content.min.css
  8. 0 66
      public/tinymce/skins/content/document/content.css
  9. 0 1
      public/tinymce/skins/content/document/content.min.css
  10. 0 66
      public/tinymce/skins/content/tinymce-5-dark/content.css
  11. 0 1
      public/tinymce/skins/content/tinymce-5-dark/content.min.css
  12. 0 61
      public/tinymce/skins/content/tinymce-5/content.css
  13. 0 1
      public/tinymce/skins/content/tinymce-5/content.min.css
  14. 0 62
      public/tinymce/skins/content/writer/content.css
  15. 0 1
      public/tinymce/skins/content/writer/content.min.css
  16. 0 766
      public/tinymce/skins/ui/oxide-dark/content.css
  17. 0 779
      public/tinymce/skins/ui/oxide-dark/content.inline.css
  18. 0 1
      public/tinymce/skins/ui/oxide-dark/content.inline.min.css
  19. 0 1
      public/tinymce/skins/ui/oxide-dark/content.min.css
  20. 0 3762
      public/tinymce/skins/ui/oxide-dark/skin.css
  21. 0 1
      public/tinymce/skins/ui/oxide-dark/skin.min.css
  22. 0 30
      public/tinymce/skins/ui/oxide-dark/skin.shadowdom.css
  23. 0 1
      public/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
  24. 0 785
      public/tinymce/skins/ui/oxide/content.css
  25. 0 779
      public/tinymce/skins/ui/oxide/content.inline.css
  26. 0 1
      public/tinymce/skins/ui/oxide/content.inline.min.css
  27. 0 1
      public/tinymce/skins/ui/oxide/content.min.css
  28. 0 3759
      public/tinymce/skins/ui/oxide/skin.css
  29. 0 1
      public/tinymce/skins/ui/oxide/skin.min.css
  30. 0 30
      public/tinymce/skins/ui/oxide/skin.shadowdom.css
  31. 0 1
      public/tinymce/skins/ui/oxide/skin.shadowdom.min.css
  32. 0 766
      public/tinymce/skins/ui/tinymce-5-dark/content.css
  33. 0 779
      public/tinymce/skins/ui/tinymce-5-dark/content.inline.css
  34. 0 1
      public/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css
  35. 0 1
      public/tinymce/skins/ui/tinymce-5-dark/content.min.css
  36. 0 3853
      public/tinymce/skins/ui/tinymce-5-dark/skin.css
  37. 0 1
      public/tinymce/skins/ui/tinymce-5-dark/skin.min.css
  38. 0 30
      public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.css
  39. 0 1
      public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.min.css
  40. 0 785
      public/tinymce/skins/ui/tinymce-5/content.css
  41. 0 779
      public/tinymce/skins/ui/tinymce-5/content.inline.css
  42. 0 1
      public/tinymce/skins/ui/tinymce-5/content.inline.min.css
  43. 0 1
      public/tinymce/skins/ui/tinymce-5/content.min.css
  44. 0 3853
      public/tinymce/skins/ui/tinymce-5/skin.css
  45. 0 1
      public/tinymce/skins/ui/tinymce-5/skin.min.css
  46. 0 30
      public/tinymce/skins/ui/tinymce-5/skin.shadowdom.css
  47. 0 1
      public/tinymce/skins/ui/tinymce-5/skin.shadowdom.min.css
  48. BIN
      src/assets/device/operIcon/maintenance.png
  49. 83 2
      src/assets/language/en.json
  50. 83 2
      src/assets/language/es.json
  51. 83 2
      src/assets/language/fr.json
  52. 83 2
      src/assets/language/ja.json
  53. 83 2
      src/assets/language/pt.json
  54. 83 2
      src/assets/language/ru.json
  55. 83 2
      src/assets/language/uk.json
  56. 89 4
      src/assets/language/zh.json
  57. 0 123
      src/components/TinymceEditor/index.vue
  58. 21 0
      src/router/index.js
  59. 5 0
      src/service/device/index.js
  60. 17 0
      src/service/maintenance/index.js
  61. 343 124
      src/views/advertManage/adSet.vue
  62. 252 40
      src/views/advertManage/advertRule/add.vue
  63. 283 77
      src/views/advertManage/advertRule/index.vue
  64. 316 53
      src/views/advertManage/advertRule/screen.vue
  65. 381 97
      src/views/advertManage/index.vue
  66. 167 39
      src/views/announcement/index.vue
  67. 44 6
      src/views/device/deviceOper.vue
  68. 339 91
      src/views/device/devicePassword/index.vue
  69. 394 146
      src/views/device/doSugar.vue
  70. 471 0
      src/views/device/maintenance/add.vue
  71. 911 0
      src/views/device/maintenance/index.vue
  72. 102 0
      src/views/device/maintenance/success.vue
  73. 9 2
      src/views/device/modifyPrice/index.vue
  74. 20 16
      src/views/device/modulation.vue
  75. 212 48
      src/views/device/paramsSet/index.vue
  76. 460 135
      src/views/device/paramsSet/paramsSetInfo.vue
  77. 395 119
      src/views/device/returnCoin/index.vue
  78. 32 24
      src/views/device/showGoods/index.vue
  79. 1 1
      src/views/device/tax/index.vue
  80. 220 138
      src/views/device/toDaySugarList.vue
  81. 3 0
      src/views/device/viewLogs/index.vue
  82. 509 137
      src/views/discountCode/index.vue
  83. 612 105
      src/views/discountCode/payCode.vue
  84. 541 234
      src/views/distributionSet/detail.vue
  85. 1 1
      src/views/home/index.vue
  86. 660 618
      src/views/purse/index.vue

+ 2 - 0
package.json

@@ -17,6 +17,8 @@
     "@vant/area-data": "^1.5.0",
     "@vant/compat": "^1.0.0",
     "@vant/touch-emulator": "^1.4.0",
+    "@wangeditor/editor": "^5.1.23",
+    "@wangeditor/editor-for-vue": "^5.1.12",
     "axios": "^0.20.0",
     "better-scroll": "^2.3.0",
     "big.js": "^6.2.1",

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/langs/zh-Hans.js


+ 0 - 462
public/tinymce/langs/zh_CN.js

@@ -1,462 +0,0 @@
-tinymce.addI18n('zh_CN',{
-	"Redo": "\u91cd\u505a",
-	"Undo": "\u64a4\u9500",
-	"Cut": "\u526a\u5207",
-	"Copy": "\u590d\u5236",
-	"Paste": "\u7c98\u8d34",
-	"Select all": "\u5168\u9009",
-	"New document": "\u65b0\u6587\u4ef6",
-	"Ok": "\u786e\u5b9a",
-	"Cancel": "\u53d6\u6d88",
-	"Visual aids": "\u7f51\u683c\u7ebf",
-	"Bold": "\u7c97\u4f53",
-	"Italic": "\u659c\u4f53",
-	"Underline": "\u4e0b\u5212\u7ebf",
-	"Strikethrough": "\u5220\u9664\u7ebf",
-	"Superscript": "\u4e0a\u6807",
-	"Subscript": "\u4e0b\u6807",
-	"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
-	"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
-	"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
-	"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
-	"Justify": "\u4e24\u7aef\u5bf9\u9f50",
-	"Bullet list": "\u9879\u76ee\u7b26\u53f7",
-	"Numbered list": "\u7f16\u53f7\u5217\u8868",
-	"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
-	"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
-	"Close": "\u5173\u95ed",
-	"Formats": "\u683c\u5f0f",
-	"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
-	"Headers": "\u6807\u9898",
-	"Header 1": "\u6807\u98981",
-	"Header 2": "\u6807\u98982",
-	"Header 3": "\u6807\u98983",
-	"Header 4": "\u6807\u98984",
-	"Header 5": "\u6807\u98985",
-	"Header 6": "\u6807\u98986",
-	"Headings": "\u6807\u9898",
-	"Heading 1": "\u6807\u98981",
-	"Heading 2": "\u6807\u98982",
-	"Heading 3": "\u6807\u98983",
-	"Heading 4": "\u6807\u98984",
-	"Heading 5": "\u6807\u98985",
-	"Heading 6": "\u6807\u98986",
-	"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
-	"Div": "Div",
-	"Pre": "Pre",
-	"Code": "\u4ee3\u7801",
-	"Paragraph": "\u6bb5\u843d",
-	"Blockquote": "\u5f15\u6587\u533a\u5757",
-	"Inline": "\u6587\u672c",
-	"Blocks": "\u57fa\u5757",
-	"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
-	"Fonts": "\u5b57\u4f53",
-	"Font Sizes": "\u5b57\u53f7",
-	"Class": "\u7c7b\u578b",
-	"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
-	"OR": "\u6216",
-	"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
-	"Upload": "\u4e0a\u4f20",
-	"Block": "\u5757",
-	"Align": "\u5bf9\u9f50",
-	"Default": "\u9ed8\u8ba4",
-	"Circle": "\u7a7a\u5fc3\u5706",
-	"Disc": "\u5b9e\u5fc3\u5706",
-	"Square": "\u65b9\u5757",
-	"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
-	"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
-	"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
-	"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
-	"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
-	"Anchor...": "\u951a\u70b9...",
-	"Name": "\u540d\u79f0",
-	"Id": "\u6807\u8bc6\u7b26",
-	"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
-	"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
-	"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
-	"Special character...": "\u7279\u6b8a\u5b57\u7b26...",
-	"Source code": "\u6e90\u4ee3\u7801",
-	"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
-	"Language": "\u8bed\u8a00",
-	"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
-	"Color Picker": "\u9009\u8272\u5668",
-	"R": "R",
-	"G": "G",
-	"B": "B",
-	"Left to right": "\u4ece\u5de6\u5230\u53f3",
-	"Right to left": "\u4ece\u53f3\u5230\u5de6",
-	"Emoticons": "\u8868\u60c5",
-	"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
-	"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
-	"Title": "\u6807\u9898",
-	"Keywords": "\u5173\u952e\u8bcd",
-	"Description": "\u63cf\u8ff0",
-	"Robots": "\u673a\u5668\u4eba",
-	"Author": "\u4f5c\u8005",
-	"Encoding": "\u7f16\u7801",
-	"Fullscreen": "\u5168\u5c4f",
-	"Action": "\u64cd\u4f5c",
-	"Shortcut": "\u5feb\u6377\u952e",
-	"Help": "\u5e2e\u52a9",
-	"Address": "\u5730\u5740",
-	"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
-	"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
-	"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
-	"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
-	"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
-	"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
-	"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
-	"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
-	"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
-	"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
-	"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
-	"Plugins": "\u63d2\u4ef6",
-	"Handy Shortcuts": "\u5feb\u6377\u952e",
-	"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
-	"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
-	"Alternative description": "\u66ff\u4ee3\u63cf\u8ff0",
-	"Accessibility": "\u8f85\u52a9\u529f\u80fd",
-	"Image is decorative": "\u56fe\u50cf\u662f\u88c5\u9970\u6027\u7684",
-	"Source": "\u5730\u5740",
-	"Dimensions": "\u5927\u5c0f",
-	"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
-	"General": "\u666e\u901a",
-	"Advanced": "\u9ad8\u7ea7",
-	"Style": "\u6837\u5f0f",
-	"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
-	"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
-	"Border": "\u8fb9\u6846",
-	"Insert image": "\u63d2\u5165\u56fe\u7247",
-	"Image...": "\u56fe\u7247...",
-	"Image list": "\u56fe\u7247\u5217\u8868",
-	"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
-	"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
-	"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
-	"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
-	"Edit image": "\u7f16\u8f91\u56fe\u7247",
-	"Image options": "\u56fe\u7247\u9009\u9879",
-	"Zoom in": "\u653e\u5927",
-	"Zoom out": "\u7f29\u5c0f",
-	"Crop": "\u88c1\u526a",
-	"Resize": "\u8c03\u6574\u5927\u5c0f",
-	"Orientation": "\u65b9\u5411",
-	"Brightness": "\u4eae\u5ea6",
-	"Sharpen": "\u9510\u5316",
-	"Contrast": "\u5bf9\u6bd4\u5ea6",
-	"Color levels": "\u989c\u8272\u5c42\u6b21",
-	"Gamma": "\u4f3d\u9a6c\u503c",
-	"Invert": "\u53cd\u8f6c",
-	"Apply": "\u5e94\u7528",
-	"Back": "\u540e\u9000",
-	"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
-	"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
-	"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
-	"Text to display": "\u663e\u793a\u6587\u5b57",
-	"Url": "\u5730\u5740",
-	"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
-	"Current window": "\u5f53\u524d\u7a97\u53e3",
-	"None": "\u65e0",
-	"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
-	"Open link": "\u6253\u5f00\u94fe\u63a5",
-	"Remove link": "\u5220\u9664\u94fe\u63a5",
-	"Anchors": "\u951a\u70b9",
-	"Link...": "\u94fe\u63a5...",
-	"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
-	"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
-	"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
-	"The URL you entered seems to be an external link. Do you want to add the required https:\/\/ prefix?": "\u60a8\u8f93\u5165\u7684 URL \u4f3c\u4e4e\u662f\u4e00\u4e2a\u5916\u90e8\u94fe\u63a5\u3002\u60a8\u60f3\u6dfb\u52a0\u6240\u9700\u7684 https:\/\/ \u524d\u7f00\u5417\uff1f",
-	"Link list": "\u94fe\u63a5\u5217\u8868",
-	"Insert video": "\u63d2\u5165\u89c6\u9891",
-	"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
-	"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
-	"Alternative source": "\u955c\u50cf",
-	"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
-	"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
-	"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
-	"Embed": "\u5185\u5d4c",
-	"Media...": "\u591a\u5a92\u4f53...",
-	"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
-	"Page break": "\u5206\u9875\u7b26",
-	"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
-	"Preview": "\u9884\u89c8",
-	"Print...": "\u6253\u5370...",
-	"Save": "\u4fdd\u5b58",
-	"Find": "\u67e5\u627e",
-	"Replace with": "\u66ff\u6362\u4e3a",
-	"Replace": "\u66ff\u6362",
-	"Replace all": "\u5168\u90e8\u66ff\u6362",
-	"Previous": "\u4e0a\u4e00\u4e2a",
-	"Next": "\u4e0b\u4e00\u4e2a",
-	"Find and Replace": "\u67e5\u627e\u548c\u66ff\u6362",
-	"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
-	"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
-	"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
-	"Find whole words only": "\u5168\u5b57\u5339\u914d",
-	"Find in selection": "\u5728\u9009\u533a\u4e2d\u67e5\u627e",
-	"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
-	"Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
-	"No misspellings found.": "\u6ca1\u6709\u53d1\u73b0\u62fc\u5199\u9519\u8bef",
-	"Ignore": "\u5ffd\u7565",
-	"Ignore all": "\u5168\u90e8\u5ffd\u7565",
-	"Finish": "\u5b8c\u6210",
-	"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
-	"Insert table": "\u63d2\u5165\u8868\u683c",
-	"Table properties": "\u8868\u683c\u5c5e\u6027",
-	"Delete table": "\u5220\u9664\u8868\u683c",
-	"Cell": "\u5355\u5143\u683c",
-	"Row": "\u884c",
-	"Column": "\u5217",
-	"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
-	"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
-	"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
-	"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
-	"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
-	"Delete row": "\u5220\u9664\u884c",
-	"Row properties": "\u884c\u5c5e\u6027",
-	"Cut row": "\u526a\u5207\u884c",
-	"Copy row": "\u590d\u5236\u884c",
-	"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
-	"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
-	"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
-	"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
-	"Delete column": "\u5220\u9664\u5217",
-	"Cols": "\u5217",
-	"Rows": "\u884c",
-	"Width": "\u5bbd",
-	"Height": "\u9ad8",
-	"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
-	"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
-	"Caption": "\u6807\u9898",
-	"Show caption": "\u663e\u793a\u6807\u9898",
-	"Left": "\u5de6\u5bf9\u9f50",
-	"Center": "\u5c45\u4e2d",
-	"Right": "\u53f3\u5bf9\u9f50",
-	"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
-	"Scope": "\u8303\u56f4",
-	"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
-	"H Align": "\u6c34\u5e73\u5bf9\u9f50",
-	"V Align": "\u5782\u76f4\u5bf9\u9f50",
-	"Top": "\u9876\u90e8\u5bf9\u9f50",
-	"Middle": "\u5782\u76f4\u5c45\u4e2d",
-	"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
-	"Header cell": "\u8868\u5934\u5355\u5143\u683c",
-	"Row group": "\u884c\u7ec4",
-	"Column group": "\u5217\u7ec4",
-	"Row type": "\u884c\u7c7b\u578b",
-	"Header": "\u8868\u5934",
-	"Body": "\u8868\u4f53",
-	"Footer": "\u8868\u5c3e",
-	"Border color": "\u8fb9\u6846\u989c\u8272",
-	"Insert template...": "\u63d2\u5165\u6a21\u677f...",
-	"Templates": "\u6a21\u677f",
-	"Template": "\u6a21\u677f",
-	"Text color": "\u6587\u5b57\u989c\u8272",
-	"Background color": "\u80cc\u666f\u8272",
-	"Custom...": "\u81ea\u5b9a\u4e49...",
-	"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
-	"No color": "\u65e0",
-	"Remove color": "\u79fb\u9664\u989c\u8272",
-	"Table of Contents": "\u5185\u5bb9\u5217\u8868",
-	"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
-	"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
-	"Word count": "\u5b57\u6570",
-	"Count": "\u8ba1\u6570",
-	"Document": "\u6587\u6863",
-	"Selection": "\u9009\u62e9",
-	"Words": "\u5355\u8bcd",
-	"Words: {0}": "\u5b57\u6570\uff1a{0}",
-	"{0} words": "{0} \u5b57",
-	"File": "\u6587\u4ef6",
-	"Edit": "\u7f16\u8f91",
-	"Insert": "\u63d2\u5165",
-	"View": "\u89c6\u56fe",
-	"Format": "\u683c\u5f0f",
-	"Table": "\u8868\u683c",
-	"Tools": "\u5de5\u5177",
-	"Powered by {0}": "\u7531{0}\u9a71\u52a8",
-	"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
-	"Image title": "\u56fe\u7247\u6807\u9898",
-	"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
-	"Border style": "\u8fb9\u6846\u6837\u5f0f",
-	"Error": "\u9519\u8bef",
-	"Warn": "\u8b66\u544a",
-	"Valid": "\u6709\u6548",
-	"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
-	"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
-	"System Font": "\u7cfb\u7edf\u5b57\u4f53",
-	"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
-	"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
-	"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
-	"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
-	"example": "\u793a\u4f8b",
-	"Search": "\u641c\u7d22",
-	"All": "\u5168\u90e8",
-	"Currency": "\u8d27\u5e01",
-	"Text": "\u6587\u5b57",
-	"Quotations": "\u5f15\u7528",
-	"Mathematical": "\u6570\u5b66",
-	"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
-	"Symbols": "\u7b26\u53f7",
-	"Arrows": "\u7bad\u5934",
-	"User Defined": "\u81ea\u5b9a\u4e49",
-	"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
-	"currency sign": "\u8d27\u5e01\u7b26\u53f7",
-	"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
-	"colon sign": "\u5192\u53f7",
-	"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
-	"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
-	"lira sign": "\u91cc\u62c9\u7b26\u53f7",
-	"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
-	"naira sign": "\u5948\u62c9\u7b26\u53f7",
-	"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
-	"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
-	"won sign": "\u97e9\u5143\u7b26\u53f7",
-	"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
-	"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
-	"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
-	"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
-	"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
-	"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
-	"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
-	"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
-	"austral sign": "\u6fb3\u5143\u7b26\u53f7",
-	"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
-	"cedi sign": "\u585e\u5730\u7b26\u53f7",
-	"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
-	"spesmilo sign": "spesmilo\u7b26\u53f7",
-	"tenge sign": "\u575a\u6208\u7b26\u53f7",
-	"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
-	"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
-	"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
-	"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
-	"ruble sign": "\u5362\u5e03\u7b26\u53f7",
-	"yen character": "\u65e5\u5143\u5b57\u6837",
-	"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
-	"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
-	"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
-	"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
-	"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
-	"People": "\u4eba\u7c7b",
-	"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
-	"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
-	"Activity": "\u6d3b\u52a8",
-	"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
-	"Objects": "\u7269\u4ef6",
-	"Flags": "\u65d7\u5e1c",
-	"Characters": "\u5b57\u7b26",
-	"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
-	"{0} characters": "{0} \u4e2a\u5b57\u7b26",
-	"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
-	"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
-	"Update": "\u66f4\u65b0",
-	"Color swatch": "\u989c\u8272\u6837\u672c",
-	"Turquoise": "\u9752\u7eff\u8272",
-	"Green": "\u7eff\u8272",
-	"Blue": "\u84dd\u8272",
-	"Purple": "\u7d2b\u8272",
-	"Navy Blue": "\u6d77\u519b\u84dd",
-	"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
-	"Dark Green": "\u6df1\u7eff\u8272",
-	"Medium Blue": "\u4e2d\u84dd\u8272",
-	"Medium Purple": "\u4e2d\u7d2b\u8272",
-	"Midnight Blue": "\u6df1\u84dd\u8272",
-	"Yellow": "\u9ec4\u8272",
-	"Orange": "\u6a59\u8272",
-	"Red": "\u7ea2\u8272",
-	"Light Gray": "\u6d45\u7070\u8272",
-	"Gray": "\u7070\u8272",
-	"Dark Yellow": "\u6697\u9ec4\u8272",
-	"Dark Orange": "\u6df1\u6a59\u8272",
-	"Dark Red": "\u6df1\u7ea2\u8272",
-	"Medium Gray": "\u4e2d\u7070\u8272",
-	"Dark Gray": "\u6df1\u7070\u8272",
-	"Light Green": "\u6d45\u7eff\u8272",
-	"Light Yellow": "\u6d45\u9ec4\u8272",
-	"Light Red": "\u6d45\u7ea2\u8272",
-	"Light Purple": "\u6d45\u7d2b\u8272",
-	"Light Blue": "\u6d45\u84dd\u8272",
-	"Dark Purple": "\u6df1\u7d2b\u8272",
-	"Dark Blue": "\u6df1\u84dd\u8272",
-	"Black": "\u9ed1\u8272",
-	"White": "\u767d\u8272",
-	"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
-	"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
-	"history": "\u5386\u53f2",
-	"styles": "\u6837\u5f0f",
-	"formatting": "\u683c\u5f0f\u5316",
-	"alignment": "\u5bf9\u9f50",
-	"indentation": "\u7f29\u8fdb",
-	"Font": "\u5b57\u4f53",
-	"Size": "\u5b57\u53f7",
-	"More...": "\u66f4\u591a...",
-	"Select...": "\u9009\u62e9...",
-	"Preferences": "\u9996\u9009\u9879",
-	"Yes": "\u662f",
-	"No": "\u5426",
-	"Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
-	"Version": "\u7248\u672c",
-	"Code view": "\u4ee3\u7801\u89c6\u56fe",
-	"Open popup menu for split buttons": "\u6253\u5f00\u5f39\u51fa\u5f0f\u83dc\u5355\uff0c\u7528\u4e8e\u62c6\u5206\u6309\u94ae",
-	"List Properties": "\u5217\u8868\u5c5e\u6027",
-	"List properties...": "\u6807\u9898\u5b57\u4f53\u5c5e\u6027",
-	"Start list at number": "\u4ee5\u6570\u5b57\u5f00\u59cb\u5217\u8868",
-	"Line height": "\u884c\u9ad8",
-	"comments": "\u5907\u6ce8",
-	"Format Painter": "\u683c\u5f0f\u5237",
-	"Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6",
-	"Capitalization": "\u5927\u5199",
-	"lowercase": "\u5c0f\u5199",
-	"UPPERCASE": "\u5927\u5199",
-	"Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
-	"permanent pen": "\u8bb0\u53f7\u7b14",
-	"Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
-	"Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
-	"case change": "\u6848\u4f8b\u66f4\u6539",
-	"page embed": "\u9875\u9762\u5d4c\u5165",
-	"Advanced sort...": "\u9ad8\u7ea7\u6392\u5e8f...",
-	"Advanced Sort": "\u9ad8\u7ea7\u6392\u5e8f",
-	"Sort table by column ascending": "\u6309\u5217\u5347\u5e8f\u8868",
-	"Sort table by column descending": "\u6309\u5217\u964d\u5e8f\u8868",
-	"Sort": "\u6392\u5e8f",
-	"Order": "\u6392\u5e8f",
-	"Sort by": "\u6392\u5e8f\u65b9\u5f0f",
-	"Ascending": "\u5347\u5e8f",
-	"Descending": "\u964d\u5e8f",
-	"Column {0}": "\u5217{0}",
-	"Row {0}": "\u884c{0}",
-	"Spellcheck...": "\u62fc\u5199\u68c0\u67e5...",
-	"Misspelled word": "\u62fc\u5199\u9519\u8bef\u7684\u5355\u8bcd",
-	"Suggestions": "\u5efa\u8bae",
-	"Change": "\u66f4\u6539",
-	"Finding word suggestions": "\u67e5\u627e\u5355\u8bcd\u5efa\u8bae",
-	"Success": "\u6210\u529f",
-	"Repair": "\u4fee\u590d",
-	"Issue {0} of {1}": "\u5171\u8ba1{1}\u95ee\u9898{0}",
-	"Images must be marked as decorative or have an alternative text description": "\u56fe\u50cf\u5fc5\u987b\u6807\u8bb0\u4e3a\u88c5\u9970\u6027\u6216\u5177\u6709\u66ff\u4ee3\u6587\u672c\u63cf\u8ff0",
-	"Images must have an alternative text description. Decorative images are not allowed.": "\u56fe\u50cf\u5fc5\u987b\u5177\u6709\u66ff\u4ee3\u6587\u672c\u63cf\u8ff0\u3002\u4e0d\u5141\u8bb8\u4f7f\u7528\u88c5\u9970\u56fe\u50cf\u3002",
-	"Or provide alternative text:": "\u6216\u63d0\u4f9b\u5907\u9009\u6587\u672c\uff1a",
-	"Make image decorative:": "\u4f7f\u56fe\u50cf\u88c5\u9970\uff1a",
-	"ID attribute must be unique": "ID \u5c5e\u6027\u5fc5\u987b\u662f\u552f\u4e00\u7684",
-	"Make ID unique": "\u4f7f ID \u72ec\u4e00\u65e0\u4e8c",
-	"Keep this ID and remove all others": "\u4fdd\u7559\u6b64 ID \u5e76\u5220\u9664\u6240\u6709\u5176\u4ed6",
-	"Remove this ID": "\u5220\u9664\u6b64 ID",
-	"Remove all IDs": "\u6e05\u9664\u5168\u90e8IDs",
-	"Checklist": "\u6e05\u5355",
-	"Anchor": "\u951a\u70b9",
-	"Special character": "\u7279\u6b8a\u7b26\u53f7",
-	"Code sample": "\u4ee3\u7801\u793a\u4f8b",
-	"Color": "\u989c\u8272",
-	"Document properties": "\u6587\u6863\u5c5e\u6027",
-	"Image description": "\u56fe\u7247\u63cf\u8ff0",
-	"Image": "\u56fe\u7247",
-	"Insert link": "\u63d2\u5165\u94fe\u63a5",
-	"Target": "\u6253\u5f00\u65b9\u5f0f",
-	"Link": "\u94fe\u63a5",
-	"Poster": "\u5c01\u9762",
-	"Media": "\u5a92\u4f53",
-	"Print": "\u6253\u5370",
-	"Prev": "\u4e0a\u4e00\u4e2a",
-	"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
-	"Whole words": "\u5168\u5b57\u5339\u914d",
-	"Insert template": "\u63d2\u5165\u6a21\u677f"
-});

+ 0 - 66
public/tinymce/skins/content/dark/content.css

@@ -1,66 +0,0 @@
-body {
-  background-color: #222f3e;
-  color: #fff;
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-  line-height: 1.4;
-  margin: 1rem;
-}
-a {
-  color: #4099ff;
-}
-table {
-  border-collapse: collapse;
-}
-/* Apply a default padding if legacy cellpadding attribute is missing */
-table:not([cellpadding]) th,
-table:not([cellpadding]) td {
-  padding: 0.4rem;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-width"]) th,
-table[border]:not([border="0"]):not([style*="border-width"]) td {
-  border-width: 1px;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-style"]) th,
-table[border]:not([border="0"]):not([style*="border-style"]) td {
-  border-style: solid;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-color"]) th,
-table[border]:not([border="0"]):not([style*="border-color"]) td {
-  border-color: #6d737b;
-}
-figure {
-  display: table;
-  margin: 1rem auto;
-}
-figure figcaption {
-  color: #8a8f97;
-  display: block;
-  margin-top: 0.25rem;
-  text-align: center;
-}
-hr {
-  border-color: #6d737b;
-  border-style: solid;
-  border-width: 1px 0 0 0;
-}
-code {
-  background-color: #6d737b;
-  border-radius: 3px;
-  padding: 0.1rem 0.2rem;
-}
-.mce-content-body:not([dir=rtl]) blockquote {
-  border-left: 2px solid #6d737b;
-  margin-left: 1.5rem;
-  padding-left: 1rem;
-}
-.mce-content-body[dir=rtl] blockquote {
-  border-right: 2px solid #6d737b;
-  margin-right: 1.5rem;
-  padding-right: 1rem;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/content/dark/content.min.css


+ 0 - 61
public/tinymce/skins/content/default/content.css

@@ -1,61 +0,0 @@
-body {
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-  line-height: 1.4;
-  margin: 1rem;
-}
-table {
-  border-collapse: collapse;
-}
-/* Apply a default padding if legacy cellpadding attribute is missing */
-table:not([cellpadding]) th,
-table:not([cellpadding]) td {
-  padding: 0.4rem;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-width"]) th,
-table[border]:not([border="0"]):not([style*="border-width"]) td {
-  border-width: 1px;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-style"]) th,
-table[border]:not([border="0"]):not([style*="border-style"]) td {
-  border-style: solid;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-color"]) th,
-table[border]:not([border="0"]):not([style*="border-color"]) td {
-  border-color: #ccc;
-}
-figure {
-  display: table;
-  margin: 1rem auto;
-}
-figure figcaption {
-  color: #999;
-  display: block;
-  margin-top: 0.25rem;
-  text-align: center;
-}
-hr {
-  border-color: #ccc;
-  border-style: solid;
-  border-width: 1px 0 0 0;
-}
-code {
-  background-color: #e8e8e8;
-  border-radius: 3px;
-  padding: 0.1rem 0.2rem;
-}
-.mce-content-body:not([dir=rtl]) blockquote {
-  border-left: 2px solid #ccc;
-  margin-left: 1.5rem;
-  padding-left: 1rem;
-}
-.mce-content-body[dir=rtl] blockquote {
-  border-right: 2px solid #ccc;
-  margin-right: 1.5rem;
-  padding-right: 1rem;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/content/default/content.min.css


+ 0 - 66
public/tinymce/skins/content/document/content.css

@@ -1,66 +0,0 @@
-@media screen {
-  html {
-    background: #f4f4f4;
-    min-height: 100%;
-  }
-}
-body {
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-}
-@media screen {
-  body {
-    background-color: #fff;
-    box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
-    box-sizing: border-box;
-    margin: 1rem auto 0;
-    max-width: 820px;
-    min-height: calc(100vh - 1rem);
-    padding: 4rem 6rem 6rem 6rem;
-  }
-}
-table {
-  border-collapse: collapse;
-}
-/* Apply a default padding if legacy cellpadding attribute is missing */
-table:not([cellpadding]) th,
-table:not([cellpadding]) td {
-  padding: 0.4rem;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-width"]) th,
-table[border]:not([border="0"]):not([style*="border-width"]) td {
-  border-width: 1px;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-style"]) th,
-table[border]:not([border="0"]):not([style*="border-style"]) td {
-  border-style: solid;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-color"]) th,
-table[border]:not([border="0"]):not([style*="border-color"]) td {
-  border-color: #ccc;
-}
-figure figcaption {
-  color: #999;
-  margin-top: 0.25rem;
-  text-align: center;
-}
-hr {
-  border-color: #ccc;
-  border-style: solid;
-  border-width: 1px 0 0 0;
-}
-.mce-content-body:not([dir=rtl]) blockquote {
-  border-left: 2px solid #ccc;
-  margin-left: 1.5rem;
-  padding-left: 1rem;
-}
-.mce-content-body[dir=rtl] blockquote {
-  border-right: 2px solid #ccc;
-  margin-right: 1.5rem;
-  padding-right: 1rem;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/content/document/content.min.css


+ 0 - 66
public/tinymce/skins/content/tinymce-5-dark/content.css

@@ -1,66 +0,0 @@
-body {
-  background-color: #2f3742;
-  color: #dfe0e4;
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-  line-height: 1.4;
-  margin: 1rem;
-}
-a {
-  color: #4099ff;
-}
-table {
-  border-collapse: collapse;
-}
-/* Apply a default padding if legacy cellpadding attribute is missing */
-table:not([cellpadding]) th,
-table:not([cellpadding]) td {
-  padding: 0.4rem;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-width"]) th,
-table[border]:not([border="0"]):not([style*="border-width"]) td {
-  border-width: 1px;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-style"]) th,
-table[border]:not([border="0"]):not([style*="border-style"]) td {
-  border-style: solid;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-color"]) th,
-table[border]:not([border="0"]):not([style*="border-color"]) td {
-  border-color: #6d737b;
-}
-figure {
-  display: table;
-  margin: 1rem auto;
-}
-figure figcaption {
-  color: #8a8f97;
-  display: block;
-  margin-top: 0.25rem;
-  text-align: center;
-}
-hr {
-  border-color: #6d737b;
-  border-style: solid;
-  border-width: 1px 0 0 0;
-}
-code {
-  background-color: #6d737b;
-  border-radius: 3px;
-  padding: 0.1rem 0.2rem;
-}
-.mce-content-body:not([dir=rtl]) blockquote {
-  border-left: 2px solid #6d737b;
-  margin-left: 1.5rem;
-  padding-left: 1rem;
-}
-.mce-content-body[dir=rtl] blockquote {
-  border-right: 2px solid #6d737b;
-  margin-right: 1.5rem;
-  padding-right: 1rem;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/content/tinymce-5-dark/content.min.css


+ 0 - 61
public/tinymce/skins/content/tinymce-5/content.css

@@ -1,61 +0,0 @@
-body {
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-  line-height: 1.4;
-  margin: 1rem;
-}
-table {
-  border-collapse: collapse;
-}
-/* Apply a default padding if legacy cellpadding attribute is missing */
-table:not([cellpadding]) th,
-table:not([cellpadding]) td {
-  padding: 0.4rem;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-width"]) th,
-table[border]:not([border="0"]):not([style*="border-width"]) td {
-  border-width: 1px;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-style"]) th,
-table[border]:not([border="0"]):not([style*="border-style"]) td {
-  border-style: solid;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-color"]) th,
-table[border]:not([border="0"]):not([style*="border-color"]) td {
-  border-color: #ccc;
-}
-figure {
-  display: table;
-  margin: 1rem auto;
-}
-figure figcaption {
-  color: #999;
-  display: block;
-  margin-top: 0.25rem;
-  text-align: center;
-}
-hr {
-  border-color: #ccc;
-  border-style: solid;
-  border-width: 1px 0 0 0;
-}
-code {
-  background-color: #e8e8e8;
-  border-radius: 3px;
-  padding: 0.1rem 0.2rem;
-}
-.mce-content-body:not([dir=rtl]) blockquote {
-  border-left: 2px solid #ccc;
-  margin-left: 1.5rem;
-  padding-left: 1rem;
-}
-.mce-content-body[dir=rtl] blockquote {
-  border-right: 2px solid #ccc;
-  margin-right: 1.5rem;
-  padding-right: 1rem;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/content/tinymce-5/content.min.css


+ 0 - 62
public/tinymce/skins/content/writer/content.css

@@ -1,62 +0,0 @@
-body {
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-  line-height: 1.4;
-  margin: 1rem auto;
-  max-width: 900px;
-}
-table {
-  border-collapse: collapse;
-}
-/* Apply a default padding if legacy cellpadding attribute is missing */
-table:not([cellpadding]) th,
-table:not([cellpadding]) td {
-  padding: 0.4rem;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-width"]) th,
-table[border]:not([border="0"]):not([style*="border-width"]) td {
-  border-width: 1px;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-style"]) th,
-table[border]:not([border="0"]):not([style*="border-style"]) td {
-  border-style: solid;
-}
-/* Set default table styles if a table has a positive border attribute
-   and no inline css */
-table[border]:not([border="0"]):not([style*="border-color"]) th,
-table[border]:not([border="0"]):not([style*="border-color"]) td {
-  border-color: #ccc;
-}
-figure {
-  display: table;
-  margin: 1rem auto;
-}
-figure figcaption {
-  color: #999;
-  display: block;
-  margin-top: 0.25rem;
-  text-align: center;
-}
-hr {
-  border-color: #ccc;
-  border-style: solid;
-  border-width: 1px 0 0 0;
-}
-code {
-  background-color: #e8e8e8;
-  border-radius: 3px;
-  padding: 0.1rem 0.2rem;
-}
-.mce-content-body:not([dir=rtl]) blockquote {
-  border-left: 2px solid #ccc;
-  margin-left: 1.5rem;
-  padding-left: 1rem;
-}
-.mce-content-body[dir=rtl] blockquote {
-  border-right: 2px solid #ccc;
-  margin-right: 1.5rem;
-  padding-right: 1rem;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/content/writer/content.min.css


File diff suppressed because it is too large
+ 0 - 766
public/tinymce/skins/ui/oxide-dark/content.css


File diff suppressed because it is too large
+ 0 - 779
public/tinymce/skins/ui/oxide-dark/content.inline.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide-dark/content.inline.min.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide-dark/content.min.css


File diff suppressed because it is too large
+ 0 - 3762
public/tinymce/skins/ui/oxide-dark/skin.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide-dark/skin.min.css


+ 0 - 30
public/tinymce/skins/ui/oxide-dark/skin.shadowdom.css

@@ -1,30 +0,0 @@
-body.tox-dialog__disable-scroll {
-  overflow: hidden;
-}
-.tox-fullscreen {
-  border: 0;
-  height: 100%;
-  margin: 0;
-  overflow: hidden;
-  overscroll-behavior: none;
-  padding: 0;
-  touch-action: pinch-zoom;
-  width: 100%;
-}
-.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
-  display: none;
-}
-.tox.tox-tinymce.tox-fullscreen,
-.tox-shadowhost.tox-fullscreen {
-  left: 0;
-  position: fixed;
-  top: 0;
-  z-index: 1200;
-}
-.tox.tox-tinymce.tox-fullscreen {
-  background-color: transparent;
-}
-.tox-fullscreen .tox.tox-tinymce-aux,
-.tox-fullscreen ~ .tox.tox-tinymce-aux {
-  z-index: 1201;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css


File diff suppressed because it is too large
+ 0 - 785
public/tinymce/skins/ui/oxide/content.css


File diff suppressed because it is too large
+ 0 - 779
public/tinymce/skins/ui/oxide/content.inline.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide/content.inline.min.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide/content.min.css


File diff suppressed because it is too large
+ 0 - 3759
public/tinymce/skins/ui/oxide/skin.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide/skin.min.css


+ 0 - 30
public/tinymce/skins/ui/oxide/skin.shadowdom.css

@@ -1,30 +0,0 @@
-body.tox-dialog__disable-scroll {
-  overflow: hidden;
-}
-.tox-fullscreen {
-  border: 0;
-  height: 100%;
-  margin: 0;
-  overflow: hidden;
-  overscroll-behavior: none;
-  padding: 0;
-  touch-action: pinch-zoom;
-  width: 100%;
-}
-.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
-  display: none;
-}
-.tox.tox-tinymce.tox-fullscreen,
-.tox-shadowhost.tox-fullscreen {
-  left: 0;
-  position: fixed;
-  top: 0;
-  z-index: 1200;
-}
-.tox.tox-tinymce.tox-fullscreen {
-  background-color: transparent;
-}
-.tox-fullscreen .tox.tox-tinymce-aux,
-.tox-fullscreen ~ .tox.tox-tinymce-aux {
-  z-index: 1201;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/oxide/skin.shadowdom.min.css


File diff suppressed because it is too large
+ 0 - 766
public/tinymce/skins/ui/tinymce-5-dark/content.css


File diff suppressed because it is too large
+ 0 - 779
public/tinymce/skins/ui/tinymce-5-dark/content.inline.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5-dark/content.min.css


File diff suppressed because it is too large
+ 0 - 3853
public/tinymce/skins/ui/tinymce-5-dark/skin.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5-dark/skin.min.css


+ 0 - 30
public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.css

@@ -1,30 +0,0 @@
-body.tox-dialog__disable-scroll {
-  overflow: hidden;
-}
-.tox-fullscreen {
-  border: 0;
-  height: 100%;
-  margin: 0;
-  overflow: hidden;
-  overscroll-behavior: none;
-  padding: 0;
-  touch-action: pinch-zoom;
-  width: 100%;
-}
-.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
-  display: none;
-}
-.tox.tox-tinymce.tox-fullscreen,
-.tox-shadowhost.tox-fullscreen {
-  left: 0;
-  position: fixed;
-  top: 0;
-  z-index: 1200;
-}
-.tox.tox-tinymce.tox-fullscreen {
-  background-color: transparent;
-}
-.tox-fullscreen .tox.tox-tinymce-aux,
-.tox-fullscreen ~ .tox.tox-tinymce-aux {
-  z-index: 1201;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.min.css


File diff suppressed because it is too large
+ 0 - 785
public/tinymce/skins/ui/tinymce-5/content.css


File diff suppressed because it is too large
+ 0 - 779
public/tinymce/skins/ui/tinymce-5/content.inline.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5/content.inline.min.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5/content.min.css


File diff suppressed because it is too large
+ 0 - 3853
public/tinymce/skins/ui/tinymce-5/skin.css


File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5/skin.min.css


+ 0 - 30
public/tinymce/skins/ui/tinymce-5/skin.shadowdom.css

@@ -1,30 +0,0 @@
-body.tox-dialog__disable-scroll {
-  overflow: hidden;
-}
-.tox-fullscreen {
-  border: 0;
-  height: 100%;
-  margin: 0;
-  overflow: hidden;
-  overscroll-behavior: none;
-  padding: 0;
-  touch-action: pinch-zoom;
-  width: 100%;
-}
-.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
-  display: none;
-}
-.tox.tox-tinymce.tox-fullscreen,
-.tox-shadowhost.tox-fullscreen {
-  left: 0;
-  position: fixed;
-  top: 0;
-  z-index: 1200;
-}
-.tox.tox-tinymce.tox-fullscreen {
-  background-color: transparent;
-}
-.tox-fullscreen .tox.tox-tinymce-aux,
-.tox-fullscreen ~ .tox.tox-tinymce-aux {
-  z-index: 1201;
-}

File diff suppressed because it is too large
+ 0 - 1
public/tinymce/skins/ui/tinymce-5/skin.shadowdom.min.css


BIN
src/assets/device/operIcon/maintenance.png


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

@@ -1739,7 +1739,7 @@
     "taxRate": "Tax Rate",
     "ratePlaceholder": "Enter tax rate percentage (100.00-200.00)",
     "rateRequired": "Tax rate is required",
-    "rateInvalid": "Enter valid value with up to 2 decimal places",
+    "rateInvalid": "Enter valid value",
     "update": "Apply Now"
   },
   "jam": {
@@ -1747,6 +1747,86 @@
     "reverse": "Reverse Extraction",
     "stop": "Emergency Stop"
   },
+  "maintenance": {
+    "title": "Maintenance Logs",
+    "type": {
+      "cleaning": "Cleaning",
+      "refill": "Refill",
+      "repair": "Repair"
+    },
+    "filter": {
+      "title": "Filters",
+      "staffId": "Technician ID",
+      "staffIdPlaceholder": "Enter technician ID",
+      "status": "Maintenance Status",
+      "dateRange": "Date Range",
+      "startDatePlaceholder": "Select start date",
+      "endDatePlaceholder": "Select end date",
+      "reset": "Reset",
+      "apply": "Apply"
+    },
+    "status": {
+      "all": "All Statuses",
+      "resolved": "Resolved",
+      "unresolved": "Unresolved",
+      "unknown": "Unknown Status"
+    },
+    "detail": {
+      "title": "Maintenance Details",
+      "staff": "Technician",
+      "type": "Maintenance Type",
+      "startTime": "Start Time",
+      "endTime": "End Time",
+      "recordTime": "Log Time",
+      "consumables": "Consumables",
+      "noConsumables": "No consumables",
+      "faultDescription": "Fault Description",
+      "noFaultDescription": "No description",
+      "solution": "Resolution",
+      "noSolution": "No solution",
+      "images": "On-site Photos (Saved for 30 days)"
+    },
+    "form": {
+      "deviceInfo": "Device Information",
+      "deviceId": "Device ID",
+      "staffInfo": "Technician",
+      "staffId": "ID",
+      "staffIdPlaceholder": "Enter technician ID",
+      "name": "Name",
+      "namePlaceholder": "Enter technician name",
+      "detail": "Maintenance Details",
+      "type": "Maintenance Type",
+      "typePlaceholder": "Select Maintenance type",
+      "date": "Maintenance Date",
+      "datePlaceholder": "Select date",
+      "time": "Maintenance Time",
+      "startTime": "Start Time",
+      "startTimePlaceholder": "Start time",
+      "endTime": "End Time",
+      "endTimePlaceholder": "End time",
+      "to": "to",
+      "consumables": "Consumables",
+      "consumablesPlaceholder": "List consumables used",
+      "description": "Fault/Process Description",
+      "descriptionPlaceholder": "Enter detailed description",
+      "images": "Issue/Resolution Photos",
+      "status": "Issue Status",
+      "submit": "Submit",
+      "confirm": "Confirm",
+      "selectTime": "Select Time",
+      "success": "Submission Successful"
+    },
+    "validation": {
+      "deviceIdRequired": "Device ID required",
+      "staffIdRequired": "Technician ID required",
+      "nameRequired": "Name required",
+      "typeRequired": "Select at least one Maintenance type",
+      "startTimeRequired": "Start time required",
+      "endTimeRequired": "End time required",
+      "timeRangeInvalid": "Start time cannot be later than end time",
+      "staffNotFound": "Technician ID not found"
+    }
+  },
   "remote": {
     "C1": "Restart Machine",
     "C2": "Sleep Toggle",
@@ -1777,6 +1857,7 @@
     "C27": "Defrost Mode (Ice Cream)",
     "C28": "Edit Payment Method",
     "C29": "Tax Administration",
-    "C30": "Jam Extraction"
+    "C30": "Jam Extraction",
+    "C31": "Maintenance Details"
   }
 }

+ 83 - 2
src/assets/language/es.json

@@ -1738,7 +1738,7 @@
         "taxRate": "Tipo Impositivo",
         "ratePlaceholder": "Introduzca porcentaje (100,00-200,00)",
         "rateRequired": "Campo obligatorio",
-        "rateInvalid": "Valor entre 100-200 con 2 decimales",
+        "rateInvalid": "Valor entre 100-200",
         "update": "Actualizar ahora"
     },
     "jam": {
@@ -1746,6 +1746,86 @@
         "reverse": "Extracción Inversa",
         "stop": "Paro de Emergencia"
     },
+    "maintenance": {
+        "title": "Registro de Mantenimiento",
+        "type": {
+            "cleaning": "Limpieza",
+            "refill": "Recarga",
+            "repair": "Reparación"
+        },
+        "filter": {
+            "title": "Filtros",
+            "staffId": "ID de técnico",
+            "staffIdPlaceholder": "Ingrese ID de técnico",
+            "status": "Estado de mantenimiento",
+            "dateRange": "Rango de fechas",
+            "startDatePlaceholder": "Seleccionar fecha de inicio",
+            "endDatePlaceholder": "Seleccionar fecha final",
+            "reset": "Restablecer",
+            "apply": "Aplicar"
+        },
+        "status": {
+            "all": "Todos los estados",
+            "resolved": "Resuelto",
+            "unresolved": "No resuelto",
+            "unknown": "Estado desconocido"
+        },
+        "detail": {
+            "title": "Detalles de Mantenimiento",
+            "staff": "Técnico",
+            "type": "Tipo de mantenimiento",
+            "startTime": "Hora de inicio",
+            "endTime": "Hora de fin",
+            "recordTime": "Hora de registro",
+            "consumables": "Consumibles",
+            "noConsumables": "Sin consumibles",
+            "faultDescription": "Descripción de falla",
+            "noFaultDescription": "Sin descripción",
+            "solution": "Solución",
+            "noSolution": "Sin solución",
+            "images": "Fotos en sitio (guardadas 30 días)"
+        },
+        "form": {
+            "deviceInfo": "Información del dispositivo",
+            "deviceId": "ID de dispositivo",
+            "staffInfo": "Técnico",
+            "staffId": "ID técnico",
+            "staffIdPlaceholder": "Ingrese ID de técnico",
+            "name": "Nombre",
+            "namePlaceholder": "Ingrese nombre del técnico",
+            "detail": "Detalles de mantenimiento",
+            "type": "Tipo de mantenimiento",
+            "typePlaceholder": "Seleccionar tipo",
+            "date": "Fecha de mantenimiento",
+            "datePlaceholder": "Seleccionar fecha",
+            "time": "Tiempo de mantenimiento",
+            "startTime": "Hora de inicio",
+            "startTimePlaceholder": "Hora de inicio",
+            "endTime": "Hora de fin",
+            "endTimePlaceholder": "Hora de fin",
+            "to": "a",
+            "consumables": "Consumibles",
+            "consumablesPlaceholder": "Listar consumibles usados",
+            "description": "Descripción falla/solución",
+            "descriptionPlaceholder": "Descripción detallada",
+            "images": "Fotos problema/solución",
+            "status": "Estado del problema",
+            "submit": "Enviar",
+            "confirm": "Confirmar",
+            "selectTime": "Seleccionar hora",
+            "success": "Envío exitoso"
+        },
+        "validation": {
+            "deviceIdRequired": "ID de dispositivo requerido",
+            "staffIdRequired": "ID técnico requerido",
+            "nameRequired": "Nombre requerido",
+            "typeRequired": "Seleccione al menos un tipo",
+            "startTimeRequired": "Hora de inicio requerida",
+            "endTimeRequired": "Hora de fin requerida",
+            "timeRangeInvalid": "La hora de inicio no puede ser posterior a la de fin",
+            "staffNotFound": "ID de técnico no encontrado"
+        }
+    },
     "remote": {
         "C1": "Reiniciar máquina",
         "C2": "Alternar suspensión",
@@ -1776,6 +1856,7 @@
         "C27": "Descongelación (Helado)",
         "C28": "Cambiar pago",
         "C29": "Gestión Tributaria",
-        "C30": "Extracción de Mermelada"
+        "C30": "Extracción de Mermelada",
+        "C31": "Detalles de Mantenimiento"
     }
 }

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

@@ -1769,7 +1769,7 @@
         "taxRate": "Taux d'Imposition",
         "ratePlaceholder": "Saisir le taux en % (100,00 à 200,00)",
         "rateRequired": "Le taux est obligatoire",
-        "rateInvalid": "Valeur entre 100 et 200 (2 décimales max)",
+        "rateInvalid": "Valeur entre 100 et 200",
         "update": "Mettre à jour"
     },
     "jam": {
@@ -1777,6 +1777,86 @@
         "reverse": "Extraction Arrière",
         "stop": "Arrêt d'Urgence"
     },
+    "maintenance": {
+        "title": "Journal de Maintenance",
+        "type": {
+            "cleaning": "Nettoyage",
+            "refill": "Réapprovisionnement",
+            "repair": "Réparation"
+        },
+        "filter": {
+            "title": "Filtres",
+            "staffId": "ID technicien",
+            "staffIdPlaceholder": "Saisir l'ID du technicien",
+            "status": "Statut de maintenance",
+            "dateRange": "Plage de dates",
+            "startDatePlaceholder": "Sélectionner la date de début",
+            "endDatePlaceholder": "Sélectionner la date de fin",
+            "reset": "Réinitialiser",
+            "apply": "Appliquer"
+        },
+        "status": {
+            "all": "Tous les statuts",
+            "resolved": "Résolu",
+            "unresolved": "Non résolu",
+            "unknown": "Statut inconnu"
+        },
+        "detail": {
+            "title": "Détails de la maintenance",
+            "staff": "Technicien",
+            "type": "Type de maintenance",
+            "startTime": "Heure de début",
+            "endTime": "Heure de fin",
+            "recordTime": "Heure d'enregistrement",
+            "consumables": "Consommables",
+            "noConsumables": "Aucun consommable",
+            "faultDescription": "Description du problème",
+            "noFaultDescription": "Aucune description",
+            "solution": "Solution",
+            "noSolution": "Aucune solution",
+            "images": "Photos sur site (conservées 30 jours)"
+        },
+        "form": {
+            "deviceInfo": "Informations appareil",
+            "deviceId": "ID appareil",
+            "staffInfo": "Technicien",
+            "staffId": "ID technicien",
+            "staffIdPlaceholder": "Saisir l'ID technicien",
+            "name": "Nom",
+            "namePlaceholder": "Saisir le nom du technicien",
+            "detail": "Détails de la maintenance",
+            "type": "Type de maintenance",
+            "typePlaceholder": "Sélectionner le type",
+            "date": "Date de maintenance",
+            "datePlaceholder": "Sélectionner une date",
+            "time": "Heure de maintenance",
+            "startTime": "Heure de début",
+            "startTimePlaceholder": "Heure de début",
+            "endTime": "Heure de fin",
+            "endTimePlaceholder": "Heure de fin",
+            "to": "à",
+            "consumables": "Consommables",
+            "consumablesPlaceholder": "Saisir les consommables utilisés",
+            "description": "Description problème/solution",
+            "descriptionPlaceholder": "Description détaillée",
+            "images": "Photos problème/résolution",
+            "status": "Statut du problème",
+            "submit": "Soumettre",
+            "confirm": "Confirmer",
+            "selectTime": "Sélectionner l'heure",
+            "success": "Soumission réussie"
+        },
+        "validation": {
+            "deviceIdRequired": "ID appareil requis",
+            "staffIdRequired": "ID technicien requis",
+            "nameRequired": "Nom requis",
+            "typeRequired": "Sélectionnez un type de maintenance",
+            "startTimeRequired": "Heure de début requise",
+            "endTimeRequired": "Heure de fin requise",
+            "timeRangeInvalid": "L'heure de début ne peut être après la fin",
+            "staffNotFound": "ID technicien introuvable"
+        }
+    },
     "remote": {
         "C1": "Redémarrer machine",
         "C2": "Mode veille",
@@ -1807,6 +1887,7 @@
         "C27": "Décongélation (Glace)",
         "C28": "Modifier paiement",
         "C29": "Gestion Fiscale",
-        "C30": "Extraction de Confiture"
+        "C30": "Extraction de Confiture",
+        "C31": "Détails de la maintenance"
     }
 }

+ 83 - 2
src/assets/language/ja.json

@@ -1730,7 +1730,7 @@
         "taxRate": "税率",
         "ratePlaceholder": "税率パーセンテージを入力(100.00~200.00)",
         "rateRequired": "税率の入力必須",
-        "rateInvalid": "100-200の数値を入力(小数点第2位まで)",
+        "rateInvalid": "100-200の数値を入力",
         "update": "即時適用"
     },
     "jam": {
@@ -1738,6 +1738,86 @@
         "reverse": "逆方向抽出",
         "stop": "緊急停止"
     },
+    "maintenance": {
+        "title": "メンテナンス記録",
+        "type": {
+            "cleaning": "清掃",
+            "refill": "補充",
+            "repair": "修理"
+        },
+        "filter": {
+            "title": "フィルター条件",
+            "staffId": "担当者番号",
+            "staffIdPlaceholder": "担当者番号を入力",
+            "status": "メンテナンス状態",
+            "dateRange": "期間指定",
+            "startDatePlaceholder": "開始日時を選択",
+            "endDatePlaceholder": "終了日時を選択",
+            "reset": "リセット",
+            "apply": "適用"
+        },
+        "status": {
+            "all": "全ての状態",
+            "resolved": "解決済み",
+            "unresolved": "未解決",
+            "unknown": "不明な状態"
+        },
+        "detail": {
+            "title": "メンテナンス詳細",
+            "staff": "担当者",
+            "type": "メンテナンス種別",
+            "startTime": "開始時間",
+            "endTime": "終了時間",
+            "recordTime": "記録時間",
+            "consumables": "消耗品",
+            "noConsumables": "消耗品なし",
+            "faultDescription": "障害内容",
+            "noFaultDescription": "内容なし",
+            "solution": "対応方法",
+            "noSolution": "対応方法なし",
+            "images": "現場写真(30日間保存)"
+        },
+        "form": {
+            "deviceInfo": "装置情報",
+            "deviceId": "装置ID",
+            "staffInfo": "担当者",
+            "staffId": "担当者ID",
+            "staffIdPlaceholder": "担当者IDを入力",
+            "name": "氏名",
+            "namePlaceholder": "担当者氏名を入力",
+            "detail": "メンテナンス詳細",
+            "type": "メンテナンス種別",
+            "typePlaceholder": "種別を選択",
+            "date": "メンテナンス日",
+            "datePlaceholder": "日付を選択",
+            "time": "メンテナンス時間",
+            "startTime": "開始時間",
+            "startTimePlaceholder": "開始時間",
+            "endTime": "終了時間",
+            "endTimePlaceholder": "終了時間",
+            "to": "~",
+            "consumables": "消耗品",
+            "consumablesPlaceholder": "使用した消耗品を入力",
+            "description": "障害/処置内容",
+            "descriptionPlaceholder": "詳細を入力",
+            "images": "異常/処置写真",
+            "status": "問題状態",
+            "submit": "送信",
+            "confirm": "確認",
+            "selectTime": "時間選択",
+            "success": "送信成功"
+        },
+        "validation": {
+            "deviceIdRequired": "装置IDは必須です",
+            "staffIdRequired": "担当者IDは必須です",
+            "nameRequired": "氏名は必須です",
+            "typeRequired": "メンテナンス種別を選択してください",
+            "startTimeRequired": "開始時間を選択してください",
+            "endTimeRequired": "終了時間を選択してください",
+            "timeRangeInvalid": "開始時刻は終了時刻より前に設定してください",
+            "staffNotFound": "担当者IDが見つかりません"
+        }
+    },
     "remote": {
         "C1": "マシン再起動",
         "C2": "スリープ切替",
@@ -1768,6 +1848,7 @@
         "C27": "解凍モード(アイスクリーム)",
         "C28": "支払い方法変更",
         "C29": "税務管理",
-        "C30": "ジャム抽出"
+        "C30": "ジャム抽出",
+        "C31": "メンテナンス記録"
     }
 }

+ 83 - 2
src/assets/language/pt.json

@@ -1738,7 +1738,7 @@
         "taxRate": "Alíquota",
         "ratePlaceholder": "Insira a porcentagem (100,00-200,00)",
         "rateRequired": "Campo obrigatório",
-        "rateInvalid": "Valor entre 100-200 com 2 decimais",
+        "rateInvalid": "Valor entre 100-200",
         "update": "Atualizar agora"
     },
     "jam": {
@@ -1746,6 +1746,86 @@
         "reverse": "Extração Reversa",
         "stop": "Parada de Emergência"
     },
+    "maintenance": {
+        "title": "Registros de Manutenção",
+        "type": {
+            "cleaning": "Limpeza",
+            "refill": "Reabastecimento",
+            "repair": "Reparo"
+        },
+        "filter": {
+            "title": "Filtros",
+            "staffId": "ID do técnico",
+            "staffIdPlaceholder": "Digite o ID do técnico",
+            "status": "Status da manutenção",
+            "dateRange": "Período",
+            "startDatePlaceholder": "Selecione data de início",
+            "endDatePlaceholder": "Selecione data final",
+            "reset": "Redefinir",
+            "apply": "Aplicar"
+        },
+        "status": {
+            "all": "Todos os status",
+            "resolved": "Resolvido",
+            "unresolved": "Não resolvido",
+            "unknown": "Status desconhecido"
+        },
+        "detail": {
+            "title": "Detalhes da Manutenção",
+            "staff": "Técnico",
+            "type": "Tipo de manutenção",
+            "startTime": "Hora de início",
+            "endTime": "Hora de término",
+            "recordTime": "Horário de registro",
+            "consumables": "Consumíveis",
+            "noConsumables": "Sem consumíveis",
+            "faultDescription": "Descrição da falha",
+            "noFaultDescription": "Sem descrição",
+            "solution": "Solução",
+            "noSolution": "Sem solução",
+            "images": "Fotos no local (salvas por 30 dias)"
+        },
+        "form": {
+            "deviceInfo": "Informações do equipamento",
+            "deviceId": "ID do equipamento",
+            "staffInfo": "Técnico",
+            "staffId": "ID do técnico",
+            "staffIdPlaceholder": "Digite o ID do técnico",
+            "name": "Nome",
+            "namePlaceholder": "Digite o nome do técnico",
+            "detail": "Detalhes da manutenção",
+            "type": "Tipo de manutenção",
+            "typePlaceholder": "Selecione o tipo",
+            "date": "Data da manutenção",
+            "datePlaceholder": "Selecionar data",
+            "time": "Horário da manutenção",
+            "startTime": "Hora de início",
+            "startTimePlaceholder": "Hora de início",
+            "endTime": "Hora de término",
+            "endTimePlaceholder": "Hora de término",
+            "to": "até",
+            "consumables": "Consumíveis",
+            "consumablesPlaceholder": "Liste os consumíveis usados",
+            "description": "Descrição da falha/solução",
+            "descriptionPlaceholder": "Descrição detalhada",
+            "images": "Fotos do problema/solução",
+            "status": "Status do problema",
+            "submit": "Enviar",
+            "confirm": "Confirmar",
+            "selectTime": "Selecionar hora",
+            "success": "Envio realizado"
+        },
+        "validation": {
+            "deviceIdRequired": "Informe o ID do equipamento",
+            "staffIdRequired": "Informe o ID do técnico",
+            "nameRequired": "Informe o nome",
+            "typeRequired": "Selecione pelo menos um tipo",
+            "startTimeRequired": "Selecione a hora de início",
+            "endTimeRequired": "Selecione a hora de término",
+            "timeRangeInvalid": "Hora de início não pode ser posterior ao término",
+            "staffNotFound": "ID do técnico não encontrado"
+        }
+    },
     "remote": {
         "C1": "Reiniciar máquina",
         "C2": "Alternar sono",
@@ -1776,6 +1856,7 @@
         "C27": "Descongelamento (Sorvete)",
         "C28": "Alterar pagamento",
         "C29": "Gestão Tributária",
-        "C30": "Extração de Geleia"
+        "C30": "Extração de Geleia",
+        "C31": "Registros de Manutenção"
     }
 }

+ 83 - 2
src/assets/language/ru.json

@@ -1769,7 +1769,7 @@
         "taxRate": "Налоговая ставка",
         "ratePlaceholder": "Введите процентную ставку (100,00-200,00)",
         "rateRequired": "Укажите налоговую ставку",
-        "rateInvalid": "Допустимое значение: 100-200 с 2 знаками после запятой",
+        "rateInvalid": "Допустимое значение: 100-200",
         "update": "Применить сейчас"
     },
     "jam": {
@@ -1777,6 +1777,86 @@
         "reverse": "Обратная прокачка",
         "stop": "Аварийная остановка"
     },
+    "maintenance": {
+        "title": "Журнал обслуживания",
+        "type": {
+            "cleaning": "Чистка",
+            "refill": "Заправка",
+            "repair": "Ремонт"
+        },
+        "filter": {
+            "title": "Фильтры",
+            "staffId": "ID техника",
+            "staffIdPlaceholder": "Введите ID техника",
+            "status": "Статус обслуживания",
+            "dateRange": "Диапазон дат",
+            "startDatePlaceholder": "Выберите дату начала",
+            "endDatePlaceholder": "Выберите дату окончания",
+            "reset": "Сброс",
+            "apply": "Применить"
+        },
+        "status": {
+            "all": "Все статусы",
+            "resolved": "Устранено",
+            "unresolved": "Не устранено",
+            "unknown": "Неизвестно"
+        },
+        "detail": {
+            "title": "Детали обслуживания",
+            "staff": "Техник",
+            "type": "Тип обслуживания",
+            "startTime": "Время начала",
+            "endTime": "Время окончания",
+            "recordTime": "Время записи",
+            "consumables": "Расходники",
+            "noConsumables": "Нет расходников",
+            "faultDescription": "Описание неисправности",
+            "noFaultDescription": "Нет описания",
+            "solution": "Решение",
+            "noSolution": "Решение отсутствует",
+            "images": "Фото на месте (хранятся 30 дней)"
+        },
+        "form": {
+            "deviceInfo": "Информация об устройстве",
+            "deviceId": "ID устройства",
+            "staffInfo": "Техник",
+            "staffId": "ID сотрудника",
+            "staffIdPlaceholder": "Введите ID техника",
+            "name": "ФИО",
+            "namePlaceholder": "Введите ФИО техника",
+            "detail": "Детали обслуживания",
+            "type": "Тип обслуживания",
+            "typePlaceholder": "Выберите тип",
+            "date": "Дата обслуживания",
+            "datePlaceholder": "Выберите дату",
+            "time": "Время обслуживания",
+            "startTime": "Время начала",
+            "startTimePlaceholder": "Время начала",
+            "endTime": "Время окончания",
+            "endTimePlaceholder": "Время окончания",
+            "to": "до",
+            "consumables": "Расходные материалы",
+            "consumablesPlaceholder": "Введите список материалов",
+            "description": "Описание неисправности/решения",
+            "descriptionPlaceholder": "Подробное описание",
+            "images": "Фото проблемы/решения",
+            "status": "Статус проблемы",
+            "submit": "Отправить",
+            "confirm": "Подтвердить",
+            "selectTime": "Выбрать время",
+            "success": "Успешно отправлено"
+        },
+        "validation": {
+            "deviceIdRequired": "Укажите ID устройства",
+            "staffIdRequired": "Укажите ID техника",
+            "nameRequired": "Укажите ФИО",
+            "typeRequired": "Выберите тип обслуживания",
+            "startTimeRequired": "Укажите время начала",
+            "endTimeRequired": "Укажите время окончания",
+            "timeRangeInvalid": "Время начала не может быть позже окончания",
+            "staffNotFound": "ID техника не найден"
+        }
+    },
     "remote": {
         "C1": "Перезагрузка машины",
         "C2": "Переключение сна",
@@ -1807,6 +1887,7 @@
         "C27": "Разморозка (Мороженое)",
         "C28": "Изменить оплату",
         "C29": "Управление налогообложением",
-        "C30": "Экстракция джема"
+        "C30": "Экстракция джема",
+        "C31": "Журнал обслуживания"
     }
 }

+ 83 - 2
src/assets/language/uk.json

@@ -1739,7 +1739,7 @@
         "taxRate": "Податкова ставка",
         "ratePlaceholder": "Введіть відсоток (100,00-200,00)",
         "rateRequired": "Обов'язкове поле",
-        "rateInvalid": "Допустиме значення: 100-200 з 2 десятковими",
+        "rateInvalid": "Допустиме значення: 100-200",
         "update": "Застосувати"
     },
     "jam": {
@@ -1747,6 +1747,86 @@
         "reverse": "Зворотне витягнення",
         "stop": "Аварійне зупинення"
     },
+    "maintenance": {
+        "title": "Журнал обслуговування",
+        "type": {
+            "cleaning": "Чистка",
+            "refill": "Поповнення",
+            "repair": "Ремонт"
+        },
+        "filter": {
+            "title": "Фільтри",
+            "staffId": "ID техніка",
+            "staffIdPlaceholder": "Введіть ID техніка",
+            "status": "Статус обслуговування",
+            "dateRange": "Діапазон дат",
+            "startDatePlaceholder": "Обрати дату початку",
+            "endDatePlaceholder": "Обрати дату закінчення",
+            "reset": "Скинути",
+            "apply": "Застосувати"
+        },
+        "status": {
+            "all": "Всі статуси",
+            "resolved": "Виправлено",
+            "unresolved": "Не виправлено",
+            "unknown": "Невідомий статус"
+        },
+        "detail": {
+            "title": "Деталі обслуговування",
+            "staff": "Технік",
+            "type": "Тип обслуговування",
+            "startTime": "Час початку",
+            "endTime": "Час закінчення",
+            "recordTime": "Час запису",
+            "consumables": "Витратні матеріали",
+            "noConsumables": "Немає витратних матеріалів",
+            "faultDescription": "Опис несправності",
+            "noFaultDescription": "Без опису",
+            "solution": "Рішення",
+            "noSolution": "Рішення відсутнє",
+            "images": "Фото на місці (зберігаються 30 днів)"
+        },
+        "form": {
+            "deviceInfo": "Інформація про пристрій",
+            "deviceId": "ID пристрою",
+            "staffInfo": "Технік",
+            "staffId": "ID техніка",
+            "staffIdPlaceholder": "Введіть ID техніка",
+            "name": "ПІБ",
+            "namePlaceholder": "Введіть ПІБ техніка",
+            "detail": "Деталі обслуговування",
+            "type": "Тип обслуговування",
+            "typePlaceholder": "Обрати тип",
+            "date": "Дата обслуговування",
+            "datePlaceholder": "Обрати дату",
+            "time": "Час обслуговування",
+            "startTime": "Час початку",
+            "startTimePlaceholder": "Час початку",
+            "endTime": "Час закінчення",
+            "endTimePlaceholder": "Час закінчення",
+            "to": "до",
+            "consumables": "Витратні матеріали",
+            "consumablesPlaceholder": "Введіть список витратних матеріалів",
+            "description": "Опис несправності/рішення",
+            "descriptionPlaceholder": "Детальний опис",
+            "images": "Фото проблеми/вирішення",
+            "status": "Статус проблеми",
+            "submit": "Відправити",
+            "confirm": "Підтвердити",
+            "selectTime": "Обрати час",
+            "success": "Успішно відправлено"
+        },
+        "validation": {
+            "deviceIdRequired": "Вкажіть ID пристрою",
+            "staffIdRequired": "Вкажіть ID техніка",
+            "nameRequired": "Вкажіть ПІБ",
+            "typeRequired": "Оберіть тип обслуговування",
+            "startTimeRequired": "Оберіть час початку",
+            "endTimeRequired": "Оберіть час закінчення",
+            "timeRangeInvalid": "Час початку не може бути пізніше закінчення",
+            "staffNotFound": "ID техніка не знайдено"
+        }
+    },
     "remote": {
         "C1": "Перезавантажити пристрій",
         "C2": "Режим сну",
@@ -1777,6 +1857,7 @@
         "C27": "Розморозка (морозиво)",
         "C28": "Змінити оплату",
         "C29": "Управління оподаткуванням",
-        "C30": "Витяг джему"
+        "C30": "Витяг джему",
+        "C31": "Журнал обслуговування"
     }
 }

+ 89 - 4
src/assets/language/zh.json

@@ -111,6 +111,7 @@
     "clickQuery": "点击查询"
   },
   "advertManage": {
+    "basicInfo": "基础信息",
     "advertisingNameLabel": "广告名称",
     "advertisingNamePlaceholder": "请输入广告名称",
     "affiliatedMerchantsLabel": "所属商家",
@@ -119,10 +120,12 @@
     "advertisingOrderPlaceholder": "请输入广告顺序",
     "machineType": "机器类型",
     "machineTypePlace": "请选择所属机器类型",
+    "playSettings": "播放设置",
     "advertisingDurationLabel": "广告时长",
     "advertisingDurationPlaceholder": "请输入广告时长",
     "playbackTimesLabel": "播放次数",
     "playbackTimesPlaceholder": "请输入播放次数",
+    "adSettings": "广告设置",
     "advertisingPosition": "广告位置",
     "screenA": "A屏",
     "screenB": "B屏",
@@ -134,10 +137,11 @@
     "advertisingType": "广告类型",
     "playTimeStatus": "机器状态",
     "acquiesce": "默认",
-    "standby": "待机状态",
-    "work": "工作状态",
+    "standby": "待机",
+    "work": "工作",
     "picture": "图片",
     "video": "视频",
+    "mediaContent": "媒体内容",
     "pictureAddressLabel": "图片地址",
     "pictureAddressPlaceholder": "请输入图片地址",
     "thumbnailAddressLabel": "缩略图地址",
@@ -1739,7 +1743,7 @@
     "taxRate": "税率",
     "ratePlaceholder": "请输入税率百分比",
     "rateRequired": "税率不能为空",
-    "rateInvalid": "请输入100-200之间的有效数值(最多两位小数)",
+    "rateInvalid": "请输入100-200之间的有效数值",
     "update": "立即更新"
   },
   "jam": {
@@ -1747,6 +1751,86 @@
     "reverse": "反抽",
     "stop": "停止"
   },
+  "maintenance": {
+    "title": "维护记录",
+    "type": {
+      "cleaning": "清洁",
+      "refill": "补料",
+      "repair": "维修"
+    },
+    "filter": {
+      "title": "筛选条件",
+      "staffId": "维护人员工号",
+      "staffIdPlaceholder": "请输入维护人员工号",
+      "status": "维护状态",
+      "dateRange": "时间范围",
+      "startDatePlaceholder": "选择开始时间",
+      "endDatePlaceholder": "选择结束时间",
+      "reset": "重置",
+      "apply": "应用"
+    },
+    "status": {
+      "all": "全部状态",
+      "resolved": "已解决",
+      "unresolved": "未解决",
+      "unknown": "未知状态"
+    },
+    "detail": {
+      "title": "维护记录详情",
+      "staff": "维护人员",
+      "type": "维护类型",
+      "startTime": "开始时间",
+      "endTime": "结束时间",
+      "recordTime": "记录时间",
+      "consumables": "消耗物料",
+      "noConsumables": "无消耗物料",
+      "faultDescription": "故障描述",
+      "noFaultDescription": "无故障描述",
+      "solution": "解决方案",
+      "noSolution": "无解决方案",
+      "images": "现场图片(图片仅保存30天)"
+    },
+    "form": {
+      "deviceInfo": "设备信息",
+      "deviceId": "设备编号",
+      "staffInfo": "维护人员",
+      "staffId": "工号",
+      "staffIdPlaceholder": "请输入维护人员工号",
+      "name": "姓名",
+      "namePlaceholder": "请输入维护人员姓名",
+      "detail": "维护详情",
+      "type": "维护类型",
+      "typePlaceholder": "点击选择维护类型",
+      "date": "维护日期",
+      "datePlaceholder": "点击选择日期",
+      "time": "维护时间",
+      "startTime": "开始时间",
+      "startTimePlaceholder": "开始时间",
+      "endTime": "结束时间",
+      "endTimePlaceholder": "结束时间",
+      "to": "至",
+      "consumables": "消耗物料",
+      "consumablesPlaceholder": "请输入消耗物料清单",
+      "description": "故障/处理描述",
+      "descriptionPlaceholder": "请输入详细描述",
+      "images": "异常/处理图片",
+      "status": "问题状态",
+      "submit": "提交",
+      "confirm": "确定",
+      "selectTime": "选择时间",
+      "success": "提交成功"
+    },
+    "validation": {
+      "deviceIdRequired": "请填写设备编号",
+      "staffIdRequired": "请填写工号",
+      "nameRequired": "请填写姓名",
+      "typeRequired": "请至少选择一个维护类型",
+      "startTimeRequired": "请选择开始时间",
+      "endTimeRequired": "请选择结束时间",
+      "timeRangeInvalid": "开始时间不能大于结束时间",
+      "staffNotFound": "工号不存在"
+    }
+  },
   "remote": {
     "C1": "重启设备",
     "C2": "睡眠开关",
@@ -1777,6 +1861,7 @@
     "C27": "解冻模式(冰淇淋)",
     "C28": "修改支付方式",
     "C29": "税收管理",
-    "C30": "果酱抽取"
+    "C30": "果酱抽取",
+    "C31": "维护记录"
   }
 }

+ 0 - 123
src/components/TinymceEditor/index.vue

@@ -1,123 +0,0 @@
-<template>
-  <div>
-    <Editor v-model="content" :init="myTinyInit"></Editor>
-  </div>
-</template>
-
-<script setup name="TinymceEditor">
-import { computed, defineEmits, defineProps, onMounted, reactive, ref, watch } from 'vue'
-import tinymce from 'tinymce/tinymce'
-import Editor from '@tinymce/tinymce-vue'
-import 'tinymce/icons/default/icons'
-import 'tinymce/themes/silver'
-import 'tinymce/models/dom/model'
-//按需引入插件
-import 'tinymce/plugins/image'
-import 'tinymce/plugins/table'
-import 'tinymce/plugins/lists'
-import 'tinymce/plugins/link'
-import 'tinymce/plugins/help'
-import 'tinymce/plugins/wordcount'
-import 'tinymce/plugins/code'
-import 'tinymce/plugins/preview'
-import 'tinymce/plugins/visualblocks'
-import 'tinymce/plugins/visualchars'
-import 'tinymce/plugins/fullscreen'
-
-const props = defineProps({
-  modelValue: {
-    type: String,
-    default: '',
-  },
-  plugins: {
-    type: [String, Array],
-    default: 'lists link image table help wordcount code preview fullscreen fontsize',
-  },
-  toolbar: {
-    type: [String, Array],
-    default:
-      'undo redo formatselect fontsize bold italic forecolor backcolor lineheight alignleft aligncenter alignright alignjustify bullist numlist subscript superscript outdent indent table code preview fullscreen',
-  },
-})
-// image
-const emit = defineEmits(['input'])
-
-const myTinyInit = reactive({
-  width: '100%',
-  height: 500, // 默认高度
-  language_url: '/shenze/tinymce/langs/zh_CN.js', // 配置汉化
-  language: 'zh_CN', // 语言标识
-  branding: false, // 不显示右下角logo
-  auto_update: false, // 不进行自动更新
-  resize: true, // 可以调整大小
-  menubar: false, // 菜单栏
-  skin_url: '/shenze/tinymce/skins/ui/oxide', // 手动引入CSS
-  content_css: '/shenze/tinymce/skins/content/default/content.css', // 手动引入CSS
-  toolbar_mode: 'wrap',
-  // selector: 'textarea',
-  plugins: props.plugins, // 插件
-  toolbar: props.toolbar, // 功能按钮
-  help_accessibility: false,
-  //图片上传方法(注意!!:要使用 promise 对象中的 resolve 返回图片路径,否则会报错)
-  images_upload_handler: (blobInfo) =>
-    new Promise((resolve) => {
-      // 获取到文件流数据
-      console.log(blobInfo.blob())
-      let formData = new FormData()
-      formData.append('file', blobInfo.blob(), blobInfo.filename())
-      // 为了方便测试,返回一张固定的在线图片
-      resolve('https://szx-bucket1.oss-cn-hangzhou.aliyuncs.com/picgo/image-20230512090059968.png')
-      // 请求真实的上传图片接口获取在线地址并通过resolve返回
-      // axios
-      //   .post(`/api/backend/upload`, formData, {
-      //     headers: {
-      //       'Content-Type': 'multipart/form-data',
-      //       Authorization: 'Bearer ' + store.state.user.accessToken,
-      //     },
-      //   })
-      //   .then((res) => {
-      //     if (res.data.code === 1) {
-      //       resolve(`/image_manipulation${res.data.data.filePath}`)
-      //     } else {
-      //       ElNotification.warning(res.data.msg)
-      //     }
-      //   })
-      //   .catch((error) => {
-      //     reject(error)
-      //   })
-    }),
-})
-
-const initContent = computed(() => {
-  return props.modelValue
-})
-
-onMounted(() => {
-  tinymce.init({})
-})
-
-const content = ref()
-watch(
-  initContent,
-  (newVal) => {
-    content.value = newVal
-  },
-  { deep: true, immediate: true }
-)
-
-watch(
-  content,
-  (newVal) => {
-    emit('input', newVal)
-  },
-  { deep: true }
-)
-</script>
-
-<style scoped lang="less">
-
-.tox-editor-header {
-  width: auto;
-}
-
-</style>

+ 21 - 0
src/router/index.js

@@ -28,6 +28,20 @@ const router = createRouter({
       component: () => import("@/views/fife/modifyLoc.vue"),
       meta: { index: 1 },
     },
+    // 录入维护记录
+    {
+      path: "/addMaintenance",
+      name: "addMaintenance",
+      component: () => import("@/views/device/maintenance/add.vue"),
+      meta: { index: 1, noLogin: true },
+    },
+    // 录入成功页面
+    {
+      path: "/addMaintenanceSuccess",
+      name: "addMaintenanceSuccess",
+      component: () => import("@/views/device/maintenance/success.vue"),
+      meta: { index: 1, noLogin: true },
+    },
     // 登录页面
     {
       path: "/login",
@@ -526,6 +540,13 @@ const router = createRouter({
       component: () => import("@/views/device/jam/index"),
       meta: { index: 1 },
     },
+    // 维护记录
+    {
+      path: "/maintenance",
+      name: "maintenance",
+      component: () => import("@/views/device/maintenance/index"),
+      meta: { index: 1 },
+    },
     // 修改机器密码
     {
       path: "/devicePassword",

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

@@ -296,6 +296,11 @@ export function getReturnCoinList(params) {
     return axios.get(`/SZWL-SERVER/returnCoinRecord/list`, { params })
 }
 
+// 获取当天远程退币申请记录
+export function returnCoinList(params) {
+    return axios.post(`/SZWL-SERVER/returnCoinRecord/getList`, params)
+}
+
 // 提交远程退币申请
 export function applyReturnCoin(params) {
     return axios.post(`/SZWL-SERVER/returnCoinRecord/apply`, params)

+ 17 - 0
src/service/maintenance/index.js

@@ -0,0 +1,17 @@
+import axios from "../../utils/axios";
+// import { stringToUrl } from "@/common/js/utils";
+
+// 保存维护记录
+export function save(params) {
+  return axios.post(`/SZWL-SERVER/maintenanceRecord/save`, params);
+}
+
+// 维护记录列表
+export function list(params) {
+  return axios.post(`/SZWL-SERVER/maintenanceRecord/list`, params);
+}
+
+// 上传图片
+export function uploadPic(params) {
+  return axios.post(`/SZWL-SERVER/maintenanceRecord/uploadPic`, params);
+}

+ 343 - 124
src/views/advertManage/adSet.vue

@@ -1,150 +1,226 @@
 <template>
-  <!-- 广告管理 - 新增|修改 -->
-  <div class="advertPage flex-col">
-    <s-header :name="pageTitle" :noback="false"></s-header>
-    <div class="advertBox flex-col">
-      <van-form @submit="updateAdFun">
-        <van-field required v-model="adInfo.name" name="name" :label="`${$t('advertManage.advertisingNameLabel')}:`"
-          :placeholder="$t('advertManage.advertisingNamePlaceholder')" :rules="[
-            {
-              required: true,
-              message: $t('advertManage.advertisingNamePlaceholder'),
-            },
-          ]" />
-        <van-field v-model="adInfo.adminId" name="adminId" :label="`${$t('advertManage.affiliatedMerchantsLabel')}:`"
-          :placeholder="$t('advertManage.affiliatedMerchantsPlaceholder')" />
-        <van-field v-model="adInfo.orders" name="orders" type="number"
-          :label="`${$t('advertManage.advertisingOrderLabel')}:`"
-          :placeholder="$t('advertManage.advertisingOrderPlaceholder')" />
-        <van-field required clearable @click-input="busiInpClk" v-model="adInfo.equipmentType" name="equipmentType"
-          :label="`${$t('advertManage.machineType')}:`" :placeholder="$t('advertManage.machineTypePlace')" :rules="[
-            {
-              required: true,
-              message: $t('advertManage.machineTypePlace'),
-            },
-          ]">
-          <template #right-icon>
-            <div class="l-flex-RC">
-              <van-icon v-if="adInfo.equipmentType" @click="adInfo.equipmentType = ''" class="o-mr-6" name="clear" />
-              <van-icon @click="busiInpClk" name="arrow-down" />
+  <div class="advert-form-page">
+    <!-- 头部导航 -->
+    <s-header :name="pageTitle" :noback="false" />
+
+    <!-- 表单内容 -->
+    <div class="form-container">
+      <van-form @submit="updateAdFun" class="advert-form">
+        <!-- 基本信息部分 -->
+        <div class="form-section">
+          <div class="section-title">{{ $t("advertManage.basicInfo") }}</div>
+          <van-field
+            required
+            v-model="adInfo.name"
+            name="name"
+            :label="`${$t('advertManage.advertisingNameLabel')}`"
+            :placeholder="$t('advertManage.advertisingNamePlaceholder')"
+            :rules="[
+              {
+                required: true,
+                message: $t('advertManage.advertisingNamePlaceholder'),
+              },
+            ]"
+          />
+
+          <van-field
+            v-model="adInfo.adminId"
+            name="adminId"
+            :label="`${$t('advertManage.affiliatedMerchantsLabel')}`"
+            :placeholder="$t('advertManage.affiliatedMerchantsPlaceholder')"
+          />
+
+          <van-field
+            v-model="adInfo.orders"
+            name="orders"
+            type="number"
+            :label="`${$t('advertManage.advertisingOrderLabel')}`"
+            :placeholder="$t('advertManage.advertisingOrderPlaceholder')"
+          />
+
+          <van-field
+            required
+            readonly
+            v-model="adInfo.equipmentType"
+            name="equipmentType"
+            :label="`${$t('advertManage.machineType')}`"
+            :placeholder="$t('advertManage.machineTypePlace')"
+            :rules="[
+              { required: true, message: $t('advertManage.machineTypePlace') },
+            ]"
+            @click="busiInpClk"
+          >
+            <template #right-icon>
+              <van-icon name="arrow-down" />
+            </template>
+          </van-field>
+        </div>
+
+        <!-- 播放设置部分 -->
+        <div class="form-section">
+          <div class="section-title">{{ $t("advertManage.playSettings") }}</div>
+          <van-field
+            v-model="adInfo.duration"
+            name="duration"
+            type="number"
+            :label="`${$t('advertManage.advertisingDurationLabel')}`"
+            :placeholder="$t('advertManage.advertisingDurationPlaceholder')"
+          />
+
+          <van-field
+            v-model="adInfo.plays"
+            name="plays"
+            type="number"
+            :label="`${$t('advertManage.playbackTimesLabel')}`"
+            :placeholder="$t('advertManage.playbackTimesPlaceholder')"
+          />
+
+          <div class="radio-group">
+            <div class="group-label">
+              {{ $t("advertManage.playTimeStatus") }}
             </div>
-          </template>
-        </van-field>
-        <van-field v-model="adInfo.duration" name="duration" type="number"
-          :label="`${$t('advertManage.advertisingDurationLabel')}:`"
-          :placeholder="$t('advertManage.advertisingDurationPlaceholder')" />
-        <van-field v-model="adInfo.plays" name="plays" type="number" :label="`${$t('advertManage.playbackTimesLabel')}:`"
-          :placeholder="$t('advertManage.playbackTimesPlaceholder')" />
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label">
-            <span>{{ $t("advertManage.advertisingPosition") }}:</span>
-          </div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="adInfo.screenType" direction="horizontal">
-              <van-radio name="0" icon-size="18px">{{
-                $t("advertManage.screenA")
-              }}</van-radio>
-              <van-radio name="1" icon-size="18px">{{
-                $t("advertManage.screenB")
-              }}</van-radio>
+            <van-radio-group v-model="adInfo.playTimeStatus">
+              <van-radio name="0">{{ $t("advertManage.acquiesce") }}</van-radio>
+              <van-radio name="1">{{ $t("advertManage.standby") }}</van-radio>
+              <van-radio name="2">{{ $t("advertManage.work") }}</van-radio>
             </van-radio-group>
           </div>
         </div>
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label">
-            <span>{{ $t("advertManage.locationType") }}:</span>
+
+        <!-- 广告设置部分 -->
+        <div class="form-section">
+          <div class="section-title">{{ $t("advertManage.adSettings") }}</div>
+
+          <div class="radio-group">
+            <div class="group-label">
+              {{ $t("advertManage.advertisingPosition") }}
+            </div>
+            <van-radio-group v-model="adInfo.screenType">
+              <van-radio name="0">{{ $t("advertManage.screenA") }}</van-radio>
+              <van-radio name="1">{{ $t("advertManage.screenB") }}</van-radio>
+            </van-radio-group>
           </div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="adInfo.locationType" direction="horizontal">
-              <van-radio name="0" icon-size="18px">{{
+
+          <div class="radio-group">
+            <div class="group-label">
+              {{ $t("advertManage.locationType") }}
+            </div>
+            <van-radio-group v-model="adInfo.locationType">
+              <van-radio name="0">{{
                 $t("advertManage.advertising")
               }}</van-radio>
-              <van-radio name="1" icon-size="18px">{{
+              <van-radio name="1">{{
                 $t("advertManage.externalAdvertising")
               }}</van-radio>
             </van-radio-group>
           </div>
-        </div>
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label">
-            <span>{{ $t("advertManage.defaultDownload") }}:</span>
-          </div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="adInfo.type" direction="horizontal">
-              <van-radio name="0" icon-size="18px">{{
+
+          <div class="radio-group">
+            <div class="group-label">
+              {{ $t("advertManage.defaultDownload") }}
+            </div>
+            <van-radio-group v-model="adInfo.type">
+              <van-radio name="0">{{
                 $t("advertManage.defaultDownload")
               }}</van-radio>
-              <van-radio name="1" icon-size="18px">{{
+              <van-radio name="1">{{
                 $t("advertManage.doNotDownloadByDefault")
               }}</van-radio>
             </van-radio-group>
           </div>
-        </div>
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label">
-            <span>{{ $t("advertManage.advertisingType") }}:</span>
-          </div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="adInfo.adType" direction="horizontal">
-              <van-radio name="0" icon-size="18px">{{
-                $t("advertManage.picture")
-              }}</van-radio>
-              <van-radio name="1" icon-size="18px">{{
-                $t("advertManage.video")
-              }}</van-radio>
+
+          <div class="radio-group">
+            <div class="group-label">
+              {{ $t("advertManage.advertisingType") }}
+            </div>
+            <van-radio-group v-model="adInfo.adType">
+              <van-radio name="0">{{ $t("advertManage.picture") }}</van-radio>
+              <van-radio name="1">{{ $t("advertManage.video") }}</van-radio>
             </van-radio-group>
           </div>
         </div>
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label">
-            <span>{{ $t("advertManage.playTimeStatus") }}:</span>
+
+        <!-- 媒体内容部分 -->
+        <div class="form-section">
+          <div class="section-title">{{ $t("advertManage.mediaContent") }}</div>
+
+          <!-- 图片广告内容 -->
+          <div v-if="adInfo.adType === '0'" class="media-content">
+            <van-field
+              required
+              v-model="adInfo.url"
+              name="url"
+              :label="`${$t('advertManage.pictureAddressLabel')}`"
+              :placeholder="$t('advertManage.pictureAddressPlaceholder')"
+              :rules="[
+                {
+                  required: true,
+                  message: $t('advertManage.pictureAddressPlaceholder'),
+                },
+              ]"
+            />
+
+            <div class="preview-area" v-if="adInfo.url">
+              <div class="preview-label">预览</div>
+              <img :src="adInfo.url" class="image-preview" alt="广告图片预览" />
+            </div>
           </div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="adInfo.playTimeStatus" direction="horizontal">
-              <van-radio name="0" icon-size="18px">{{
-                $t("advertManage.acquiesce")
-              }}</van-radio>
-              <van-radio name="1" icon-size="18px">{{
-                $t("advertManage.standby")
-              }}</van-radio>
-              <van-radio name="2" icon-size="18px">{{
-                $t("advertManage.work")
-              }}</van-radio>
-            </van-radio-group>
+
+          <!-- 视频广告内容 -->
+          <div v-if="adInfo.adType === '1'" class="media-content">
+            <van-field
+              v-model="adInfo.mediaPreview"
+              name="mediaPreview"
+              :label="`${$t('advertManage.thumbnailAddressLabel')}`"
+              :placeholder="$t('advertManage.thumbnailAddressPlaceholder')"
+            />
+
+            <van-field
+              required
+              v-model="adInfo.url"
+              name="url"
+              :label="`${$t('advertManage.videoLinkLabel')}`"
+              :placeholder="$t('advertManage.videoLinkPlaceholder')"
+              :rules="[
+                {
+                  required: true,
+                  message: $t('advertManage.videoLinkPlaceholder'),
+                },
+              ]"
+            />
+
+            <div class="preview-area" v-if="adInfo.url">
+              <div class="preview-label">预览</div>
+              <video :src="adInfo.url" class="video-preview" controls></video>
+            </div>
           </div>
         </div>
-        <div v-if="adInfo.adType === '0'">
-          <van-field required v-model="adInfo.url" name="url" :label="`${$t('advertManage.pictureAddressLabel')}:`"
-            :placeholder="$t('advertManage.pictureAddressPlaceholder')" :rules="[
-              {
-                required: true,
-                message: $t('advertManage.pictureAddressPlaceholder'),
-              },
-            ]" />
-        </div>
-        <div v-if="adInfo.adType === '1'">
-          <van-field v-model="adInfo.mediaPreview" name="mediaPreview"
-            :label="`${$t('advertManage.thumbnailAddressLabel')}:`"
-            :placeholder="$t('advertManage.thumbnailAddressPlaceholder')" />
-          <van-field required v-model="adInfo.url" name="url" :label="`${$t('advertManage.videoLinkLabel')}:`"
-            :placeholder="$t('advertManage.videoLinkPlaceholder')" :rules="[
-              {
-                required: true,
-                message: $t('advertManage.videoLinkPlaceholder'),
-              },
-            ]" />
-        </div>
-        <van-row justify="space-around" style="padding: 1em">
-          <van-button span="5" round type="primary" style="height: 2em; padding: 0 2em" native-type="submit">
+
+        <!-- 提交按钮 -->
+        <div class="submit-section">
+          <van-button
+            round
+            block
+            type="primary"
+            native-type="submit"
+            class="submit-button"
+          >
             {{ $t("advertManage.submit") }}
           </van-button>
-        </van-row>
+        </div>
       </van-form>
     </div>
-    <!-- 机器类型选择框 -->
-    <van-popup v-model:show="busiPopShow" position="bottom">
-      <van-picker v-model="busiDefaIdx" :title="$t('advertManage.machineTypePlace')" :columns="busiPopList"
-        :columns-field-names="busiPopFieldName" @confirm="busiPopConfirm" @cancel="busiPopShow = false" />
+
+    <!-- 机器类型选择弹窗 -->
+    <van-popup v-model:show="busiPopShow" position="bottom" round>
+      <van-picker
+        v-model="busiDefaIdx"
+        :title="$t('advertManage.machineTypePlace')"
+        :columns="busiPopList"
+        :columns-field-names="busiPopFieldName"
+        @confirm="busiPopConfirm"
+        @cancel="busiPopShow = false"
+      />
     </van-popup>
   </div>
 </template>
@@ -154,7 +230,7 @@ import { onMounted, ref, reactive } from "vue";
 import sHeader from "../../components/SimpleHeader";
 import { addAdInfo, updateAdInfo } from "../../service/advertManage";
 import { useRouter } from "vue-router";
-import { showFailToast, showSuccessToast } from 'vant';
+import { showFailToast, showSuccessToast } from "vant";
 import { useI18n } from "vue-i18n";
 import { styleUrl } from "../../common/js/utils";
 
@@ -289,7 +365,7 @@ export default {
     const pageTitle = ref("");
     onMounted(async () => {
       // 加载样式
-      styleUrl('advertManage');
+      styleUrl("advertManage");
       let adverInfo = localStorage.getItem("adverInfo");
       if (adverInfo) {
         pageTitle.value = t("advertManage.modifyAdvertisement");
@@ -339,7 +415,9 @@ export default {
         const params = Object.assign({}, adInfo.value);
         const { data } = await updateAdInfo(params);
         if (data.code === "00000") {
-          showSuccessToast(t("advertManage.successfullyModifiedTheAdvertisement"));
+          showSuccessToast(
+            t("advertManage.successfullyModifiedTheAdvertisement")
+          );
           router.push({ path: "advertManage" });
         } else {
           showFailToast(data.message);
@@ -372,6 +450,147 @@ export default {
 </script>
 
 <style lang="less" scoped>
-@import "../../common/style/common.less";
+@primary-color: #4d6add;
+
+.advert-form-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f7fa;
+}
+
+.form-container {
+  flex: 1;
+  overflow-y: auto;
+  padding: 16px;
+}
+
+.advert-form {
+  background-color: white;
+  border-radius: 12px;
+  padding: 16px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+}
+
+.form-section {
+  margin-bottom: 24px;
+  padding-bottom: 16px;
+  border-bottom: 1px solid #f0f0f0;
+
+  &:last-child {
+    margin-bottom: 0;
+    padding-bottom: 0;
+    border-bottom: none;
+  }
+}
+
+.section-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 16px;
+  padding-left: 8px;
+  position: relative;
+
+  &::before {
+    content: "";
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 3px;
+    height: 16px;
+    background-color: @primary-color;
+    border-radius: 2px;
+  }
+}
+
+.radio-group {
+  margin: 10px;
+
+  .group-label {
+    font-size: 13px;
+    color: #404d74;
+    margin-bottom: 8px;
+  }
+
+  .van-radio-group {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+  }
+
+  .van-radio {
+    background-color: #f5f7fa;
+    border-radius: 16px;
+    padding: 6px 16px;
+    margin-right: 8px;
+
+    :deep(.van-radio__label) {
+      font-size: 13px;
+    }
+  }
+}
+
+.media-content {
+  .preview-area {
+    margin-top: 16px;
+
+    .preview-label {
+      font-size: 14px;
+      color: #666;
+      margin-bottom: 8px;
+    }
+
+    .image-preview {
+      width: 100%;
+      max-height: 200px;
+      object-fit: contain;
+      border-radius: 8px;
+      border: 1px solid #eee;
+    }
+
+    .video-preview {
+      width: 100%;
+      height: 180px;
+      border-radius: 8px;
+      background-color: #000;
+    }
+  }
+}
+
+.submit-section {
+  margin-top: 24px;
+
+  .submit-button {
+    height: 44px;
+    font-size: 16px;
+    font-weight: 500;
+  }
+}
+
+@media (max-width: 480px) {
+  .form-container {
+    padding: 12px;
+  }
+
+  .advert-form {
+    padding: 12px;
+  }
+
+  .section-title {
+    font-size: 15px;
+  }
+
+  .radio-group {
+    .van-radio-group {
+      gap: 5px;
+    }
+
+    .van-radio {
+      padding: 6px 12px;
+      margin-right: 6px;
+    }
+  }
+}
 </style>
-  

+ 252 - 40
src/views/advertManage/advertRule/add.vue

@@ -1,45 +1,113 @@
 <template>
-  <div>
-    <s-header :name="titleName" :noback="false"></s-header>
-    <van-form show-error-message @submit="onSubmit">
-      <div class="cust_vantBorder">
-        <van-field colon :border="false" required v-model="cofficentForm.name" clearable name="name"
+  <div class="rule-form-container">
+    <!-- 头部导航 -->
+    <s-header :name="titleName" :noback="false" />
+
+    <!-- 表单区域 -->
+    <van-form show-error-message @submit="onSubmit" class="rule-form">
+      <div class="form-section">
+        <!-- 规则名称字段 -->
+
+        <van-field
+          required
+          v-model="cofficentForm.name"
+          clearable
+          name="name"
           :label="$t('advertManage.advertRule.addRule.ruleName')"
-          :placeholder="$t('advertManage.advertRule.addRule.ruleNamePlace')" :rules="[
+          :placeholder="$t('advertManage.advertRule.addRule.ruleNamePlace')"
+          :rules="[
             {
               required: true,
               message: $t('advertManage.advertRule.addRule.ruleNamePlace'),
             },
-          ]" />
-        <van-field colon :border="false" v-model="cofficentForm.userNames" clearable name="userNames"
-          :label="$t('advertManage.advertRule.addRule.pushMerchant')"
-          :placeholder="$t('advertManage.advertRule.addRule.pushMerchantPlace')" />
-        <van-field readonly colon :border="false" v-model="cofficentForm.A_advertList" clearable name="A_advertList"
-          :placeholder="$t('advertManage.advertRule.addRule.AScreenPlace')"
-          :label="$t('advertManage.advertRule.addRule.AScreen')">
-          <template #button>
-            <van-button @click="searchClk(0)" type="primary">{{
-              $t("advertManage.advertRule.addRule.screen")
-            }}</van-button>
+          ]"
+          class="custom-field"
+        >
+          <template #left-icon>
+            <van-icon name="label" color="#1989fa" />
           </template>
         </van-field>
-        <van-field required readonly colon :border="false" v-model="cofficentForm.B_advertList" clearable
-          name="B_advertList" :placeholder="$t('advertManage.advertRule.addRule.BScreennPlace')"
-          :label="$t('advertManage.advertRule.addRule.BScreen')" :rules="[
-            {
-              required: true,
-              message: $t('advertManage.advertRule.addRule.BScreennPlace'),
-            },
-          ]">
-          <template #button>
-            <van-button @click="searchClk(1)" type="primary">{{
-              $t("advertManage.advertRule.addRule.screen")
-            }}</van-button>
+
+        <!-- 推送商户字段 -->
+        <van-field
+          v-model="cofficentForm.userNames"
+          clearable
+          name="userNames"
+          :label="$t('advertManage.advertRule.addRule.pushMerchant')"
+          :placeholder="$t('advertManage.advertRule.addRule.pushMerchantPlace')"
+          class="custom-field"
+        >
+          <template #left-icon>
+            <van-icon name="shop" color="#1989fa" />
           </template>
         </van-field>
       </div>
-      <div style="margin: 16px">
-        <van-button round block type="primary" native-type="submit">
+
+      <!-- 屏幕广告设置部分 -->
+      <div class="form-section">
+
+        <!-- A屏幕广告设置 -->
+        <div class="screen-config">
+          <div class="screen-header">
+            <div class="screen-title">
+              <van-icon name="tv-o" color="#ee0a24" class="screen-icon" />
+              <span>{{ $t("advertManage.advertRule.addRule.AScreen") }}</span>
+            </div>
+            <van-button
+              size="small"
+              icon="search"
+              type="primary"
+              @click="searchClk(0)"
+              class="select-button"
+            >
+              {{ $t("advertManage.advertRule.addRule.screen") }}
+            </van-button>
+          </div>
+          <div class="selected-content" v-if="cofficentForm.A_advertList">
+            {{ cofficentForm.A_advertList }}
+          </div>
+          <div v-else class="placeholder">
+            {{ $t("advertManage.advertRule.addRule.AScreenPlace") }}
+          </div>
+        </div>
+
+        <!-- B屏幕广告设置 -->
+        <div class="screen-config">
+          <div class="screen-header">
+            <div class="screen-title">
+              <van-icon name="tv-o" color="#1989fa" class="screen-icon" />
+              <span>{{ $t("advertManage.advertRule.addRule.BScreen") }}</span>
+            </div>
+            <van-button
+              size="small"
+              icon="search"
+              type="primary"
+              @click="searchClk(1)"
+              class="select-button"
+            >
+              {{ $t("advertManage.advertRule.addRule.screen") }}
+            </van-button>
+          </div>
+          <div class="selected-content" v-if="cofficentForm.B_advertList">
+            {{ cofficentForm.B_advertList }}
+          </div>
+          <div v-else class="placeholder">
+            {{ $t("advertManage.advertRule.addRule.BScreennPlace") }}
+          </div>
+        </div>
+      </div>
+
+      <!-- 提交按钮 -->
+      <div class="submit-section">
+        <van-button
+          round
+          block
+          type="primary"
+          native-type="submit"
+          icon="passed"
+          size="large"
+          class="submit-button"
+        >
           {{ $t("advertManage.advertRule.addRule.submit") }}
         </van-button>
       </div>
@@ -49,7 +117,7 @@
 
 <script>
 // 导入公用方法
-import { setLocal, styleUrl } from "@/common/js/utils";
+import { setLocal } from "@/common/js/utils";
 // 导入接口
 import {
   Api_postAddAdRule,
@@ -62,7 +130,7 @@ import { useI18n } from "vue-i18n";
 import { onMounted, ref, reactive } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { useStore } from "vuex";
-import { showToast } from 'vant';
+import { showToast } from "vant";
 export default {
   name: "advertRuleAdd",
   components: { sHeader },
@@ -86,7 +154,6 @@ export default {
     // 刚进页面
     onMounted(() => {
       // 加载样式
-      styleUrl('advertManage');
       // 如果是第一次进入页面就请求接口
       const id = route.query.id || "";
       adRuleId.value = id;
@@ -95,7 +162,6 @@ export default {
         titleName.value = t("advertManage.advertRule.addRule.editTitle");
         getDetail(id);
       }
-      console.log(11111111);
       // 获取广告规则
       const A_advertList = store.state.$S_advertManage_A_advertList;
       const B_advertList = store.state.$S_advertManage_B_advertList;
@@ -123,7 +189,6 @@ export default {
         const AadsIds = A_advertList.map((item) => item.id);
         cofficentForm.A_advertList = Aads.join(",");
         cofficentForm.rule = cofficentForm.rule.concat(AadsIds);
-        console.log("AadsIds", AadsIds);
       }
       if (B_advertList && B_advertList.length > 0) {
         const Bads = B_advertList.map((item) => item.name);
@@ -134,7 +199,6 @@ export default {
     };
     const getAdsByRuleId = (id) => {
       Api_getAdListByTimeRule({ id }).then((res) => {
-        console.log("res111111111", res);
         const { data } = res.data || [];
         if (data.length > 0) {
           const AadsList = data.filter((item) => item.screenType === 0);
@@ -154,7 +218,6 @@ export default {
     const getDetail = (id) => {
       Api_getAdRuleDetail({ id }).then((res) => {
         const resData = res.data.data;
-        console.log("resData", resData);
         cofficentForm.name = resData.name;
         cofficentForm.userNames = resData.userNames;
       });
@@ -170,7 +233,6 @@ export default {
           userNames: cofficentForm.userNames,
         };
         Api_postUpdateAdRule(param).then((res) => {
-          console.log(res);
           if (res.data.code === "00000") {
             showToast(res.data.message);
             setTimeout(() => {
@@ -208,4 +270,154 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+@primary-color: #4d6add;
+
+.rule-form-container {
+  background-color: #f5f7fa;
+  min-height: 100vh;
+  // padding: 0 16px 20px;
+  display: flex;
+  flex-direction: column;
+}
+
+.rule-form {
+  background-color: white;
+  border-radius: 16px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  padding: 20px;
+  flex: 1;
+}
+
+.form-section {
+  margin-bottom: 24px;
+
+  &:last-child {
+    margin-bottom: 0;
+  }
+}
+
+.form-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16px;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #f0f0f0;
+
+  .form-icon {
+    margin-right: 10px;
+    color: @primary-color;
+  }
+
+  h3 {
+    font-size: 16px;
+    font-weight: 600;
+    color: #333;
+  }
+}
+
+.custom-field {
+  background-color: #fafafa;
+  border-radius: 10px;
+  padding: 10px 16px;
+  margin-bottom: 15px;
+
+  :deep(.van-field__label) {
+    font-weight: 500;
+    color: #555;
+  }
+
+  :deep(.van-field__control) {
+    color: #333;
+  }
+}
+
+.screen-config {
+  background: #f9f9f9;
+  border-radius: 12px;
+  padding: 16px;
+  margin-bottom: 16px;
+  border: 1px solid #eee;
+
+  .screen-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 12px;
+  }
+
+  .screen-title {
+    display: flex;
+    align-items: center;
+    font-size: 15px;
+    font-weight: 500;
+    color: #333;
+
+    .screen-icon {
+      margin-right: 8px;
+      font-size: 18px;
+    }
+  }
+
+  .select-button {
+    border-radius: 20px;
+    padding: 0 15px;
+  }
+
+  .selected-content {
+    padding: 12px;
+    background-color: #e6f7ff;
+    border-radius: 8px;
+    border: 1px dashed #91d5ff;
+    color: #0050b3;
+    font-size: 14px;
+    line-height: 1.4;
+    margin-top: 8px;
+  }
+
+  .placeholder {
+    padding: 12px;
+    color: #bfbfbf;
+    font-style: italic;
+    text-align: center;
+    font-size: 14px;
+    background-color: #fafafa;
+    border-radius: 8px;
+    margin-top: 8px;
+  }
+}
+
+.submit-section {
+  padding-top: 16px;
+  margin-top: 20px;
+  border-top: 1px dashed #f0f0f0;
+
+  .submit-button {
+    height: 46px;
+    font-size: 16px;
+    font-weight: 500;
+    box-shadow: 0 4px 10px rgba(24, 144, 255, 0.3);
+
+    &:active {
+      opacity: 0.9;
+      transform: scale(0.98);
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .rule-form {
+    padding: 16px;
+  }
+
+  .screen-config {
+    padding: 12px;
+  }
+
+  .form-header {
+    h3 {
+      font-size: 15px;
+    }
+  }
+}
+</style>

+ 283 - 77
src/views/advertManage/advertRule/index.vue

@@ -1,95 +1,116 @@
 <template>
-  <div class="advertRuleIdx">
-    <s-header :name="$t('advertManage.advertRule.sysTitle')" :noback="false"></s-header>
-    <div v-if="isDelete" class="headCon l-flex-between o-plr-20 o-pt-15 o-pb-12 kBordBott">
-      <div class="l-flex-RC c-text-15 c-text-color c-text-b">
-        {{ $t("advertManage.advertRule.selected") }}
-        {{ selectTotals }}
-        {{ $t("advertManage.advertRule.individual") }}
-      </div>
-      <div class="l-flex-RC">
-        <div @click="cancelClk" class="c-text-c c-text-13 c-color">
-          {{ $t("advertManage.advertRule.cancel") }}
-        </div>
-        <div @click="noticeClk" class="c-text-c o-ml-24 c-text-13" style="color: #df5e4c">
-          {{ $t("advertManage.advertRule.confirmDel") }}
-        </div>
-      </div>
-    </div>
-    <div v-else class="headCon l-flex-between o-plr-20 o-pt-26 o-pb-12 kBordBott">
-      <div class="l-flex-RC">
-        <img class="ruleIcon o-mr-10" src="../../../assets/advertManage/ruleIcon.png" alt="" />
-        <div class="c-text-color c-text-b c-text-15">
-          {{ $t("advertManage.advertRule.total")
-          }}<span class="c-text-20" style="color: #df5e4c">{{
-  ruleTotal
-}}</span>
-          {{ $t("advertManage.advertRule.rules") }}
-        </div>
+  <div class="advert-rule-page">
+    <!-- 头部导航 -->
+    <s-header :name="$t('advertManage.advertRule.sysTitle')" :noback="false" />
+
+    <!-- 顶部操作栏 -->
+    <div class="top-action-bar" :class="{ 'delete-mode': isDelete }">
+      <div class="status-display">
+        <!-- 正常模式显示的内容 -->
+        <template v-if="isDelete">
+          <span>{{ $t("advertManage.advertRule.selected") }}</span>
+          <span class="count-highlight">{{ selectTotals }}</span>
+          <span>{{ $t("advertManage.advertRule.individual") }}</span>
+        </template>
+        <template v-else>
+          <span>{{ $t("advertManage.advertRule.total") }}</span>
+          <span class="count-highlight">{{ ruleTotal }}</span>
+          <span>{{ $t("advertManage.advertRule.rules") }}</span>
+        </template>
       </div>
-      <div class="l-flex-RC">
-        <div @click="toAdd" class="c-text-c">
-          <div>
-            <van-icon size="18" name="add-o" />
-          </div>
-          <div class="c-text-color c-text-12">
+
+      <div class="action-buttons">
+        <template v-if="isDelete">
+          <van-button size="small" @click="cancelClk">
+            {{ $t("advertManage.advertRule.cancel") }}
+          </van-button>
+          <van-button
+            size="small"
+            type="danger"
+            class="confirm-btn"
+            @click="noticeClk"
+          >
+            {{ $t("advertManage.advertRule.confirmDel") }}
+          </van-button>
+        </template>
+        <template v-else>
+          <van-button size="small" icon="plus" type="primary" @click="toAdd">
             {{ $t("advertManage.advertRule.add") }}
-          </div>
-        </div>
-        <div @click="isDelete = true" class="c-text-c o-ml-24">
-          <div>
-            <van-icon size="18" name="close" />
-          </div>
-          <div class="c-text-color c-text-12">
+          </van-button>
+          <van-button size="small" plain icon="delete" @click="isDelete = true">
             {{ $t("advertManage.advertRule.delete") }}
-          </div>
-        </div>
+          </van-button>
+        </template>
       </div>
     </div>
-    <div class="contentCon">
-      <van-pull-refresh disabled v-model="refreshing" @refresh="onRefresh">
-        <van-list v-model="loading" :finished="finished" :finished-text="$t('common.noMoreTxt')" @load="onLoad"
-          :offset="20" :immediate-check="false">
-          <div @click.self="toEdit(item)" v-for="item in tableData" :key="item.id"
-            class="o-plr-20 o-pt-24 o-pb-12 l-flex-between kBordBott content">
-            <div>
-              <div class="c-text-b c-text-color c-text-15">{{ item.name }}</div>
-              <div class="l-flex-RC o-mt-14">
-                <span class="c-color c-text-12">{{ $t("advertManage.advertRule.createTime") }}:</span>
-                <span class="c-text-color c-text-12">{{
+
+    <!-- 规则列表 -->
+    <div class="rules-list-container">
+      <van-list
+        v-model:loading="loading"
+        :finished="finished"
+        :finished-text="$t('common.noMoreTxt')"
+        :offset="20"
+        :immediate-check="false"
+        @load="onLoad"
+      >
+        <div
+          v-for="item in tableData"
+          :key="item.id"
+          class="rule-card"
+          @click="!isDelete ? toEdit(item) : null"
+        >
+          <div class="card-content">
+            <div class="rule-info">
+              <div class="rule-name">{{ item.name }}</div>
+              <div class="create-date">
+                <span class="date-label"
+                  >{{ $t("advertManage.advertRule.createTime") }}:</span
+                >
+                <span class="date-value">{{
                   Format_time(item.createDate)
                 }}</span>
               </div>
             </div>
-            <div class="rightCon">
-              <template v-if="isDelete">
-                <van-checkbox v-model="item.checked">{{ " " }}</van-checkbox>
-              </template>
-              <template v-else>
-                <div @click="pushRuleClk(item.id)" class="pushBtn o-mb-10 c-text-14">
+
+            <!-- 操作按钮 -->
+            <div class="action-buttons">
+              <template v-if="!isDelete">
+                <van-button
+                  size="small"
+                  class="push-btn"
+                  @click.stop="pushRuleClk(item.id)"
+                >
                   {{ $t("advertManage.advertRule.push") }}
-                </div>
-                <div @click="toEdit(item)" class="editBtn c-text-14">
+                </van-button>
+                <van-button
+                  size="small"
+                  plain
+                  class="edit-btn"
+                  @click.stop="toEdit(item)"
+                >
                   {{ $t("advertManage.advertRule.edit") }}
-                </div>
+                </van-button>
+              </template>
+
+              <template v-else>
+                <van-checkbox
+                  v-model="item.checked"
+                  class="delete-checkbox"
+                  @click.stop
+                />
               </template>
             </div>
           </div>
-        </van-list>
-      </van-pull-refresh>
+        </div>
+      </van-list>
     </div>
-    <!-- 删除确认弹窗 -->
-    <kDialog :dialogTitle="$t('advertManage.delPopTitle')" :cancelBtnTxt="$t('advertManage.cancelTxt')"
-      :confirmBtnTxt="$t('advertManage.confirmDel')" ref="kDialogRef" :dialogContent="$t('advertManage.delPopContent')"
-      @confirmclk="confirmClk">
-    </kDialog>
   </div>
 </template>
 
 <script>
 import { useStore } from "vuex";
-import { Format_time, styleUrl } from "../../../common/js/utils";
-import kDialog from "../../../components/commom/kDialog/index.vue";
+import { Format_time } from "../../../common/js/utils";
 // 导入接口
 import {
   Api_postPageTimeRule,
@@ -99,10 +120,10 @@ import {
 import { computed, onMounted, reactive, ref, toRefs } from "vue";
 import sHeader from "../../../components/SimpleHeader";
 import { useRouter } from "vue-router";
-import { showToast } from 'vant';
+import { showConfirmDialog, showToast } from "vant";
 import { useI18n } from "vue-i18n";
 export default {
-  components: { sHeader, kDialog },
+  components: { sHeader },
   setup() {
     const { t } = useI18n();
     // 删除确认弹窗
@@ -113,7 +134,18 @@ export default {
         showToast(t("advertManage.delTips"));
         return;
       }
-      kDialogRef.value.openDialog();
+      showConfirmDialog({
+        title: t("advertManage.delPopTitle"),
+        message: t("advertManage.delPopContent"),
+        confirmButtonText: t("advertManage.confirmDel"),
+        cancelButtonText: t("advertManage.cancelTxt"),
+        onConfirm: () => {
+          confirmClk();
+        },
+        onCancel: () => {
+          cancelClk();
+        },
+      });
     };
     // 点击右侧按钮
     const confirmClk = () => {
@@ -128,7 +160,7 @@ export default {
       };
       Api_getDelAdRule(param).then((res) => {
         console.log(res);
-        if (res.data.code === '00000') {
+        if (res.data.code === "00000") {
           isDelete.value = false;
           showToast(res.data.message);
           setTimeout(() => {
@@ -180,7 +212,6 @@ export default {
     const store = useStore();
     onMounted(() => {
       // 加载样式
-      styleUrl('advertManage');
       onRefresh();
       // 初始化
       init();
@@ -283,4 +314,179 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+@primary-color: #4d6add;
+@error-color: #f56c6c;
+@text-color: #333;
+.advert-rule-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f7fa;
+}
+
+.top-action-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px;
+  margin: 12px;
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+  &.delete-mode {
+    background-color: #fff8f6;
+  }
+
+  .status-display {
+    font-size: 15px;
+    color: #666;
+
+    .count-highlight {
+      font-size: 18px;
+      font-weight: bold;
+      color: @primary-color;
+      margin: 0 3px;
+    }
+  }
+
+  .action-buttons {
+    display: flex;
+    gap: 8px;
+
+    .cancel-btn {
+      border: 1px solid #dcdee0;
+    }
+
+    .confirm-btn {
+      background: linear-gradient(to right, #ff7062, #df5e4c);
+    }
+  }
+}
+
+.rules-list-container {
+  flex: 1;
+  overflow-y: auto;
+}
+
+.rule-card {
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  margin-bottom: 12px;
+  transition: all 0.2s;
+  overflow: hidden;
+
+  &:hover {
+    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+    transform: translateY(-2px);
+  }
+
+  .card-content {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 16px;
+
+    .rule-info {
+      flex: 1;
+      min-width: 0;
+
+      .rule-name {
+        font-size: 15px;
+        font-weight: bold;
+        color: #333;
+        margin-bottom: 8px;
+      }
+
+      .create-date {
+        font-size: 13px;
+        color: #666;
+
+        .date-label {
+          color: #999;
+        }
+      }
+    }
+
+    .action-buttons {
+      display: flex;
+      gap: 8px;
+      margin-left: 12px;
+
+      .push-btn {
+        background-color: @primary-color;
+        color: white;
+        min-width: 60px;
+      }
+
+      .edit-btn {
+        color: @text-color;
+        border-color: #dcdee0;
+        min-width: 60px;
+      }
+    }
+  }
+}
+
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 40px 20px;
+  background-color: white;
+  border-radius: 12px;
+  text-align: center;
+
+  .empty-text {
+    color: #999;
+    font-size: 14px;
+    margin: 15px 0 20px;
+  }
+}
+
+@media (max-width: 480px) {
+  .top-action-bar {
+    display: flex;
+    justify-content: space-between;
+    .action-buttons {
+      justify-content: space-between;
+      display: flex;
+      gap: 10px;
+    }
+  }
+
+  .rules-list-container {
+    padding: 12px;
+  }
+
+  .rule-card {
+    .card-content {
+      padding: 12px;
+
+      .rule-info {
+        .rule-name {
+          font-size: 14px;
+        }
+
+        .create-date {
+          font-size: 12px;
+        }
+      }
+
+      .action-buttons {
+        flex-direction: column;
+        gap: 6px;
+
+        .push-btn,
+        .edit-btn {
+          min-width: 50px;
+          padding: 0 8px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 316 - 53
src/views/advertManage/advertRule/screen.vue

@@ -1,67 +1,150 @@
 <template>
-  <div class="tagSetIdx">
-    <s-header :name="$t('advertManage.advertRule.addRule.adScreen')" :noback="false"></s-header>
-    <div class="o-p-10 cust_vantBorder">
-      <div class="l-flex-between kBordBott o-pb-20">
-        <div style="
-            flex: 0 0 46px;
-            font-size: var(--van-cell-font-size);
-            color: var(--van-field-label-color);
-          ">
-          {{
-            cofficentForm.screenType === "0"
-            ? $t("advertManage.advertRule.addRule.AScreen")
-            : $t("advertManage.advertRule.addRule.BScreen")
+  <div class="screen-config-page">
+    <!-- 头部导航 -->
+    <s-header :name="$t('advertManage.advertRule.addRule.adScreen')" :noback="false" />
+    
+    <!-- 主要内容区域 -->
+    <div class="screen-config-container">
+      <!-- 顶部标签选择区域 -->
+      <div class="tag-section">
+        <div class="screen-indicator">
+          {{ 
+            cofficentForm.screenType === "0" 
+              ? $t("advertManage.advertRule.addRule.AScreen") 
+              : $t("advertManage.advertRule.addRule.BScreen") 
           }}
         </div>
-        <div class="c-border o-ptb-10 o-plr-12 l-flex-RC l-flex-w o-w" style="padding-bottom: 0; min-height: 36px">
-          <div class="c-border l-flex-RC o-p-3 c-color-f o-mr-10 o-mb-10"
-            style="background: #e59a6d; border: 1px solid #e59a6d" v-for="(item, index) in tagList" :key="index">
-            <span class="o-mr-3">{{ item.name }}</span>
-            <van-icon @click.stop="delTag(item, index)" name="clear" />
+        
+        <div class="selected-tags">
+          <div 
+            v-for="(item, index) in tagList" 
+            :key="index" 
+            class="tag-item"
+          >
+            <span class="tag-name">{{ item.name }}</span>
+            <van-icon 
+              @click.stop="delTag(item, index)" 
+              name="clear" 
+              class="tag-remove-icon" 
+            />
           </div>
         </div>
-        <div @click="submitClk" class="submitBtn o-ml-10">
-          <van-button round type="primary">{{
-            $t("advertManage.advertRule.add")
-          }}</van-button>
+        
+        <div class="submit-section">
+          <van-button 
+            round 
+            type="primary" 
+            class="submit-button"
+            @click="submitClk"
+          >
+            {{ $t("advertManage.advertRule.add") }}
+          </van-button>
         </div>
       </div>
-      <van-field colon :border="false" v-model="cofficentForm.username" clearable
-        :placeholder="$t('advertManage.advertRule.addRule.userNamePlace')"
-        :label="$t('advertManage.advertRule.addRule.userName')" />
-      <van-field colon :border="false" v-model="cofficentForm.name" clearable
-        :placeholder="$t('advertManage.advertRule.addRule.adNamePlace')"
-        :label="$t('advertManage.advertRule.addRule.adName')" />
-
-      <van-field colon :border="false" @click-input="busiInpClk" readonly clearable v-model="cofficentForm.type"
-        :placeholder="$t('advertManage.advertRule.addRule.adTypePlace')"
-        :label="$t('advertManage.advertRule.addRule.adType')">
-        <template #right-icon>
-          <div class="l-flex-RC">
-            <van-icon v-if="cofficentForm.type" @click="cofficentForm.type = ''" class="o-mr-6" name="clear" />
-            <van-icon @click="busiInpClk" name="arrow-down" />
-          </div>
-        </template>
-      </van-field>
-      <div class="l-flex-center" style="margin: 16px">
-        <van-button round class="searchBtn" type="primary" size="large" @click="toSearch">
-          {{ $t("advertManage.advertRule.addRule.search") }}
-        </van-button>
+      
+      <!-- 表单区域 -->
+      <div class="form-section">
+        <!-- 用户名搜索 -->
+        <van-field 
+          :border="false" 
+          v-model="cofficentForm.username" 
+          clearable
+          :placeholder="$t('advertManage.advertRule.addRule.userNamePlace')"
+          :label="$t('advertManage.advertRule.addRule.userName')"
+          class="custom-field"
+        >
+          <template #left-icon>
+            <van-icon name="user" color="#1989fa" />
+          </template>
+        </van-field>
+        
+        <!-- 广告名称 -->
+        <van-field 
+          :border="false" 
+          v-model="cofficentForm.name" 
+          clearable
+          :placeholder="$t('advertManage.advertRule.addRule.adNamePlace')"
+          :label="$t('advertManage.advertRule.addRule.adName')"
+          class="custom-field"
+        >
+          <template #left-icon>
+            <van-icon name="label" color="#1989fa" />
+          </template>
+        </van-field>
+        
+        <!-- 广告类型选择 -->
+        <van-field 
+          :border="false" 
+          readonly 
+          v-model="cofficentForm.type"
+          :placeholder="$t('advertManage.advertRule.addRule.adTypePlace')"
+          :label="$t('advertManage.advertRule.addRule.adType')"
+          class="custom-field"
+          @click="busiInpClk"
+        >
+          <template #left-icon>
+            <van-icon name="photo" color="#1989fa" />
+          </template>
+          <template #right-icon>
+            <van-icon 
+              v-if="cofficentForm.type" 
+              @click.stop="cofficentForm.type = ''" 
+              name="clear" 
+              class="field-clear-icon" 
+            />
+            <van-icon name="arrow-down" class="field-arrow-icon" />
+          </template>
+        </van-field>
+        
+        <!-- 搜索按钮 -->
+        <div class="search-button-section">
+          <van-button 
+            round 
+            type="primary" 
+            size="large" 
+            class="search-button"
+            @click="toSearch"
+          >
+            <van-icon name="search" class="button-icon" />
+            {{ $t("advertManage.advertRule.addRule.search") }}
+          </van-button>
+        </div>
       </div>
-      <div class="o-mt-30">
-        <div class="l-flex-RC l-flex-w">
-          <div @click="addAllTag(item)" class="c-border l-flex-RC o-p-3 c-color-f o-mr-10 o-mb-10"
-            style="background: #e59a6d; border: 1px solid #e59a6d" v-for="(item, index) in allTagList" :key="index">
-            <span class="o-mr-3">{{ item.name }}</span>
+      
+      <!-- 可用标签区域 -->
+      <div class="available-tags-section">
+        <h3 class="section-title">
+          可用广告
+        </h3>
+        
+        <div class="tag-list">
+          <div 
+            v-for="(item, index) in allTagList" 
+            :key="index" 
+            class="tag-item"
+            @click="addAllTag(item)"
+          >
+            <span class="tag-name">{{ item.name }}</span>
+            <van-icon name="plus" class="tag-add-icon" />
           </div>
         </div>
       </div>
     </div>
-    <!-- 广告类型选择框 -->
-    <van-popup v-model:show="busiPopShow" position="bottom">
-      <van-picker :title="$t('advertManage.advertRule.addRule.adTypePlace')" :columns="busiPopList"
-        :columns-field-names="busiPopFieldName" @confirm="busiPopConfirm" @cancel="busiPopCancel" />
+    
+    <!-- 广告类型选择弹窗 -->
+    <van-popup 
+      v-model:show="busiPopShow" 
+      position="bottom" 
+      round
+      class="type-select-popup"
+    >
+      <van-picker 
+        :title="$t('advertManage.advertRule.addRule.adTypePlace')" 
+        :columns="busiPopList"
+        :columns-field-names="busiPopFieldName" 
+        @confirm="busiPopConfirm" 
+        @cancel="busiPopCancel" 
+      />
     </van-popup>
   </div>
 </template>
@@ -245,4 +328,184 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+@primary-color: #4d6add;
+@error-color: #f56c6c;
+@text-color: #333;
+@border-color: #dcdfe6;
+
+.screen-config-page {
+  background-color: #f5f7fa;
+  min-height: 100vh;
+}
+
+.screen-config-container {
+  padding: 16px;
+}
+
+.tag-section {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  justify-content: center;
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+  padding: 15px;
+  margin-bottom: 16px;
+  
+  .screen-indicator {
+    flex: 0 0 40px;
+    display: flex;
+    align-items: center;
+  }
+  
+  .selected-tags {
+    flex: 1;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    min-height: 40px;
+  }
+  
+  .submit-section {
+    flex: 0 0 auto;
+    margin-left: 12px;
+    
+    .submit-button {
+      min-width: 80px;
+      // height: 34px;
+    }
+  }
+}
+
+.form-section {
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+  padding: 16px;
+  margin-bottom: 16px;
+}
+
+.search-button-section {
+  margin-top: 20px;
+  
+  .search-button {
+    .button-icon {
+      margin-right: 8px;
+    }
+  }
+}
+
+.available-tags-section {
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+  padding: 16px;
+  
+  .section-title {
+    // font-size: @font-size-lg;
+    font-weight: 600;
+    // color: @text-color;
+    margin-bottom: 12px;
+    padding-bottom: 8px;
+    border-bottom: 1px solid @border-color;
+  }
+}
+
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.tag-item {
+  display: flex;
+  align-items: center;
+  background: linear-gradient(135deg, #e59a6d, #e58765);
+  color: white;
+  border-radius: 20px;
+  padding: 6px 14px;
+  // font-size: @font-size-sm;
+  cursor: pointer;
+  transition: all 0.3s;
+  
+  .tag-name {
+    margin-right: 6px;
+  }
+  
+  .tag-add-icon, .tag-remove-icon {
+    font-size: 16px;
+    transition: transform 0.3s;
+  }
+  
+  .tag-add-icon {
+    opacity: 0.8;
+  }
+  
+  &:hover {
+    box-shadow: 0 4px 8px rgba(229, 138, 101, 0.3);
+    transform: translateY(-2px);
+    
+    .tag-add-icon, .tag-remove-icon {
+      transform: scale(1.2);
+    }
+  }
+}
+
+.custom-field {
+  background-color: #fafafa;
+  border-radius: 10px;
+  padding: 10px 16px;
+  margin-bottom: 15px;
+  
+  :deep(.van-field__label) {
+    font-weight: 500;
+    // color: @text-color-secondary;
+  }
+  
+  .field-clear-icon {
+    color: #c8c9cc;
+    margin-right: 8px;
+    
+    &:active {
+      color: #969799;
+    }
+  }
+  
+  .field-arrow-icon {
+    color: #969799;
+  }
+}
+
+.type-select-popup {
+  :deep(.van-picker) {
+    background-color: #f7f8fa;
+  }
+  
+  :deep(.van-picker__toolbar) {
+    background-color: white;
+  }
+}
+
+@media (max-width: 480px) {
+  .tag-section {
+    
+    .selected-tags {
+      width: 100%;
+      // margin-bottom: 15px;
+    }
+    
+
+  }
+  
+  .tag-item {
+    padding: 5px 10px;
+    // font-size: @font-size-sm - 1;
+  }
+  
+  .form-section {
+    padding: 12px;
+  }
+}
+</style>

+ 381 - 97
src/views/advertManage/index.vue

@@ -1,121 +1,176 @@
 <template>
-  <!-- 广告管理 -->
-  <div class="advertPage flex-col">
-    <s-header :name="$t('advertManage.advertisingManagement')" :noback="false"></s-header>
-    <!-- <video ref="video" id="video" controls></video> -->
-    <div class="advertBox flex-col">
-      <van-list v-model:loading="loading" v-model:error="error" error-text="{{$t('advertManage.requestFailed')}}"
-        :finished="finished" finished-text="{{$t('advertManage.noMore')}}" offset="300" :immediate-check="false"
-        @load="onLoad">
-        <div v-if="isDelete" class="l-flex-between o-plr-20 o-pt-16 o-pb-6 kBordBott">
-          <div class="l-flex-RC c-text-15 c-text-color c-text-b">
-            {{ $t("advertManage.advertRule.selected") }}
-            {{ selectTotals }}
-            {{ $t("advertManage.advertRule.individual") }}
-          </div>
-          <div class="l-flex-RC">
-            <div @click="cancelClk" class="c-text-c c-text-13 c-color">
-              {{ $t("advertManage.advertRule.cancel") }}
-            </div>
-            <div @click="noticeClk" class="c-text-c o-ml-24 c-text-13" style="color: #df5e4c">
-              {{ $t("advertManage.advertRule.confirmDel") }}
-            </div>
-          </div>
-        </div>
-        <div v-else class="searchRow l-flex-between o-plr-20 o-pt-16 o-pb-6 kBordBott">
-          <div class="flex-col">
-            <div class="flex-row justify-between bd3">
-              <div class="flex-col outer4"></div>
-              <span class="flex-col txt2 o-ml-10">{{ $t("advertManage.total")
-                }}<span class="discountNumber">{{ adTotal }}</span>{{ $t("advertManage.ads") }}</span>
-            </div>
-          </div>
-          <div class="flex-col">
-            <div class="operBox">
-              <div class="listBox" @click="pushAdRule()">
-                <img class="iconImg" src="../../assets/accountPer/listIcon.png" />
-                <span>{{ $t("advertManage.advertisingRules") }}</span>
-              </div>
-              <div class="addBox" @click="pushAdd()">
-                <img class="iconImg" src="../../assets/accountPer/addicon.png" />
-                <span>{{ $t("advertManage.add") }}</span>
-              </div>
-              <div @click="isDelete = true" class="searchBox">
-                <img class="iconImg" src="../../assets/advertManage/closeIcon.png" />
-                <span>{{ $t("advertManage.delete") }}</span>
+  <!-- 广告管理页面 -->
+  <div class="advert-page">
+    <!-- 头部导航 -->
+    <s-header
+      :name="$t('advertManage.advertisingManagement')"
+      :noback="false"
+    />
+
+    <!-- 主要内容区域 -->
+    <!-- 顶部操作栏 -->
+    <div class="top-action-bar" :class="{ 'delete-mode': isDelete }">
+      <div class="status-display">
+        <template v-if="isDelete">
+          <span>{{ $t("advertManage.advertRule.selected") }}</span>
+          <span class="count-highlight">{{ selectTotals }}</span>
+          <span>{{ $t("advertManage.advertRule.individual") }}</span>
+        </template>
+        <template v-else>
+          <span>{{ $t("advertManage.total") }}</span>
+          <span class="count-highlight">{{ adTotal }}</span>
+          <span>{{ $t("advertManage.ads") }}</span>
+        </template>
+      </div>
+
+      <div class="action-buttons">
+        <template v-if="isDelete">
+          <van-button size="small" class="cancel-btn" @click="cancelClk">
+            {{ $t("advertManage.advertRule.cancel") }}
+          </van-button>
+          <van-button
+            size="small"
+            type="danger"
+            class="confirm-btn"
+            @click="noticeClk"
+          >
+            {{ $t("advertManage.advertRule.confirmDel") }}
+          </van-button>
+        </template>
+        <template v-else>
+          <van-button plain size="small" icon="notes-o" @click="pushAdRule">
+            {{ $t("advertManage.advertisingRules") }}
+          </van-button>
+          <van-button type="primary" size="small" icon="plus" @click="pushAdd">
+            {{ $t("advertManage.add") }}
+          </van-button>
+          <van-button plain size="small" icon="delete" @click="isDelete = true">
+            {{ $t("advertManage.delete") }}
+          </van-button>
+        </template>
+      </div>
+    </div>
+
+    <!-- 广告列表 -->
+    <div class="ad-list">
+      <van-list
+        v-model:loading="loading"
+        v-model:error="error"
+        :error-text="$t('advertManage.requestFailed')"
+        :finished="finished"
+        :finished-text="$t('advertManage.noMore')"
+        :offset="300"
+        :immediate-check="false"
+        @load="onLoad"
+      >
+        <!-- 广告卡片 -->
+        <div
+          v-for="(item, index) in adList"
+          :key="index"
+          class="ad-card"
+          :class="{ selectable: isDelete }"
+        >
+          <div class="card-content" @click="isDelete ? null : adUpdate(item)">
+            <!-- 广告图片/视频预览 -->
+            <div class="media-preview" @click.stop="loadVideo(item)">
+              <img
+                :src="isAdImgUrl(item)"
+                :alt="item.name"
+                class="preview-image"
+              />
+              <div v-if="item.adType === 1" class="video-indicator">
+                <van-icon name="play-circle-o" size="24" />
               </div>
             </div>
-          </div>
-        </div>
-        <div class="l-flex-between" v-for="(item, index) in adList" :key="index">
-          <div class="adList">
-            <img class="adImg o-mr-10" :src="isAdImgUrl(item)"  @click="loadVideo(item)" />
-            <div class="adInfoBox c-text-12">
-              <div class="adTitle c-text-15 c-text-color o-mb-14">
-                {{ item.name }}
-              </div>
-              <div class="adRow" @click="adUpdate(item)">
-                <span class="adLeft">{{ $t("advertManage.locationType") }}:</span>{{
-      item.locationType === 0
-        ? $t("advertManage.advertising")
-        : $t("advertManage.externalAdvertising")
-    }}
+
+            <!-- 广告信息 -->
+            <div class="ad-info">
+              <div class="ad-title">{{ item.name }}</div>
+
+              <div class="info-row">
+                <span class="info-label"
+                  >{{ $t("advertManage.locationType") }}:</span
+                >
+                <span class="info-value">
+                  {{
+                    item.locationType === 0
+                      ? $t("advertManage.advertising")
+                      : $t("advertManage.externalAdvertising")
+                  }}
+                </span>
               </div>
-              <div class="adRow">
-                <span class="adLeft">{{ $t("advertManage.advertisingPosition") }}:</span>{{
-      item.screenType === 0
-        ? $t("advertManage.screenA")
-        : $t("advertManage.screenB")
-    }}
+
+              <div class="info-row">
+                <span class="info-label"
+                  >{{ $t("advertManage.advertisingPosition") }}:</span
+                >
+                <span class="info-value">
+                  {{
+                    item.screenType === 0
+                      ? $t("advertManage.screenA")
+                      : $t("advertManage.screenB")
+                  }}
+                </span>
               </div>
-              <div class="adRow">
-                <span class="adLeft">{{ $t("advertManage.advertisingType") }}:</span>{{
-      item.adType === 0
-        ? $t("advertManage.picture")
-        : $t("advertManage.video")
-    }}
+
+              <div class="info-row">
+                <span class="info-label"
+                  >{{ $t("advertManage.advertisingType") }}:</span
+                >
+                <span class="info-value">
+                  {{
+                    item.adType === 0
+                      ? $t("advertManage.picture")
+                      : $t("advertManage.video")
+                  }}
+                </span>
               </div>
-              <div class="adRow">
-                <span class="adLeft">{{ $t("advertManage.creationTime") }}:</span>{{ showDateTime(item.createDate) }}
+
+              <div class="info-row">
+                <span class="info-label"
+                  >{{ $t("advertManage.creationTime") }}:</span
+                >
+                <span class="info-value">{{
+                  showDateTime(item.createDate)
+                }}</span>
               </div>
             </div>
           </div>
-          <template v-if="isDelete">
-            <van-checkbox v-model="item.checked">{{ " " }}</van-checkbox>
-          </template>
+
+          <!-- 选择框 (删除模式下显示) -->
+          <van-checkbox
+            v-if="isDelete"
+            v-model="item.checked"
+            class="selection-checkbox"
+            @click.stop
+          />
         </div>
       </van-list>
     </div>
-    <!-- 删除确认弹窗 -->
-    <kDialog :dialogTitle="$t('advertManage.delPopTitle')" :cancelBtnTxt="$t('advertManage.cancelTxt')"
-      :confirmBtnTxt="$t('advertManage.confirmDel')" ref="kDialogRef" :dialogContent="$t('advertManage.delPopContent')"
-      @confirmclk="confirmClk">
-    </kDialog>
-    <van-popup v-model:show="showVideo" style="width: 85vw; height: 50vw; overflow-y: hidden">
-      <video ref="video" id="video" controls style="width: 85vw; height: 50vw;" :src="videoUrl"></video>
-      <!-- 内容 -->
+    <!-- 视频预览弹窗 -->
+    <van-popup v-model:show="showVideo" round class="video-popup">
+      <video ref="video" controls class="video-player" :src="videoUrl"></video>
     </van-popup>
   </div>
+
 </template>
 
 <script>
-import kDialog from "@/components/commom/kDialog/index.vue";
 import { getAdList, Api_getDelAd } from "@/service/advertManage";
 import { onMounted, reactive, ref, computed } from "vue";
 import sHeader from "@/components/SimpleHeader";
-import { showFailToast, showToast } from 'vant';
+import { showConfirmDialog, showFailToast, showToast } from "vant";
 import dateUtil from "@/utils/dateUtil";
 import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
-import { styleUrl } from "../../common/js/utils";
 
 export default {
-  components: { sHeader, kDialog },
+  components: { sHeader },
   setup() {
     const { t } = useI18n();
     // 删除确认弹窗
     const showVideo = ref(false);
-    const videoUrl = ref('');
+    const videoUrl = ref("");
     const kDialogRef = ref(null);
     const confirmClk = () => {
       const ids = [];
@@ -128,7 +183,7 @@ export default {
         ids,
       };
       Api_getDelAd(param).then((res) => {
-        if (res.data.code === '00000') {
+        if (res.data.code === "00000") {
           isDelete.value = false;
           showToast(res.data.message);
           setTimeout(() => {
@@ -143,7 +198,14 @@ export default {
         showToast(t("advertManage.delTips"));
         return;
       }
-      kDialogRef.value.openDialog();
+      // kDialogRef.value.openDialog();
+      showConfirmDialog({
+        title: t("advertManage.delPopTitle"),
+        message: t("advertManage.delPopContent"),
+        onConfirm: () => {
+          confirmClk();
+        },
+      });
     };
 
     const router = useRouter();
@@ -163,7 +225,6 @@ export default {
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
-      styleUrl('advertManage');
       Init();
     });
     const Init = () => {
@@ -171,7 +232,7 @@ export default {
       searchParams.current = 1;
       loading.value = true;
       getAdListFun();
-    }
+    };
     const onLoad = () => {
       if (!finished.value) {
         searchParams.current = searchParams.current + 1;
@@ -180,7 +241,6 @@ export default {
       }
     };
     // 获取广告列表
-    // const videoRef = ref(null);
     const getAdListFun = async () => {
       const params = Object.assign({}, searchParams);
       const { data } = await getAdList(params);
@@ -208,9 +268,12 @@ export default {
       videoUrl.value = item.url;
     };
 
-
     const isAdImgUrl = (item) => {
-      return item.adType === 1 ? (item.mediaPreview ? item.mediaPreview : require('../../assets/start.png')) : item.url;
+      return item.adType === 1
+        ? item.mediaPreview
+          ? item.mediaPreview
+          : require("../../assets/start.png")
+        : item.url;
       // return require('../../assets/start.png');
     };
     const showDateTime = (date) => {
@@ -284,5 +347,226 @@ export default {
 </script>
 
 <style lang="less" scoped>
-@import "../../common/style/common.less";
+@primary-color: #4d6add;
+
+.advert-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f7fa;
+}
+
+.advert-container {
+  background: #f5f6fa;
+  height: calc(100% - 50px);
+  overflow: auto;
+  overflow-x: hidden;
+}
+
+// 顶部操作栏
+.top-action-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px;
+  margin: 12px;
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+  &.delete-mode {
+    background-color: #fff8f6;
+  }
+
+  .status-display {
+    font-size: 15px;
+    color: #666;
+
+    .count-highlight {
+      font-size: 18px;
+      font-weight: bold;
+      color: @primary-color;
+      margin: 0 3px;
+    }
+  }
+
+  .action-buttons {
+    display: flex;
+    gap: 8px;
+
+    .cancel-btn {
+      border: 1px solid #dcdee0;
+    }
+
+    .confirm-btn {
+      background: linear-gradient(to right, #ff7062, #df5e4c);
+    }
+  }
+}
+
+.ad-list {
+  flex: 1;
+  overflow-y: auto;
+}
+
+// 广告卡片
+.ad-card {
+  background-color: white;
+  border-radius: 12px;
+  overflow: hidden;
+  margin: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  position: relative;
+
+  &.selectable {
+    cursor: pointer;
+
+    &:hover {
+      background-color: #fafafa;
+    }
+  }
+
+  .card-content {
+    display: flex;
+    padding: 16px;
+    cursor: pointer;
+  }
+
+  .selection-checkbox {
+    position: absolute;
+    top: 16px;
+    right: 16px;
+    z-index: 2;
+  }
+
+  .media-preview {
+    position: relative;
+    width: 100px;
+    height: 100px;
+    border-radius: 8px;
+    overflow: hidden;
+    flex-shrink: 0;
+    margin-right: 12px;
+    cursor: pointer;
+
+    .preview-image {
+      width: 100%;
+      height: 100%;
+      object-fit: cover;
+      background-color: #f0f2f5;
+    }
+
+    .video-indicator {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background-color: rgba(0, 0, 0, 0.2);
+      color: white;
+    }
+  }
+
+  .ad-info {
+    flex: 1;
+    min-width: 0;
+
+    .ad-title {
+      font-size: 15px;
+      font-weight: bold;
+      color: #333;
+      margin-bottom: 10px;
+    }
+
+    .info-row {
+      display: flex;
+      font-size: 13px;
+      color: #666;
+      margin-bottom: 6px;
+      line-height: 1.4;
+
+      .info-label {
+        color: #999;
+        flex-shrink: 0;
+      }
+
+      .info-value {
+        flex: 1;
+      }
+    }
+  }
+}
+
+// 空状态
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 40px 20px;
+  background-color: white;
+  border-radius: 12px;
+
+  .empty-text {
+    color: #999;
+    font-size: 14px;
+    margin: 15px 0 20px;
+  }
+}
+
+// 视频弹窗
+.video-popup {
+  width: 85vw;
+  max-width: 800px;
+
+  .video-player {
+    width: 100%;
+    height: 50vw;
+    max-height: 500px;
+    background-color: #000;
+  }
+}
+
+// 响应式适配
+@media (max-width: 480px) {
+  .top-action-bar {
+    flex-direction: column;
+    align-items: stretch;
+    gap: 12px;
+    padding: 12px;
+
+    .action-buttons {
+      justify-content: space-between;
+    }
+  }
+
+  .ad-card {
+    .card-content {
+      padding: 12px;
+    }
+
+    .media-preview {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 110px;
+      height: 110px;
+    }
+
+    .ad-title {
+      font-size: 14px;
+    }
+
+    .info-row {
+      font-size: 12px;
+    }
+  }
+
+  .video-popup {
+    width: 95vw;
+  }
+}
 </style>

+ 167 - 39
src/views/announcement/index.vue

@@ -2,75 +2,143 @@
   <!-- 公告编辑 -->
   <div class="showGoodsIdx">
     <s-header :name="$t('announcement.header')" :noback="false"></s-header>
-    <van-cell-group inset>
-      <van-field name="radio" label="公司平台">
-        <template #input>
-          <van-radio-group v-model="companyType" direction="horizontal">
-            <van-radio name="0">申泽</van-radio>
-            <van-radio name="1">七云</van-radio>
-          </van-radio-group>
-        </template>
-      </van-field>
-      <van-field v-model="title" label="标题" placeholder="请输入标题" />
-    </van-cell-group>
-    <div style="width: 100%">
-      <TinymceEditor v-model="content" @input="inputContent" />
-    </div>
-    <div class="o-mt-50" style="display: flex; justify-content: center; align-items: center;">
-      <van-button class="btn" round type="primary" @click="sumbitNotice">提交</van-button>
+    <div class="showGoodsIdxBox">
+      <van-cell-group class="headerMenu" inset>
+        <van-field name="radio" label="公司平台">
+          <template #input>
+            <van-radio-group v-model="companyType" direction="horizontal">
+              <van-radio name="0">申泽</van-radio>
+              <van-radio name="1">七云</van-radio>
+            </van-radio-group>
+          </template>
+        </van-field>
+        <van-field v-model="title" label="标题" placeholder="请输入标题" />
+      </van-cell-group>
+      <!-- 替换为WangEditor -->
+      <div class="mobile-editor">
+        <Toolbar
+          class="editor-toolbar"
+          :editor="editorRef"
+          :mode="'simple'"
+          :defaultConfig="toolbarConfig"
+        />
+        <Editor
+          class="editor-content"
+          v-model="contentHtml"
+          :defaultConfig="editorConfig"
+          @onCreated="handleCreated"
+        />
+      </div>
+      <div
+        class="o-mt-50"
+        style="display: flex; justify-content: center; align-items: center"
+      >
+        <van-button class="btn" round type="primary" @click="sumbitNotice"
+          >提交</van-button
+        >
+      </div>
     </div>
   </div>
 </template>
 
 <script>
-import { ref } from "vue";
+import { ref, shallowRef, onBeforeUnmount } from "vue";
 import sHeader from "../../components/SimpleHeader";
-import TinymceEditor from "../../components/TinymceEditor/index";
 import { addNotice } from "@/service/user";
 import { useI18n } from "vue-i18n";
 import { useRouter } from "vue-router";
 import { showFailToast, showSuccessToast } from "vant";
 
+// 引入WangEditor
+import "@wangeditor/editor/dist/css/style.css";
+import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
 
 export default {
-  components: { sHeader, TinymceEditor },
+  components: {
+    sHeader,
+    Editor,
+    Toolbar,
+  },
   setup() {
     const { t } = useI18n();
     const router = useRouter();
     const title = ref("");
-    const content = ref("");
+    const companyType = ref("0");
+
+    // WangEditor相关变量
+    const editorRef = shallowRef();
+    const contentHtml = ref(""); // 替代原来的content
+
+    // 移动端优化配置
+    const toolbarConfig = {
+      excludeKeys: [
+        "group-video",
+        "insertTable",
+        "codeBlock",
+        "blockquote",
+        "group-more-style", // 移除更多样式
+      ],
+      toolbarKeys: [
+        "bold",
+        "italic",
+        "underline",
+        "color",
+        "fontSize",
+        "bulletedList",
+        "numberedList",
+        "undo",
+        "redo",
+      ],
+      modalAppendToBody: false,
+    };
+
+    const editorConfig = {
+      placeholder: "请输入公告内容...",
+      mode: "simple",
+      scroll: true, // 禁用编辑器滚动(由外层容器控制)
+      autofocus: true,
+      minHeight: "350px",
+    };
 
-    const inputContent = (newVal) => {
-      // console.log(newVal)
-      content.value = newVal;
-    }
+    const handleCreated = (editor) => {
+      editorRef.value = editor;
+    };
 
     const sumbitNotice = async () => {
       let addParams = {
-        "title": title.value,
-        "note": content.value,
-        "companyType": companyType.value
+        title: title.value,
+        note: contentHtml.value, // 使用contentHtml替代原来的content
+        companyType: companyType.value,
       };
-      console.log("addParams", addParams)
-      const { data } = await addNotice(addParams)
+      console.log("addParams", addParams);
+      const { data } = await addNotice(addParams);
       if (data.code == "00000") {
-        // console.log(data);
-        showSuccessToast(t('announcement.successfully'));
+        showSuccessToast(t("announcement.successfully"));
         setTimeout(() => {
           router.push("/home");
         }, 1500);
       } else {
-        showFailToast(`${t('announcement.failed')} ${data.message}`);
+        showFailToast(`${t("announcement.failed")} ${data.message}`);
       }
-    }
-    const companyType = ref('0');
-    // 引入语言
+    };
+
+    // 组件销毁时及时销毁编辑器
+    onBeforeUnmount(() => {
+      const editor = editorRef.value;
+      if (editor == null) return;
+      editor.destroy();
+    });
+
     return {
-      inputContent,
       title,
-      content,
+      companyType,
       sumbitNotice,
-      companyType
+      // WangEditor相关
+      editorRef,
+      contentHtml,
+      toolbarConfig,
+      editorConfig,
+      handleCreated,
     };
   },
 };
@@ -79,9 +147,69 @@ export default {
 <style lang="less" scoped>
 @import "../../common/style/common.less";
 
-
 .btn {
   position: relative;
   width: 200px;
 }
+
+.showGoodsIdxBox {
+  background: #f5f6fa;
+  height: calc(100% - 50px);
+  overflow: auto;
+  overflow-x: hidden;
+}
+
+.headerMenu {
+  margin: 12px;
+}
+
+/* 移动端编辑器样式优化 */
+.mobile-editor {
+  margin: 12px;
+  border: 1px solid #e0e0e0;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #fff;
+}
+
+.editor-toolbar {
+  padding: 8px 4px !important;
+  // border-bottom: 1px solid #e0e0e0;
+  // flex-wrap: wrap;
+  // overflow-x: auto;
+  // -webkit-overflow-scrolling: touch;
+
+  /* 适配Vant主题 */
+  background-color: var(--van-gray-1);
+
+  // .w-e-bar-item {
+  //   margin: 2px !important;
+  //   padding: 6px !important;
+  //   border-radius: var(--van-border-radius-sm);
+
+  //   &:active {
+  //     background-color: var(--van-active-color);
+  //   }
+  // }
+}
+
+/* 解决移动端输入法弹起时高度问题 */
+.editor-content {
+  height: 50vh; /* 使用视口高度 */
+  min-height: 350px;
+
+  // -webkit-overflow-scrolling: touch; /* iOS滚动优化 */
+  padding: 12px;
+  font-family: var(--van-font-family);
+  font-size: var(--van-font-size-md);
+  line-height: 1.6;
+  color: var(--van-text-color);
+
+  /* 禁用双指缩放(避免与页面缩放冲突) */
+  touch-action: pan-y;
+
+  /* 文本选择优化 */
+  user-select: text;
+  -webkit-user-select: text;
+}
 </style>

+ 44 - 6
src/views/device/deviceOper.vue

@@ -599,9 +599,7 @@
       </div>
       <!-- 果酱抽取 -->
       <div
-        v-if="
-          device.machineType === '2' && controlList.includes('C30')
-        "
+        v-if="device.machineType === '2' && controlList.includes('C30')"
         class="operation-item"
         @click="jamClk()"
       >
@@ -616,6 +614,24 @@
           <span class="operation-text">{{ $t("remote.C30") }}</span>
         </div>
       </div>
+      <!-- 维护记录 -->
+      <div
+        v-if="(user.type == 0 || user.id == 31 || user.id == 34) && controlList.includes('C31') "
+        class="operation-item"
+        @click="maintenanceClk()"
+      >
+        <div class="icon-wrapper">
+          <img
+            class="operation-icon"
+            src="../../assets/device/operIcon/maintenance.png"
+            alt="maintenance"
+          />
+        </div>
+        <div class="text-wrapper">
+          <span class="operation-text">{{ $t("remote.C31") }}</span>
+        </div>
+      </div>
+      
     </div>
   </van-dialog>
   <van-dialog
@@ -680,7 +696,11 @@ export default {
     const showGoodsClk = () => {
       router.push({
         path: "showGoods",
-        query: { deviceId: device.value.id, name: device.value.name, machineType: device.value.machineType },
+        query: {
+          deviceId: device.value.id,
+          name: device.value.name,
+          machineType: device.value.machineType,
+        },
       });
     };
 
@@ -745,6 +765,17 @@ export default {
         },
       });
     };
+    // 维护记录
+    const maintenanceClk = () => {
+      router.push({
+        path: "maintenance",
+        query: {
+          deviceId: device.value.id,
+          name: device.value.name,
+          clientId: device.value.clientId,
+        },
+      });
+    };
     const { t } = useI18n();
     const user = getLoginUser();
     const router = useRouter();
@@ -842,7 +873,10 @@ export default {
     };
     // 日志功能
     const viewLogs = () => {
-      router.push({ path: "viewLogs", query: { deviceId: device.value.id } });
+      router.push({
+        path: "viewLogs",
+        query: { deviceId: device.value.id, name: device.value.name },
+      });
     };
     // 定制logo
     const customLogo = () => {
@@ -850,7 +884,10 @@ export default {
     };
     // 音量调节
     const modulation = () => {
-      router.push({ path: "modulation", query: { deviceId: device.value.id } });
+      router.push({
+        path: "modulation",
+        query: { deviceId: device.value.id, name: device.value.name },
+      });
     };
     // 远程做糖
     const doSugar = () => {
@@ -1135,6 +1172,7 @@ export default {
       returnCoinClk,
       taxClk,
       jamClk,
+      maintenanceClk,
 
       changePasswordClk,
       sleepIcon,

+ 339 - 91
src/views/device/devicePassword/index.vue

@@ -1,77 +1,133 @@
 <template>
-  <div class="passwordIdx">
-    <s-header :name="$t('device.devicePasswordPage.title')" :noback="false"></s-header>
-    <div class="headerCon kBordBott o-plr-10 o-ptb-16 l-flex-RC">
-      <div class="line o-mr-6"></div>
-      <div>
-        <span class="c-color c-text-14">{{ $t('device.devicePasswordPage.equipmentName') }}:</span>
-        <span class="c-text-color c-text-14">{{ equipmentName }}</span>
+  <div class="password-manager">
+    <s-header :name="$t('device.devicePasswordPage.title')" :noback="false" />
+
+    <!-- 设备信息卡片 -->
+    <div class="device-card">
+      <div class="device-header">
+        <div class="header-indicator"></div>
+        <h3 class="device-name">
+          {{ $t("device.devicePasswordPage.equipmentName") }}:{{
+            equipmentName
+          }}
+        </h3>
       </div>
     </div>
-  </div>
-  <van-config-provider :theme-vars="themeVars">
-    <van-dropdown-menu>
-      <van-dropdown-item v-model="pwdValue" :options="pwdType" />
-    </van-dropdown-menu>
-    <van-form v-if="pwdValue == 0" @submit="onSubmit">
-      <van-cell-group inset>
-        <van-field v-model="adminPwd" type="password" :label="$t('device.devicePasswordPage.deivcePwd')"
-          :placeholder="$t('device.devicePasswordPage.passwordPlaceholder')"
-          :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]" />
-        <van-field v-model="checkAmdinPwd" type="password" :label="$t('device.devicePasswordPage.checkDeivcePwd')"
-          :placeholder="$t('device.devicePasswordPage.passwordCheckRequired')"
-          :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]" />
-      </van-cell-group>
-      <div style="margin: 16px;">
-        <van-button round block type="primary" native-type="submit">
-          {{ $t('device.submitAndPushDeviceUpdates') }}
-        </van-button>
-      </div>
-    </van-form>
-    <van-form v-else @submit="onSubmit">
-      <van-cell-group inset>
-        <van-field v-model="guestPwd" type="password" :label="$t('device.devicePasswordPage.deivcePwd')"
-          :placeholder="$t('device.devicePasswordPage.passwordPlaceholder')"
-          :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]" />
-        <van-field v-model="checkGuestPwd" type="password" :label="$t('device.devicePasswordPage.checkDeivcePwd')"
-          :placeholder="$t('device.devicePasswordPage.passwordCheckRequired')"
-          :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]" />
-      </van-cell-group>
-      <div style="margin: 16px;">
-        <van-button round block type="primary" native-type="submit">
-          {{ $t('device.submitAndPushDeviceUpdates') }}
-        </van-button>
+
+    <!-- 密码类型选择 -->
+    <van-config-provider :theme-vars="themeVars">
+      <div class="password-selector">
+        <van-dropdown-menu :overlay="false">
+          <van-dropdown-item
+            v-model="pwdValue"
+            :options="pwdType"
+            class="modern-dropdown"
+          />
+        </van-dropdown-menu>
+
+        <!-- 密码表单 -->
+        <div class="form-card">
+          <van-form @submit="onSubmit">
+            <van-cell-group inset>
+              <!-- 管理员密码 -->
+              <template v-if="pwdValue === 0">
+                <van-field
+                  v-model="adminPwd"
+                  type="password"
+                  :label="$t('device.devicePasswordPage.deivcePwd')"
+                  :placeholder="
+                    $t('device.devicePasswordPage.passwordPlaceholder')
+                  "
+                  :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]"
+                  class="password-field"
+                >
+                  <template #left-icon>
+                    <van-icon name="lock" class="field-icon" />
+                  </template>
+                </van-field>
+                <van-field
+                  v-model="checkAmdinPwd"
+                  type="password"
+                  :label="$t('device.devicePasswordPage.checkDeivcePwd')"
+                  :placeholder="
+                    $t('device.devicePasswordPage.passwordCheckRequired')
+                  "
+                  :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]"
+                  class="password-field"
+                >
+                  <template #left-icon>
+                    <van-icon name="certificate" class="field-icon" />
+                  </template>
+                </van-field>
+              </template>
+
+              <!-- 访客密码 -->
+              <template v-else>
+                <van-field
+                  v-model="guestPwd"
+                  type="password"
+                  :label="$t('device.devicePasswordPage.deivcePwd')"
+                  :placeholder="
+                    $t('device.devicePasswordPage.passwordPlaceholder')
+                  "
+                  :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]"
+                  class="password-field"
+                >
+                  <template #left-icon>
+                    <van-icon name="lock" class="field-icon" />
+                  </template>
+                </van-field>
+                <van-field
+                  v-model="checkGuestPwd"
+                  type="password"
+                  :label="$t('device.devicePasswordPage.checkDeivcePwd')"
+                  :placeholder="
+                    $t('device.devicePasswordPage.passwordCheckRequired')
+                  "
+                  :rules="[{ validator, message: $t('device.devicePasswordPage.passwordPlaceholder') }]"
+                  class="password-field"
+                >
+                  <template #left-icon>
+                    <van-icon name="certificate" class="field-icon" />
+                  </template>
+                </van-field>
+              </template>
+            </van-cell-group>
+
+            <!-- 提交按钮 -->
+            <div class="submit-section">
+              <van-button
+                round
+                block
+                type="primary"
+                native-type="submit"
+                class="submit-btn"
+              >
+                <template #loading>
+                  <van-loading type="spinner" size="20px" />
+                </template>
+                {{ $t("device.submitAndPushDeviceUpdates") }}
+              </van-button>
+            </div>
+          </van-form>
+        </div>
       </div>
-    </van-form>
-  </van-config-provider>
+    </van-config-provider>
+  </div>
 </template>
 
 <script>
 // 导入接口
-import {
-  updateDevicePassword
-} from '@/service/device/index';
+import { updateDevicePassword } from "@/service/device/index";
 import sHeader from "@/components/SimpleHeader";
-import {
-  ref,
-  reactive
-} from "@vue/reactivity";
-import {
-  onMounted
-} from '@vue/runtime-core';
-import {
-  useRoute, useRouter
-} from 'vue-router';
-import {
-  showNotify, showDialog, showConfirmDialog, showFailToast
-} from 'vant';
-import {
-  useI18n
-} from "vue-i18n";
-import { styleUrl } from '../../../common/js/utils';
+import { ref, reactive } from "@vue/reactivity";
+import { onMounted } from "@vue/runtime-core";
+import { useRoute, useRouter } from "vue-router";
+import { showNotify, showDialog, showConfirmDialog, showFailToast } from "vant";
+import { useI18n } from "vue-i18n";
 export default {
   components: {
-    sHeader
+    sHeader,
   },
   setup() {
     // 引入语言
@@ -80,7 +136,7 @@ export default {
     const route = useRoute();
     const router = useRouter();
     // 设备名称
-    const equipmentName = ref('');
+    const equipmentName = ref("");
     const pwdValue = ref();
     const adminPwd = ref();
     const checkAmdinPwd = ref();
@@ -89,21 +145,21 @@ export default {
 
     const validator = (val) => /\w{6,}/.test(val);
     const themeVars = {
-      dropdownMenuTitleTextColor: '#404d74'
+      dropdownMenuTitleTextColor: "#404d74",
     };
-    const pwdType = [{
-      text: t('device.devicePasswordPage.adminPassword'),
-      value: 0
-    },
-    {
-      text: t('device.devicePasswordPage.guestPassword'),
-      value: 1
-    },
-    ]
+    const pwdType = [
+      {
+        text: t("device.devicePasswordPage.adminPassword"),
+        value: 0,
+      },
+      {
+        text: t("device.devicePasswordPage.guestPassword"),
+        value: 1,
+      },
+    ];
     // 刚进页面
     onMounted(() => {
       // 加载样式
-      styleUrl('device');
       pwdValue.value = pwdType[0].value;
       const id = route.query.deviceId || "";
       const name = route.query.name || "";
@@ -111,13 +167,12 @@ export default {
         passwordForm.equipmentId = id;
         equipmentName.value = name;
       }
-
     });
     // 修改的价格
     const passwordForm = reactive({
-      equipmentId: '',
-      adminPwd: '',
-      guestPwd: ''
+      equipmentId: "",
+      adminPwd: "",
+      guestPwd: "",
     });
 
     // 点击提交
@@ -125,10 +180,13 @@ export default {
       passwordForm.adminPwd = adminPwd.value;
       passwordForm.guestPwd = guestPwd.value;
       console.log(passwordForm);
-      if ((passwordForm.adminPwd != checkAmdinPwd.value) || (passwordForm.guestPwd != checkGuestPwd.value)) {
+      if (
+        passwordForm.adminPwd != checkAmdinPwd.value ||
+        passwordForm.guestPwd != checkGuestPwd.value
+      ) {
         showNotify({
-          type: 'danger',
-          message: t('device.devicePasswordPage.passwordMatch')
+          type: "danger",
+          message: t("device.devicePasswordPage.passwordMatch"),
         });
         return;
       }
@@ -140,27 +198,27 @@ export default {
       }
       showConfirmDialog({
         // title: "提示",
-        message: t('device.editCheck'),
+        message: t("device.editCheck"),
       }).then(() => {
         console.log(passwordForm);
         updateDevicePassword({
-          "equipmentId": passwordForm.equipmentId,
-          "adminPwd": passwordForm.adminPwd,
-          "guestPwd": passwordForm.guestPwd
+          equipmentId: passwordForm.equipmentId,
+          adminPwd: passwordForm.adminPwd,
+          guestPwd: passwordForm.guestPwd,
         }).then((res) => {
           if (res.data.code == "A0001") {
-            showFailToast(t('device.unknownError'));
+            showFailToast(t("device.unknownError"));
             return;
           }
           showDialog({
-            message: t('device.modificationSucceeded'),
+            message: t("device.modificationSucceeded"),
           }).then(() => {
             //返回上一页
             router.go(-1);
           });
         });
       });
-    }
+    };
 
     return {
       passwordForm,
@@ -173,10 +231,200 @@ export default {
       checkGuestPwd,
       onSubmit,
       validator,
-      themeVars
+      themeVars,
     };
   },
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+@primary-color: #4d6add;
+@card-bg: #ffffff;
+@text-primary: #2d3436;
+@border-color: #e4e7ec;
+@title-color: #404d74;
+@active-color: #4d6add;
+@item-padding: 16px 24px;
+@card-shadow: 0 4px 20px rgba(77, 106, 221, 0.1);
+
+.password-manager {
+  background: #f8fafb;
+  min-height: 100vh;
+}
+
+.device-card {
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  margin: 10px;
+  padding: 16px;
+
+  .device-header {
+    display: flex;
+    align-items: center;
+
+    .header-indicator {
+      width: 3px;
+      height: 20px;
+      background: @primary-color;
+      margin-right: 12px;
+      border-radius: 2px;
+    }
+
+    .device-name {
+      font-size: 15px;
+      color: #404d74;
+      margin: 0;
+    }
+  }
+}
+
+.password-selector {
+  margin: 10px;
+
+  .modern-dropdown {
+    :deep(.van-dropdown-menu__bar) {
+      background: @card-bg;
+      box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+      overflow: hidden;
+
+      :deep(.van-dropdown-menu__title) {
+        display: flex !important;
+        align-items: center;
+        justify-content: space-between;
+        color: @title-color;
+        font-size: 10px;
+        font-weight: 500;
+        transition: all 0.5s;
+        width: auto !important;
+
+        &::after {
+          content: "";
+          position: relative !important;
+          right: 0 !important;
+          top: 0 !important;
+          transform: none !important;
+          display: inline-block;
+          width: 0;
+          height: 0;
+          border: 5px solid;
+          border-color: @title-color transparent transparent;
+          margin-left: 8px;
+          margin-top: 3px;
+          order: 1;
+          transition: transform 0.5s;
+        }
+
+        &--active {
+          color: @active-color;
+
+          &::after {
+            border-color: @active-color transparent transparent;
+            transform: rotate(-180deg) translateY(2px) !important;
+          }
+        }
+      }
+    }
+  }
+  :deep(.van-dropdown-menu__item) {
+    flex: 1;
+    min-width: 82px;
+    position: relative;
+    padding: 8px 0px;
+
+    &:not(:last-child)::after {
+      content: "";
+      position: absolute;
+      right: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 1px;
+      height: 24px;
+      background: #eef0fa;
+    }
+  }
+
+  :deep(.van-dropdown-item) {
+    margin: 10px;
+
+    &__content {
+      left: 0 !important;
+      right: 0 !important;
+      transform: none !important;
+      border-radius: 12px;
+      box-shadow: 0 4px 20px rgba(77, 106, 221, 0.1);
+      border: 0;
+      max-height: 60vh;
+      z-index: 9999;
+      /* 确保层级 */
+    }
+  }
+}
+
+.form-card {
+  background: @card-bg;
+  border-radius: 0 0 12px 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  padding: 10px;
+
+  .password-field {
+    :deep(.van-field__label) {
+      font-weight: 500;
+    }
+
+    :deep(.van-cell) {
+      background: #f8fafb;
+      border-radius: 8px;
+      // margin: 12px 0;
+      transition: all 0.3s;
+
+      &:focus-within {
+        box-shadow: 0 2px 8px rgba(77, 194, 148, 0.2);
+      }
+    }
+
+    .field-icon {
+      color: @primary-color;
+      margin-right: 12px;
+      font-size: 18px;
+    }
+  }
+
+  .submit-section {
+    margin-top: 32px;
+
+    .submit-btn {
+      height: 48px;
+      font-size: 16px;
+      font-weight: 500;
+      background: linear-gradient(135deg, @primary-color 0%, #3daa80 100%);
+      border: none;
+      transition: transform 0.2s;
+
+      &:active {
+        transform: scale(0.98);
+      }
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .form-card {
+    padding: 10px;
+
+    .submit-btn {
+      height: 44px !important;
+      font-size: 14px;
+    }
+  }
+
+  :deep(.van-dropdown-menu__title) {
+    padding: 2px 4px !important;
+    font-size: 12px;
+
+    &::after {
+      margin-right: -5px;
+    }
+  }
+}
+</style>

+ 394 - 146
src/views/device/doSugar.vue

@@ -1,104 +1,176 @@
 <template>
-  <!-- 远程做糖 -->
-  <div class="sugarPage flex-col">
-    <s-header :name="$t('device.remoteSugarMaking')" :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>
-      <div v-if="machineType === null || machineType === '0'">
-        <van-field v-model="fieldValue" is-link readonly :label="$t('device.clickToSelectPattern')"
-          :placeholder="$t('device.pleaseSelectAPattern')" @click="show = true" />
-        <van-popup v-model:show="show" round position="bottom">
-          <van-cascader v-model="cascaderValue" :title="$t('device.pleaseSelectAPattern')" :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.text }}</div>
-              </div>
-            </template>
-          </van-cascader>
-        </van-popup>
-        <div class="textRow o-pr-20">
-          <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>
-        <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 class="sugar-maker">
+    <s-header :name="$t('device.remoteSugarMaking')" :noback="false" />
+
+    <!-- 设备信息卡片 -->
+    <div class="device-card">
+      <div class="device-header">
+        <div class="header-indicator"></div>
+        <h3 class="device-name">
+          {{ $t("device.equipmentName") }}:{{ deviceDetal?.name || "-" }}
+        </h3>
       </div>
-      <div v-if="machineType === '1'">
-        <van-field v-model="fieldValue" is-link readonly :label="$t('device.clickToSelectTaste')"
-          :placeholder="$t('device.pleaseSelectTaste')" @click="show = true" />
+    </div>
+
+    <!-- 主操作区 -->
+    <div class="operation-card">
+      <!-- 动态选择器 -->
+      <div v-if="[null, '0', '1'].includes(machineType)" class="selector-card">
+        <van-field
+          v-model="fieldValue"
+          is-link
+          readonly
+          :label="
+            machineType === '1'
+              ? $t('device.clickToSelectTaste')
+              : $t('device.clickToSelectPattern')
+          "
+          :placeholder="
+            machineType === '1'
+              ? $t('device.pleaseSelectTaste')
+              : $t('device.pleaseSelectAPattern')
+          "
+          @click="show = true"
+          class="smart-field"
+        />
         <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">
+          <van-cascader
+            v-model="cascaderValue"
+            :title="
+              machineType === '1'
+                ? $t('device.pleaseSelectTaste')
+                : $t('device.pleaseSelectAPattern')
+            "
+            :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.text }}</div>
+                <van-image
+                  :src="option.imgUrl"
+                  width="55px"
+                  height="55px"
+                  class="option-image"
+                >
+                  <template v-if="!option.imgUrl" #error>
+                    <van-icon name="photo" size="24" />
+                  </template>
+                </van-image>
+                <div class="option-label">{{ option.text }}</div>
               </div>
             </template>
           </van-cascader>
         </van-popup>
-        <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" 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'">
+
+      <!-- 复合材料选择(类型2) -->
+      <div v-if="machineType === '2'" class="material-selector">
         <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
+            :title="$t('device.jam')"
+            name="1"
+            class="material-section"
+          >
+            <div class="grid-container">
+              <div
+                v-for="(item, index) in jamData"
+                :key="`jam-${index}`"
+                class="material-item"
+              >
+                <van-image
+                  :src="showPopPhoto(item)"
+                  width="50"
+                  height="50"
+                  fit="contain"
+                />
+                <span class="material-name">{{ item.name }}</span>
+                <van-checkbox
+                  v-model="item.checked"
+                  shape="square"
+                  class="material-checkbox"
+                />
+              </div>
+            </div>
           </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
+            :title="$t('device.nuts​')"
+            name="2"
+            class="material-section"
+          >
+            <div class="grid-container">
+              <div
+                v-for="(item, index) in crushData"
+                :key="`nut-${index}`"
+                class="material-item"
+              >
+                <van-image
+                  :src="showPopPhoto(item)"
+                  width="50"
+                  height="50"
+                  fit="contain"
+                />
+                <span class="material-name">{{ item.name }}</span>
+                <van-checkbox
+                  v-model="item.checked"
+                  shape="square"
+                  class="material-checkbox"
+                />
+              </div>
+            </div>
           </van-collapse-item>
         </van-collapse>
-        <br>
-        <div class="textRow o-pr-20">
-          <span @click="pushToDaySugarList">{{ $t('device.todaysMakeList') }}>></span>
+      </div>
+
+      <!-- 公共操作区 -->
+      <div class="action-area">
+        <div class="quick-link" @click="pushToDaySugarList">
+          {{
+            $t(
+              machineType === "2"
+                ? "device.todaysMakeList"
+                : "device.todaysSugarList"
+            )
+          }}
+          <van-icon name="arrow" />
         </div>
-        <div v-if="!doSugartData" class="block5 flex-col" @click="submitDoSugar"><span class="txt3">{{
-      $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>
+
+        <van-button
+          round
+          :type="doSugartData ? 'success' : 'primary'"
+          class="submit-btn"
+          :disabled="doSugartType"
+          @click="doSugartData ? checkData() : submitDoSugar()"
+        >
+          <van-icon :name="doSugartData ? 'eye' : 'play-circle'" />
+          {{
+            $t(doSugartData ? "device.viewResults" : "device.submitToMakeSugar")
+          }}
+        </van-button>
       </div>
     </div>
   </div>
 </template>
 <script>
-import { onMounted, ref } from 'vue';
+import { onMounted, ref } from "vue";
 import sHeader from "@/components/SimpleHeader";
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute, useRouter } from "vue-router";
 import {
-  getDeviceDetal, selectProducts,
+  getDeviceDetal,
+  selectProducts,
   remoteProduction,
-  selectSugarStatus
-} from '@/service/device'
-import { showConfirmDialog, showFailToast, showSuccessToast, showToast } from 'vant';
-import { useI18n } from 'vue-i18n';
+  selectSugarStatus,
+} from "@/service/device";
+import {
+  showConfirmDialog,
+  showFailToast,
+  showSuccessToast,
+  showToast,
+} from "vant";
+import { useI18n } from "vue-i18n";
 import { styleUrl } from "../../common/js/utils";
 
 export default {
@@ -110,11 +182,11 @@ export default {
     const machineType = route.query.machineType;
     const deviceDetal = ref(null);
     const show = ref(false);
-    const fieldValue = ref('');
-    const cascaderValue = ref('');
+    const fieldValue = ref("");
+    const cascaderValue = ref("");
     const options = ref([]);
-    const activeNames = ref(['1', '2']);
-    const productNo = ref('');
+    const activeNames = ref(["1", "2"]);
+    const productNo = ref("");
 
     // 果酱数据
     const iceName = ref();
@@ -130,17 +202,17 @@ export default {
       return imgId;
     };
     const onFinish = ({ selectedOptions }) => {
-      console.log('onFinish', selectedOptions);
+      console.log("onFinish", selectedOptions);
       show.value = false;
       fieldValue.value = selectedOptions[0].text;
       productNo.value = selectedOptions[0].value;
-    }
+    };
     const doSugartData = ref(null);
-    const doSugartType = ref(true);
+    const doSugartType = ref(false);
 
     // 初始化页面获取列表
     onMounted(async () => {
-      styleUrl('doSugar')
+      styleUrl("doSugar");
       getDeviceDetalFun();
     });
 
@@ -151,94 +223,98 @@ export default {
     // 获取设备列表数据
     const getDeviceDetalFun = async () => {
       const { data } = await getDeviceDetal({ id: deviceId });
-      if (data.code === '00000') {
+      if (data.code === "00000") {
         deviceDetal.value = data.data;
         getProduct();
-      } else { showFailToast(data.message); }
-    }
+      } else {
+        showFailToast(data.message);
+      }
+    };
     // 获取花形下拉列表
     const getProduct = async () => {
       const { data } = await selectProducts({ equipmentId: deviceId });
       if (data.code) {
-        if (machineType != '2') {
-          options.value = data.data.map(item => {
+        if (machineType != "2") {
+          options.value = data.data.map((item) => {
             return {
               text: item.productName,
               value: item.no,
               imgUrl: showSugarPhoto(item.no),
             };
-          })
+          });
           console.log(options.value);
         } else {
-          data.data.forEach(item => {
-            if (item.no.includes('J01')) {
+          data.data.forEach((item) => {
+            if (item.no.includes("J01")) {
               // 果酱1
               jamData.value.push({
                 name: item.productName,
                 no: item.no,
                 value: 1,
-                checked: false
+                checked: false,
               });
-            } else if (item.no.includes('J02')) {
+            } else if (item.no.includes("J02")) {
               // 果酱2
               jamData.value.push({
                 name: item.productName,
                 no: item.no,
                 value: 2,
-                checked: false
+                checked: false,
               });
-            } else if (item.no.includes('J03')) {
+            } else if (item.no.includes("J03")) {
               // 果酱3
               jamData.value.push({
                 name: item.productName,
                 no: item.no,
                 value: 3,
-                checked: false
+                checked: false,
               });
-            } else if (item.no.includes('C01')) {
+            } else if (item.no.includes("C01")) {
               // 果碎1
               crushData.value.push({
                 name: item.productName,
                 no: item.no,
                 value: 1,
-                checked: false
+                checked: false,
               });
-            } else if (item.no.includes('C02')) {
+            } else if (item.no.includes("C02")) {
               // 果碎2
               crushData.value.push({
                 name: item.productName,
                 no: item.no,
                 value: 2,
-                checked: false
+                checked: false,
               });
-            } else if (item.no.includes('I01')) {
+            } else if (item.no.includes("I01")) {
               // 雪糕
               iceName.value = item.productName;
             }
-          })
+          });
         }
-      } else { showFailToast(data.message); }
-    }
+      } else {
+        showFailToast(data.message);
+      }
+    };
     const submitDoSugar = async () => {
       const makeCodes = ref([1, 0, 0]);
       doSugartData.value = null;
       doSugartType.value = true;
 
-      if (machineType == '2') {
-        fieldValue.value = '';
+      if (machineType == "2") {
+        fieldValue.value = "";
         let jamCount = 0;
         let crushCount = 0;
         // 如果是冰淇淋机器
-        jamData.value.forEach(item => {
+        jamData.value.forEach((item) => {
           if (item.checked) {
-            fieldValue.value += item.name + ',';
+            fieldValue.value += item.name + ",";
             makeCodes.value[1] += item.value;
             jamCount++;
           }
         });
-        crushData.value.forEach(item => {
+        crushData.value.forEach((item) => {
           if (item.checked) {
-            fieldValue.value += item.name + ',';
+            fieldValue.value += item.name + ",";
             makeCodes.value[2] += item.value;
             crushCount++;
           }
@@ -249,51 +325,61 @@ export default {
         if (crushCount > 1) {
           makeCodes.value[2] += 1;
         }
-        if (fieldValue.value === '') {
+        if (fieldValue.value === "") {
           fieldValue.value = iceName.value;
         } else {
-          fieldValue.value = fieldValue.value.substring(0, fieldValue.value.length - 1);
+          fieldValue.value = fieldValue.value.substring(
+            0,
+            fieldValue.value.length - 1
+          );
           fieldValue.value = iceName.value + "(" + fieldValue.value + ")";
         }
       }
-      if (fieldValue.value === '') { showFailToast(t('device.pleaseSelectAPattern')); return; }
+      if (fieldValue.value === "") {
+        showFailToast(t("device.pleaseSelectAPattern"));
+        return;
+      }
       showConfirmDialog({
-        title: t('user.tips'),
-        message: t('device.confirmMake')+ fieldValue.value +'?',
-      }).then(async() => {
-        const { data } = await remoteProduction({ 
-          equipmentId: deviceId, 
-          productName: fieldValue.value, 
-          makeCodes: makeCodes.value.join(','),
-          productNo: productNo.value
-         });
-        if (data.code == "00000") {
-          doSugartData.value = data.data;
-          setTimeout(() => {
-            doSugartType.value = false;
-          }, 5000);
-        } else { showFailToast(data.message); }
-      }).catch((error) => {
-        console.error(error);
+        title: t("user.tips"),
+        message: t("device.confirmMake") + fieldValue.value + "?",
       })
-    }
+        .then(async () => {
+          const { data } = await remoteProduction({
+            equipmentId: deviceId,
+            productName: fieldValue.value,
+            makeCodes: makeCodes.value.join(","),
+            productNo: productNo.value,
+          });
+          if (data.code == "00000") {
+            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 });
       if (data.code) {
-        if (data.data == '1') {
-          showToast(t('device.receiveInstruction'));
+        if (data.data == "1") {
+          showToast(t("device.receiveInstruction"));
         } else {
           showSuccessToast(data.message);
         }
         doSugartData.value = null;
         doSugartType.value = true;
       } else {
-        if (data.data == '0') {
-          showToast(t('device.notUploadData'));
-        } else if (data.data == '2') {
-          showToast(t('device.machineException'));
-        } else if (data.data == '3') {
-          showToast(t('device.netException'));
+        if (data.data == "0") {
+          showToast(t("device.notUploadData"));
+        } else if (data.data == "2") {
+          showToast(t("device.machineException"));
+        } else if (data.data == "3") {
+          showToast(t("device.netException"));
         } else {
           showToast(data.message);
         }
@@ -301,8 +387,11 @@ export default {
     };
 
     const pushToDaySugarList = async () => {
-      router.push({ path: 'toDaySugarList', query: { deviceId: deviceId, clientId: deviceDetal.value.clientId } })
-    }
+      router.push({
+        path: "toDaySugarList",
+        query: { deviceId: deviceId, clientId: deviceDetal.value.clientId },
+      });
+    };
 
     return {
       deviceDetal,
@@ -321,12 +410,171 @@ export default {
       activeNames,
       jamData,
       crushData,
-      showPopPhoto
+      showPopPhoto,
     };
   },
   components: { sHeader },
 };
 </script>
 <style lang="less" scoped>
-@import "../../common/style/common";
+@primary-color: #4d6add;
+@card-bg: #ffffff;
+@text-primary: #2d3436;
+@border-color: #e4e7ec;
+
+.sugar-maker {
+  background: #f8fafb;
+  min-height: 100vh;
+}
+
+.device-card {
+  background: @card-bg;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  margin: 10px;
+  padding: 16px;
+
+  .device-header {
+    display: flex;
+    align-items: center;
+
+    .header-indicator {
+      width: 3px;
+      height: 20px;
+      background: @primary-color;
+      margin-right: 12px;
+      border-radius: 2px;
+    }
+
+    .device-name {
+      margin: 0;
+      font-size: 15px;
+      color: #404d74;
+      font-weight: 550;
+    }
+  }
+}
+
+.operation-card {
+  background: @card-bg;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  padding: 10px;
+  margin: 10px;
+
+  .selector-card {
+    margin-bottom: 24px;
+
+    .smart-field {
+      :deep(.van-field__label) {
+        color: @primary-color;
+        font-weight: 500;
+      }
+    }
+
+    .cascader-item {
+      padding: 5px;
+      display: flex;
+      align-items: center;
+
+      .option-image {
+        border-radius: 8px;
+        margin-right: 15px;
+      }
+
+      .option-label {
+        font-size: 14px;
+        color: @text-primary;
+      }
+    }
+  }
+
+  .material-selector {
+    .material-section {
+      :deep(.van-collapse-item__title) {
+        font-weight: bold;
+        color: #404d74;
+      }
+    }
+
+    .grid-container {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 12px;
+    }
+
+    .material-item {
+      display: flex;
+      align-items: center;
+      padding: 8px;
+      background: #f8fafb;
+      border-radius: 8px;
+
+      .material-name {
+        flex: 1;
+        margin: 0 12px;
+        font-size: 13px;
+        .ellipsis();
+      }
+
+      .material-checkbox {
+        :deep(.van-checkbox__icon) {
+          border-color: @primary-color;
+
+          &.van-checkbox__icon--checked {
+            background: @primary-color;
+          }
+        }
+      }
+    }
+  }
+
+  .action-area {
+    margin-top: 24px;
+
+    .quick-link {
+      color: @primary-color;
+      font-size: 13px;
+      text-align: right;
+      margin-bottom: 16px;
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+
+      .van-icon {
+        margin-left: 4px;
+      }
+    }
+
+    .submit-btn {
+      width: 100%;
+      height: 48px;
+      font-size: 16px;
+
+      .van-icon {
+        margin-right: 8px;
+      }
+    }
+  }
+}
+
+.ellipsis() {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+@media (max-width: 480px) {
+  .material-item {
+    padding: 6px !important;
+
+    .material-name {
+      font-size: 12px !important;
+    }
+  }
+}
+
+:deep(.van-image__img) {
+  object-fit: contain !important;
+}
 </style>

+ 471 - 0
src/views/device/maintenance/add.vue

@@ -0,0 +1,471 @@
+<template>
+  <div class="maintenance-form">
+    <s-header :name="$t('maintenance.title')" :noback="true" />
+    <div class="content-container">
+      <van-form @submit="onSubmit" class="form-container">
+        <!-- 设备信息 -->
+        <van-cell-group :title="$t('maintenance.form.deviceInfo')" inset>
+          <van-field
+            v-model="formData.clientId"
+            readonly
+            :label="$t('maintenance.form.deviceId')"
+            :rules="[
+              {
+                required: true,
+                message: $t('maintenance.validation.deviceIdRequired'),
+              },
+            ]"
+          />
+        </van-cell-group>
+
+        <!-- 维护人员信息 -->
+        <van-cell-group :title="$t('maintenance.form.staffInfo')" inset>
+          <van-field
+            v-model="formData.managerId"
+            :label="$t('maintenance.form.staffId')"
+            :placeholder="$t('maintenance.form.staffIdPlaceholder')"
+            type="number"
+            required
+            :rules="[
+              {
+                required: true,
+                message: $t('maintenance.validation.staffIdRequired'),
+              },
+            ]"
+          />
+          <van-field
+            v-model="formData.name"
+            :label="$t('maintenance.form.name')"
+            required
+            :placeholder="$t('maintenance.form.namePlaceholder')"
+            :rules="[
+              {
+                required: true,
+                message: $t('maintenance.validation.nameRequired'),
+              },
+            ]"
+          />
+        </van-cell-group>
+
+        <!-- 维护详情 -->
+        <van-cell-group :title="$t('maintenance.form.detail')" inset>
+          <van-field
+            readonly
+            is-link
+            required
+            v-model="typeText"
+            name="maintenanceType"
+            :label="$t('maintenance.form.type')"
+            :placeholder="$t('maintenance.form.typePlaceholder')"
+            :rules="[
+              {
+                validator: validateType,
+                message: $t('maintenance.validation.typeRequired'),
+              },
+            ]"
+            @click="showType = true"
+          />
+          <van-popup v-model:show="showType" position="bottom" round>
+            <van-checkbox-group v-model="typeValue" ref="checkboxGroup">
+              <van-cell-group>
+                <van-cell
+                  v-for="(item, index) in maintenanceTypes"
+                  :key="index"
+                  clickable
+                  :title="item.text"
+                  @click="toggle(index)"
+                >
+                  <template #right-icon>
+                    <van-checkbox
+                      :name="item.value"
+                      @click.stop
+                      shape="square"
+                    />
+                  </template>
+                </van-cell>
+              </van-cell-group>
+            </van-checkbox-group>
+            <div class="popup-footer">
+              <van-button size="small" type="primary" @click="confirmType">{{
+                $t("maintenance.form.confirm")
+              }}</van-button>
+            </div>
+          </van-popup>
+
+          <van-field
+            v-model="dateText"
+            required
+            is-link
+            readonly
+            name="datePicker"
+            :label="$t('maintenance.form.date')"
+            :placeholder="$t('maintenance.form.datePlaceholder')"
+            @click="showDate = true"
+          />
+          <van-popup v-model:show="showDate" destroy-on-close position="bottom">
+            <van-date-picker
+              v-model="dateValue"
+              :min-date="minDate"
+              :max-date="maxDate"
+              @confirm="onDateConfirm"
+              @cancel="showDate = false"
+            />
+          </van-popup>
+
+          <van-field readonly required :label="$t('maintenance.form.time')">
+            <template #input>
+              <van-field
+                readonly
+                required
+                v-model="startTime"
+                :placeholder="$t('maintenance.form.startTimePlaceholder')"
+                :rules="[
+                  {
+                    required: true,
+                    message: $t('maintenance.validation.startTimeRequired'),
+                  },
+                ]"
+                @click="showStartTime = true"
+              />
+              <span class="separator">{{ $t("maintenance.form.to") }}</span>
+              <van-field
+                readonly
+                required
+                v-model="endTime"
+                :placeholder="$t('maintenance.form.endTimePlaceholder')"
+                :rules="[
+                  {
+                    required: true,
+                    message: $t('maintenance.validation.endTimeRequired'),
+                  },
+                ]"
+                @click="showEndTime = true"
+              />
+            </template>
+          </van-field>
+          <van-popup v-model:show="showStartTime" position="bottom">
+            <van-time-picker
+              :title="$t('maintenance.form.selectTime')"
+              @confirm="confirmStartTime"
+              @cancel="showStartTime = false"
+            />
+          </van-popup>
+          <van-popup v-model:show="showEndTime" position="bottom">
+            <van-time-picker
+              :title="$t('maintenance.form.selectTime')"
+              @confirm="confirmEndTime"
+              @cancel="showEndTime = false"
+            />
+          </van-popup>
+
+          <van-field
+            v-model="formData.consumption"
+            :label="$t('maintenance.form.consumables')"
+            type="textarea"
+            autosize
+            :placeholder="$t('maintenance.form.consumablesPlaceholder')"
+            clearable
+          />
+
+          <van-field
+            v-model="formData.description"
+            :label="$t('maintenance.form.description')"
+            :placeholder="$t('maintenance.form.descriptionPlaceholder')"
+            type="textarea"
+            autosize
+            clearable
+          />
+        </van-cell-group>
+
+        <!-- 图片上传 -->
+        <van-cell-group :title="$t('maintenance.form.images')" inset>
+          <van-uploader
+            v-model="fileList"
+            multiple
+            :max-count="5"
+            :after-read="handleFileUpload"
+            :before-delete="handleFileDelete"
+          />
+        </van-cell-group>
+
+        <!-- 问题状态 -->
+        <van-cell-group :title="$t('maintenance.form.status')" inset>
+          <van-cell>
+            <van-radio-group v-model="formData.status" direction="horizontal">
+              <van-radio :name="1">{{ $t('maintenance.status.resolved') }}</van-radio>
+              <van-radio :name="2">{{ $t('maintenance.status.unresolved') }}</van-radio>
+            </van-radio-group>
+          </van-cell>
+        </van-cell-group>
+        <!-- 提交按钮 -->
+        <div class="submit-btn">
+          <van-button round block type="primary" native-type="submit">
+            {{ $t('maintenance.form.submit') }}
+          </van-button>
+        </div>
+      </van-form>
+    </div>
+  </div>
+</template>
+
+<script>
+import { ref, reactive, computed, onMounted } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import sHeader from "@/components/SimpleHeader";
+import { uploadPic, save } from "@/service/maintenance/index";
+import { showToast, showFailToast } from "vant";
+import { useI18n } from "vue-i18n";
+
+export default {
+  components: { sHeader },
+  setup() {
+    const { t } = useI18n();
+    const formData = reactive({
+      clientId: "",
+      managerId: "",
+      name: "",
+      type: "",
+      startTime: "",
+      endTime: "",
+      consumption: "",
+      description: "",
+      pic: "",
+      status: 1,
+    });
+
+    const maintenanceTypes = ref([
+    { text: t('maintenance.type.cleaning'), value: "1", checked: false },
+      { text: t('maintenance.type.refill'), value: "2", checked: false },
+      { text: t('maintenance.type.repair'), value: "3", checked: false },
+    ]);
+
+    const route = useRoute();
+    const router = useRouter();
+    const showType = ref(false);
+    const typeValue = ref([]);
+    const typeText = computed(() => {
+      return maintenanceTypes.value
+        .filter((item) => typeValue.value.includes(item.value))
+        .map((item) => item.text)
+        .join(",");
+    });
+    const pics = ref([]);
+    const fileList = ref([]);
+    const showDate = ref(false);
+    const maxDate = ref(new Date());
+    const currentDate = ref(new Date());
+
+    const minDate = ref(
+      new Date(currentDate.value.getTime() - 36 * 1000 * 24 * 365)
+    );
+    const showStartTime = ref(false);
+    const showEndTime = ref(false);
+    const dateValue = ref([
+      currentDate.value.getFullYear(),
+      currentDate.value.getMonth() + 1,
+      currentDate.value.getDate(),
+    ]);
+    const dateText = computed(() => {
+      const [year, month, day] = dateValue.value;
+      return `${year}-${month}-${day}`;
+    });
+    const startTime = ref("");
+    const endTime = ref("");
+
+    onMounted(() => {
+      formData.clientId = route.query.clientId;
+    });
+
+    const onConfirmType = () => {
+      showType.value = false;
+    };
+
+    // 处理时间选择
+    const onDateConfirm = ({ selectedValues }) => {
+      dateValue.value = selectedValues;
+      showDate.value = false;
+    };
+
+    const confirmStartTime = ({ selectedValues }) => {
+      startTime.value = selectedValues.join(":");
+      showStartTime.value = false;
+    };
+
+    const confirmEndTime = ({ selectedValues }) => {
+      endTime.value = selectedValues.join(":");
+      showEndTime.value = false;
+    };
+
+    // 文件处理
+    const handleFileUpload = async (file) => {
+      // 此处添加文件上传逻辑
+      try {
+        const fileData = new FormData();
+        fileData.append("file", file.file);
+        const { data } = await uploadPic(fileData);
+        if (data.code === "00000") {
+          pics.value.push(data.data);
+          const tempFile = data.data.split("/pic/");
+          fileList.value[fileList.value.length - 1].tempName = tempFile[1];
+        }
+      } catch (error) {
+        console.log(error);
+      }
+    };
+
+    const handleFileDelete = (file) => {
+      pics.value = pics.value.filter(
+        (item) => item.split("/pic/")[1] !== file.tempName
+      );
+      // 删除文件处理
+      return true;
+    };
+
+    // 提交表单
+    const onSubmit = async () => {
+      formData.startTime = new Date(dateText.value + " " + startTime.value);
+      formData.endTime = new Date(dateText.value + " " + endTime.value);
+      if (formData.startTime.getTime() >= formData.endTime.getTime()) {
+        showToast(t('maintenance.validation.timeRangeInvalid'));
+        return;
+      }
+      formData.type = JSON.stringify(typeValue.value);
+      formData.pic = JSON.stringify(pics.value);
+      console.log(formData);
+      try {
+        const { data } = await save(formData);
+        if (data.code === "00000") {
+          router.push("/addMaintenanceSuccess");
+        } else if (data.code === "A0001" || data.code === "A0002") {
+          showFailToast(t('maintenance.validation.staffNotFound'));
+        } else {
+          showFailToast(data.message);
+        }
+      } catch (error) {
+        console.log(error);
+      }
+      // 此处添加提交逻辑
+    };
+
+    // 切换选择方法
+    const toggle = (index) => {
+      const value = maintenanceTypes.value[index].value;
+      const currentIndex = typeValue.value.indexOf(value);
+      currentIndex === -1
+        ? typeValue.value.push(value)
+        : typeValue.value.splice(currentIndex, 1);
+    };
+
+    // 验证方法修改
+    const validateType = () => typeValue.value.length > 0;
+
+    // 确认选择
+    const confirmType = () => {
+      showType.value = false;
+    };
+
+    return {
+      formData,
+      maintenanceTypes,
+      fileList,
+      currentDate,
+      confirmStartTime,
+      confirmEndTime,
+      handleFileUpload,
+      handleFileDelete,
+      onSubmit,
+      showType,
+      typeValue,
+      typeText,
+      onConfirmType,
+      onDateConfirm,
+      showDate,
+      dateText,
+      dateValue,
+      showStartTime,
+      showEndTime,
+      startTime,
+      endTime,
+      toggle,
+      validateType,
+      confirmType,
+      maxDate,
+      minDate,
+    };
+  },
+};
+</script>
+<style lang="less" scoped>
+.maintenance-form {
+  //   background: #f7f8fa;
+
+  .content-container {
+    background: #f5f6fa;
+    height: calc(100% - 50px);
+    overflow: auto;
+    overflow-x: hidden;
+  }
+
+  .form-container {
+    .van-cell-group__title {
+      padding: 16px 0;
+      font-size: 14px;
+      color: #969799;
+    }
+
+    .time-range {
+      display: flex;
+      align-items: center;
+      width: 100%;
+
+      .van-field {
+        flex: 1;
+      }
+
+      .separator {
+        margin: 0 8px;
+        color: #969799;
+      }
+    }
+
+    .submit-btn {
+      margin: 30px 16px;
+    }
+
+    .van-uploader {
+      padding: 16px;
+      width: 100%;
+    }
+  }
+}
+
+:deep(.van-field) {
+  /* label文本居中对齐 */
+  .van-field__label {
+    display: flex;
+    align-items: center;
+    // justify-content: center;
+    font-weight: 500;
+    width: 100px;
+
+    /* 小屏幕适配 */
+    @media (max-width: 480px) {
+      width: 100px;
+      font-size: 13px;
+    }
+  }
+}
+
+.popup-footer {
+  padding: 10px;
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  background: #f7f8fa;
+}
+
+.van-checkbox__icon--checked .van-checkbox__icon {
+  background-color: #4dc294;
+  border-color: #4dc294;
+}
+</style>

+ 911 - 0
src/views/device/maintenance/index.vue

@@ -0,0 +1,911 @@
+<template>
+  <div class="maintenance-page">
+    <!-- 顶部导航栏 -->
+    <s-header :name="$t('maintenance.title')" :noback="false"></s-header>
+
+    <div class="content-container">
+      <!-- 设备信息卡片 -->
+      <div class="device-card" v-if="name">
+        <div class="device-header">
+          <div class="header-indicator"></div>
+          <h3 class="device-name">
+            {{ $t("device.equipmentName") }}:{{ name }}
+          </h3>
+        </div>
+      </div>
+
+      <!-- 统计卡片 -->
+      <div class="stats-card">
+        <div class="stats-content">
+          <div class="stats-icon-box">
+            <van-icon name="completed" class="stats-icon" />
+          </div>
+          <div class="stats-text">
+            <div class="stats-title">{{ $t("maintenance.title") }}</div>
+            <div class="stats-info">
+              <span class="stats-number">{{ total }}</span>
+            </div>
+          </div>
+          <div class="action-buttons">
+            <van-icon
+              name="filter-o"
+              class="action-icon"
+              @click="showFilter = true"
+            />
+          </div>
+        </div>
+      </div>
+
+      <!-- 维护记录列表 -->
+      <van-list
+        v-model:loading="loading"
+        :finished="finished"
+        :finished-text="$t('public.noMore')"
+        @load="onLoad"
+      >
+        <div
+          v-for="(item, index) in list"
+          :key="index"
+          class="maintenance-item"
+          @click="showDetail(item)"
+        >
+          <div class="item-header">
+            <div class="device-id">
+              <van-icon name="setting" /> {{ item.clientId }}
+            </div>
+            <div class="status-tag" :class="statusClass(item.status)">
+              {{ getStatusText(item.status) }}
+            </div>
+          </div>
+
+          <div class="item-content">
+            <div class="staff-info">
+              <van-icon name="manager-o" />
+              <span>{{ item.name }} ({{ item.managerId }})</span>
+            </div>
+            <div class="type-info">
+              <van-icon name="label-o" />
+              <span>{{ formatMaintenanceTypes(item.type) }}</span>
+            </div>
+            <div class="time-range">
+              <span class="start-time">{{
+                formatDateTime(item.startTime)
+              }}</span>
+              <van-icon name="arrow" />
+              <span class="end-time">{{ formatDateTime(item.endTime) }}</span>
+            </div>
+          </div>
+        </div>
+      </van-list>
+    </div>
+
+    <!-- 筛选弹窗 -->
+    <van-popup v-model:show="showFilter" position="right" class="filter-popup">
+      <div class="popup-header">
+        <h3>{{ $t("maintenance.filter.title") }}</h3>
+        <van-icon name="cross" @click="showFilter = false" />
+      </div>
+
+      <div class="filter-content">
+        <!-- 设备编号 -->
+        <div class="filter-item">
+          <label>{{ $t("device.equipmentCodeLabel") }}</label>
+          <van-field
+            v-if="user.type < 2"
+            v-model="filterParams.clientId"
+            clearable
+            :placeholder="$t('device.equipmentCodePlaceholder')"
+          />
+          <van-field
+            v-else
+            v-model="filterParams.clientId"
+            is-link
+            readonly
+            :placeholder="$t('device.equipmentCodePlaceholder')"
+            @click="showDevicePicker = true"
+          />
+          <van-popup
+            v-model:show="showDevicePicker"
+            destroy-on-close
+            position="bottom"
+          >
+            <van-picker
+              :columns="deviceColumns"
+              @confirm="onDeviceConfirm"
+              @cancel="showDevicePicker = false"
+            />
+          </van-popup>
+        </div>
+
+        <!-- 维护人员 -->
+        <div class="filter-item">
+          <label>{{ $t("maintenance.filter.staffId") }}</label>
+          <van-field
+            v-model="filterParams.managerId"
+            :placeholder="$t('maintenance.filter.staffIdPlaceholder')"
+            clearable
+          />
+        </div>
+
+        <!-- 维护状态 -->
+        <div class="filter-item">
+          <label>{{ $t("maintenance.filter.status") }}</label>
+          <van-radio-group v-model="filterParams.status">
+            <van-cell-group>
+              <van-cell
+                v-for="status in statusOptions"
+                :key="status.value"
+                :title="status.label"
+                clickable
+                @click="filterParams.status = status.value"
+              >
+                <template #right-icon>
+                  <van-radio :name="status.value" />
+                </template>
+              </van-cell>
+            </van-cell-group>
+          </van-radio-group>
+        </div>
+
+        <!-- 时间范围 -->
+        <div class="filter-item">
+          <label>{{ $t("maintenance.filter.dateRange") }}</label>
+          <div class="date-range">
+            <van-field
+              v-model="filterParams.startDate"
+              :placeholder="$t('maintenance.filter.startDatePlaceholder')"
+              readonly
+              @click="showStartPicker = true"
+            />
+            <span class="date-separator">~</span>
+            <van-field
+              v-model="filterParams.endDate"
+              :placeholder="$t('maintenance.filter.endDatePlaceholder')"
+              readonly
+              @click="showEndPicker = true"
+            />
+          </div>
+        </div>
+      </div>
+
+      <div class="popup-footer">
+        <van-button type="default" @click="resetFilter">{{
+          $t("maintenance.filter.reset")
+        }}</van-button>
+        <van-button type="primary" @click="applyFilter">{{
+          $t("maintenance.filter.apply")
+        }}</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-popup
+      v-model:show="showDetailPopup"
+      position="bottom"
+      :style="{ height: '85%' }"
+      class="detail-popup"
+    >
+      <div class="detail-content" v-if="selectedItem">
+        <div class="detail-header">
+          <h3>{{ $t("maintenance.detail.title") }}</h3>
+          <div class="device-id">{{ selectedItem.clientId }}</div>
+        </div>
+
+        <div class="detail-body">
+          <div class="detail-row">
+            <label>{{ $t("maintenance.detail.staff") }}</label>
+            <div>{{ selectedItem.name }} ({{ selectedItem.managerId }})</div>
+          </div>
+
+          <div class="detail-row">
+            <label>{{ $t("maintenance.detail.type") }}</label>
+            <div class="maintenance-type-tags">
+              <van-tag
+                v-for="(type, index) in JSON.parse(selectedItem.type)"
+                :key="index"
+                plain
+                type="primary"
+              >
+                {{ getMaintenanceTypeLabel(type) }}
+              </van-tag>
+            </div>
+          </div>
+
+          <div class="detail-row">
+            <label>{{ $t("maintenance.detail.startTime") }}</label>
+            <div>{{ formatDateTime(selectedItem.startTime) }}</div>
+          </div>
+
+          <div class="detail-row">
+            <label>{{ $t("maintenance.detail.endTime") }}</label>
+            <div>{{ formatDateTime(selectedItem.endTime) }}</div>
+          </div>
+
+          <div class="detail-row">
+            <label>{{ $t("maintenance.detail.recordTime") }}</label>
+            <div>{{ formatDateTime(selectedItem.createDate) }}</div>
+          </div>
+
+          <div class="detail-row">
+            <label>{{ $t("maintenance.detail.consumables") }}</label>
+            <div>
+              {{
+                selectedItem.consumables ||
+                $t("maintenance.detail.noConsumables")
+              }}
+            </div>
+          </div>
+
+          <div class="detail-section">
+            <h4>{{ $t("maintenance.detail.faultDescription") }}</h4>
+            <div class="detail-text">
+              {{
+                selectedItem.description ||
+                $t("maintenance.detail.noFaultDescription")
+              }}
+            </div>
+          </div>
+
+          <div class="detail-section">
+            <h4>{{ $t("maintenance.detail.solution") }}</h4>
+            <div class="detail-text">
+              {{ selectedItem.solution || $t("maintenance.detail.noSolution") }}
+            </div>
+          </div>
+
+          <div
+            class="image-section"
+            v-if="selectedItem.pic && JSON.parse(selectedItem.pic).length > 0"
+          >
+            <h4>{{ $t("maintenance.detail.images") }}</h4>
+            <div class="image-grid">
+              <van-uploader
+                v-model="fileList"
+                multiple
+                :show-upload="false"
+                :deletable="false"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import { reactive, toRefs } from "vue";
+import sHeader from "@/components/SimpleHeader";
+import { useRoute } from "vue-router";
+import { list } from "@/service/maintenance";
+import { showFailToast } from "vant";
+import { getEquipmentList } from "@/service/typeSelectList";
+import { getLoginUser } from "@/common/js/utils";
+import { onMounted } from "vue";
+import { useI18n } from "vue-i18n";
+
+export default {
+  components: {
+    sHeader,
+  },
+
+  setup() {
+    const { t } = useI18n();
+
+    const route = useRoute();
+    // 定义状态和响应式数据
+    const state = reactive({
+      deviceDetail: null,
+      loading: false,
+      finished: false,
+      name: route.query.name
+        ? route.query.name
+        : route.query.clientId.slice(-6),
+      list: [],
+      total: 0,
+      selectedItem: null,
+      showFilter: false,
+      showDetailPopup: false,
+      showDevicePicker: false,
+      showStartPicker: false,
+      showEndPicker: false,
+      startDate: [
+        new Date().getFullYear(),
+        new Date().getMonth() + 1,
+        new Date().getDate(),
+      ],
+      endDate: [
+        new Date().getFullYear(),
+        new Date().getMonth() + 1,
+        new Date().getDate(),
+      ],
+      filterParams: {
+        managerId: "", // 维护人员工号
+        clientId: route.query.clientId, // 设备编号
+        adminId: null,
+        status: "", // 问题解决状态
+        name: "", // 维护人员姓名
+        startDate: "",
+        endDate: "",
+        current: 0, // 页数
+        size: 10, // 页大小
+      },
+      maintenanceTypeMap: {
+        1: t("maintenance.type.cleaning"),
+        2: t("maintenance.type.refill"),
+        3: t("maintenance.type.repair"),
+      },
+      fileList: [],
+      deviceColumns: [],
+      user: getLoginUser(),
+    });
+
+    // 状态选项
+    const statusOptions = [
+    { value: "", label: t('maintenance.status.all') },
+      { value: 1, label: t('maintenance.status.resolved') },
+      { value: 2, label: t('maintenance.status.unresolved') },
+    ];
+
+    const columnsType = ["year", "month", "day"];
+
+    // 方法
+    const methods = {
+      onLoad() {
+        if (!state.finished) {
+          state.filterParams.current = state.filterParams.current + 1;
+          getListFun();
+        }
+      },
+
+      showDetail(item) {
+        state.selectedItem = item;
+        state.fileList = [];
+        if (item.pic && JSON.parse(item.pic).length > 0) {
+          JSON.parse(item.pic).forEach((img) => {
+            state.fileList.push({ url: img });
+          });
+        }
+        state.showDetailPopup = true;
+      },
+
+      handleStartConfirm({ selectedValues }) {
+        state.filterParams.startDate = selectedValues.join("/");
+        state.showStartPicker = false;
+      },
+
+      handleEndConfirm({ selectedValues }) {
+        state.filterParams.endDate = selectedValues.join("/");
+        state.showEndPicker = false;
+      },
+
+      resetFilter() {
+        state.filterParams = {
+          clientId: "",
+          managerId: "",
+          status: "",
+          startDate: "",
+          endDate: "",
+          current: 0,
+          size: 10,
+        };
+      },
+
+      applyFilter() {
+        state.showFilter = false;
+        state.list = [];
+        state.filterParams.current = 1;
+        getListFun();
+      },
+
+      getMaintenanceTypeLabel(type) {
+        return state.maintenanceTypeMap[type] || type;
+      },
+
+      formatMaintenanceTypes(types) {
+        types = JSON.parse(types);
+        return types
+          .map((type) => methods.getMaintenanceTypeLabel(type))
+          .join("、");
+      },
+
+      onDeviceConfirm({ selectedValues, selectedOptions }) {
+        state.name = selectedOptions[0].text;
+        state.filterParams.clientId = selectedValues[0];
+        state.showDevicePicker = false;
+      },
+
+      formatDateTime(dateString) {
+        if (!dateString) return "";
+        const date = new Date(dateString);
+        return date.toLocaleString();
+      },
+
+      statusClass(status) {
+        return {
+          "status-completed": status === 1,
+          "status-pending": status === 2,
+        };
+      },
+
+      getStatusText(status) {
+        switch (status) {
+          case 1:
+            return t('maintenance.status.resolved');
+          case 2:
+            return t('maintenance.status.unresolved');
+          default:
+            return t('maintenance.status.unknown');
+        }
+      },
+    };
+
+    onMounted(() => {
+      state.filterParams.adminId = state.user.type > 1 ? state.user.id : null;
+      getDeviceListFun();
+    });
+
+    // 获取设备列表
+    const getDeviceListFun = async () => {
+      const { data } = await getEquipmentList({ adminId: state.user.id });
+      if (data.code === "00000") {
+        state.deviceColumns = data.data.map((item) => {
+          return {
+            text: item.name != null ? item.name : item.clientId,
+            value: item.clientId,
+          };
+        });
+        state.deviceColumns.unshift({
+          text: t("typeSelectList.allDevices"),
+          value: "",
+        });
+      }
+    };
+
+    // 获取维护记录
+    const getListFun = async () => {
+      state.finished = false;
+      const { data } = await list(state.filterParams);
+      if (data.code === "00000") {
+        state.list = state.list.concat(data.data.records);
+        state.total = data.data.total;
+        if (state.list.length === data.data.total) {
+          state.finished = true;
+        }
+        state.loading = false;
+      } else {
+        showFailToast(data.message);
+      }
+    };
+
+    // 返回所有状态和方法
+    return {
+      ...toRefs(state),
+      statusOptions,
+      columnsType,
+      ...methods,
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.maintenance-page {
+  background: #f5f7fa;
+  min-height: 100vh;
+
+  .content-container {
+    background: #f5f6fa;
+    height: calc(100% - 50px);
+    overflow: auto;
+    overflow-x: hidden;
+  }
+
+  .device-card {
+    background: #ffffff;
+    border-radius: 8px;
+    padding: 15px;
+    margin: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+    .device-header {
+      display: flex;
+      align-items: center;
+
+      .header-indicator {
+        width: 3px;
+        height: 16px;
+        background: #4e6bdd;
+        border-radius: 2px;
+        margin-right: 8px;
+      }
+
+      .device-name {
+        margin: 0;
+        font-size: 15px;
+        color: #404d74;
+        font-weight: 550;
+      }
+    }
+  }
+
+  .stats-card {
+    background: #ffffff;
+    border-radius: 8px;
+    padding: 15px;
+    margin: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+    .stats-content {
+      display: flex;
+      align-items: center;
+
+      .stats-icon-box {
+        width: 48px;
+        height: 48px;
+        background: #e6f0ff;
+        border-radius: 8px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-right: 12px;
+
+        .stats-icon {
+          font-size: 24px;
+          color: #4e6bdd;
+        }
+      }
+
+      .stats-text {
+        flex: 1;
+
+        .stats-title {
+          font-size: 14px;
+          color: #666;
+          margin-bottom: 4px;
+        }
+
+        .stats-info {
+          .stats-number {
+            font-size: 22px;
+            font-weight: 600;
+            color: #4e6bdd;
+          }
+
+          .stats-unit {
+            font-size: 14px;
+            color: #999;
+          }
+        }
+      }
+
+      .action-buttons {
+        display: flex;
+
+        .action-icon {
+          width: 36px;
+          height: 36px;
+          background: #e6f0ff;
+          border-radius: 8px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          margin-left: 8px;
+          font-size: 18px;
+          color: #4e6bdd;
+        }
+      }
+    }
+  }
+
+  .maintenance-item {
+    background: #ffffff;
+    border-radius: 8px;
+    padding: 14px;
+    margin: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 10px;
+      padding-bottom: 10px;
+      border-bottom: 1px solid #f5f7fa;
+
+      .device-id {
+        display: flex;
+        align-items: center;
+        font-size: 14px;
+        font-weight: 500;
+        color: #333;
+
+        .van-icon {
+          margin-right: 6px;
+          color: #4e6bdd;
+        }
+      }
+
+      .status-tag {
+        padding: 4px 10px;
+        border-radius: 20px;
+        font-size: 12px;
+        font-weight: 500;
+
+        &.status-completed {
+          background: #e8f5e9;
+          color: #4caf50;
+        }
+
+        &.status-pending {
+          background: #ffebee;
+          color: #f44336;
+        }
+      }
+    }
+
+    .item-content {
+      .staff-info,
+      .type-info {
+        display: flex;
+        align-items: center;
+        margin-bottom: 8px;
+        font-size: 14px;
+        color: #666;
+
+        .van-icon {
+          margin-right: 6px;
+          color: #999;
+        }
+      }
+
+      .time-range {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        font-size: 13px;
+        color: #888;
+        padding-top: 8px;
+        margin-top: 8px;
+        border-top: 1px dashed #eee;
+
+        .start-time,
+        .end-time {
+          flex: 1;
+        }
+
+        .van-icon {
+          color: #ccc;
+        }
+      }
+    }
+  }
+
+  .no-data {
+    text-align: center;
+    padding: 40px 20px;
+    background: #fff;
+    border-radius: 8px;
+
+    p {
+      margin-top: 15px;
+      font-size: 15px;
+      color: #999;
+    }
+  }
+
+  .filter-popup {
+    width: 85%;
+    height: 100%;
+    background: #f5f7fa;
+
+    .popup-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 18px;
+      background: #fff;
+      border-bottom: 1px solid #eee;
+
+      h3 {
+        margin: 0;
+        font-size: 16px;
+        font-weight: 500;
+      }
+
+      .van-icon {
+        font-size: 20px;
+        color: #999;
+      }
+    }
+
+    .filter-content {
+      padding: 15px;
+      overflow-y: auto;
+
+      .filter-item {
+        margin-bottom: 20px;
+
+        label {
+          display: block;
+          margin-bottom: 8px;
+          font-size: 14px;
+          font-weight: 500;
+          color: #333;
+        }
+
+        .date-range {
+          display: flex;
+          align-items: center;
+
+          .van-field {
+            flex: 1;
+          }
+
+          .date-separator {
+            padding: 0 10px;
+            color: #999;
+          }
+        }
+      }
+    }
+
+    .popup-footer {
+      position: fixed;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      display: flex;
+      justify-content: space-between;
+      padding: 15px;
+      background: #fff;
+      border-top: 1px solid #eee;
+
+      .van-button {
+        flex: 1;
+        margin: 0 5px;
+      }
+    }
+  }
+
+  .detail-popup {
+    background: #f5f7fa;
+    border-top-left-radius: 20px;
+    border-top-right-radius: 20px;
+    overflow: hidden;
+
+    .detail-content {
+      .detail-header {
+        padding: 20px 15px;
+        background: linear-gradient(135deg, #4e6bdd, #3a56c0);
+        color: white;
+        border-top-left-radius: 20px;
+        border-top-right-radius: 20px;
+
+        h3 {
+          margin: 0 0 5px 0;
+          font-size: 18px;
+        }
+
+        .device-id {
+          font-size: 14px;
+          opacity: 0.9;
+        }
+      }
+
+      .detail-body {
+        padding: 15px;
+        height: calc(80vh - 80px);
+        overflow-y: auto;
+        overflow-x: hidden;
+
+        .detail-row {
+          padding: 12px;
+          background: #fff;
+          border-radius: 8px;
+          margin-bottom: 10px;
+
+          label {
+            display: block;
+            font-size: 13px;
+            color: #999;
+            margin-bottom: 5px;
+          }
+
+          div {
+            font-size: 15px;
+            color: #333;
+            font-weight: 500;
+          }
+
+          .maintenance-type-tags {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 6px;
+            margin-top: 5px;
+          }
+        }
+
+        .detail-section {
+          background: #fff;
+          border-radius: 8px;
+          padding: 12px;
+          margin-top: 15px;
+
+          h4 {
+            margin: 0 0 8px 0;
+            font-size: 15px;
+            color: #333;
+            font-weight: 500;
+          }
+
+          .detail-text {
+            font-size: 14px;
+            color: #666;
+            line-height: 1.6;
+          }
+        }
+
+        .image-section {
+          background: #fff;
+          border-radius: 8px;
+          padding: 12px;
+          margin-top: 15px;
+
+          h4 {
+            margin: 0 0 10px 0;
+            font-size: 15px;
+            color: #333;
+            font-weight: 500;
+          }
+
+          .image-grid {
+            display: grid;
+            grid-template-columns: repeat(1, 1fr);
+            gap: 25px;
+            padding: 10px;
+            border-radius: 6px;
+            overflow: hidden;
+            background: #f0f0f0;
+          }
+        }
+      }
+    }
+  }
+
+  .export-content {
+    padding: 15px;
+
+    p {
+      margin: 0 0 15px 0;
+      font-size: 15px;
+      color: #666;
+      text-align: center;
+    }
+
+    .export-options {
+      .van-radio {
+        margin: 10px 0;
+      }
+    }
+  }
+}
+</style>

+ 102 - 0
src/views/device/maintenance/success.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="success-page">
+    <!-- 顶部导航栏 -->
+    <s-header :name="$t('maintenance.form.success')" :noback="true" />
+
+    <div class="content-wrapper">
+      <!-- 成功图标 -->
+      <div class="success-icon-box">
+        <van-icon name="checked" class="success-icon" />
+      </div>
+
+      <!-- 成功标题 -->
+      <h1 class="success-title">{{ $t("maintenance.form.success") }}</h1>
+    </div>
+  </div>
+</template>
+
+<script>
+import sHeader from "@/components/SimpleHeader";
+
+export default {
+  name: "FormSuccessPage",
+  components: {
+    sHeader,
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.success-page {
+  min-height: 100vh;
+  background-color: #f5f7fa;
+  display: flex;
+  flex-direction: column;
+
+  .content-wrapper {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 40px 20px;
+    box-sizing: border-box;
+  }
+
+  .success-icon-box {
+    width: 100px;
+    height: 100px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: linear-gradient(135deg, #4ade80, #22c55e);
+    border-radius: 50%;
+    margin: 20px 0 30px;
+  }
+
+  .success-icon {
+    color: white;
+    font-size: 50px;
+  }
+
+  .success-title {
+    font-size: 24px;
+    font-weight: bold;
+    margin-bottom: 12px;
+    color: #333;
+    text-align: center;
+  }
+
+  .primary-btn {
+    margin-bottom: 15px;
+    background: linear-gradient(to right, #3c7eff, #5b8cff);
+    border: none;
+    font-weight: 500;
+  }
+
+  .secondary-btn {
+    color: #3c7eff;
+    font-weight: 500;
+    background-color: #f0f5ff;
+    border: 1px solid #d0e3ff;
+  }
+
+  @media (max-width: 480px) {
+    .success-icon-box {
+      width: 80px;
+      height: 80px;
+    }
+
+    .success-icon {
+      font-size: 40px;
+    }
+
+    .success-title {
+      font-size: 21px;
+    }
+
+    .success-description {
+      font-size: 14px;
+    }
+  }
+}
+</style>

+ 9 - 2
src/views/device/modifyPrice/index.vue

@@ -288,9 +288,16 @@ export default {
     }
 
     .device-name {
-      font-size: 15px;
-      color: @text-primary;
       margin: 0;
+      font-size: 15px;
+      color: #404d74;
+      font-weight: 550;
+
+      // 长名称处理
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      max-width: 70vw;
     }
   }
 }

+ 20 - 16
src/views/device/modulation.vue

@@ -15,7 +15,9 @@
     <!-- 音量调节卡片 -->
     <div class="control-card">
       <div class="volume-display">
-        <span class="volume-label">{{ $t("device.slideSliderToAdjustVolume") }}</span>
+        <span class="volume-label">{{
+          $t("device.slideSliderToAdjustVolume")
+        }}</span>
         <div class="volume-value">{{ volume }}</div>
       </div>
 
@@ -56,12 +58,13 @@ export default {
     const deviceId = route.query.deviceId;
     const deviceDetal = ref(null);
     const volume = ref(0);
+    const equipmentName = ref("");
 
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
       getDeviceDetalFun();
-      console.log(deviceId);
+      equipmentName.value = route.query.name;
     });
 
     // 获取设备列表数据
@@ -69,15 +72,13 @@ export default {
       const { data } = await getDeviceDetal({ id: deviceId });
       if (data.code === "00000") {
         deviceDetal.value = data.data;
-        volume.value = data.data.volume ? data.data.volume : 0;
+        volume.value = data.data.volume ? Number.parseInt(data.data.volume) : 0;
       } else {
         showFailToast(data.message);
       }
     };
     // 音量调节保存
     const volumeChange = async () => {
-      // const volumeB = 15 / 100 ;
-      // const volumeC = parseInt(volume.value * volumeB);
       const { data } = await updateVolume({
         id: deviceId,
         volume: volume.value.toString(),
@@ -93,15 +94,12 @@ export default {
       } else {
         showFailToast(data.message);
       }
-      console.log("volumeChange", {
-        equipmentId: deviceId,
-        volume: volume.value.toString(),
-      });
     };
     return {
       deviceDetal,
       volume,
       volumeChange,
+      equipmentName,
     };
   },
   components: { sHeader },
@@ -121,7 +119,7 @@ export default {
 .device-card {
   background: @card-bg;
   border-radius: 12px;
-  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
   margin: 10px;
   padding: 16px;
 
@@ -138,9 +136,16 @@ export default {
     }
 
     .device-name {
-      font-size: 16px;
-      color: @text-primary;
       margin: 0;
+      font-size: 15px;
+      color: #404d74;
+      font-weight: 550;
+
+      // 长名称处理
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      max-width: 70vw;
     }
   }
 }
@@ -148,7 +153,7 @@ export default {
 .control-card {
   background: @card-bg;
   border-radius: 12px;
-  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
   padding: 24px 16px;
   margin: 10px;
 
@@ -182,7 +187,7 @@ export default {
       font-size: 14px;
       line-height: 36px;
       text-align: center;
-      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
     }
   }
 
@@ -200,10 +205,9 @@ export default {
 }
 
 @media (max-width: 480px) {
-
   .control-card {
     padding: 20px 12px;
-    
+
     .volume-display .volume-value {
       font-size: 22px;
     }

+ 212 - 48
src/views/device/paramsSet/index.vue

@@ -1,56 +1,58 @@
 <template>
-  <!-- 设备参数调整列表 -->
-  <div class="paramsSetPage flex-col">
-    <s-header :name="$t('device.parameterAdjustment')" :noback="false"></s-header>
-    <div class="paramsSetBox">
-      <div class="wrap2 flex-col van-hairline--bottom">
-        <div class="outer1 justify-between">
-          <div class="block1"></div>
-          <span class="txt2">{{ $t('device.equipmentName') }}:{{ name ? name : $t("device.equipmentNameTips") }}</span>
-        </div>
-      </div>
-      <div class="list van-hairline--bottom" @click="pushPage('0')">
-        <div class="title">
-          <img class="icon" src='../../../assets/device/currency1.png' />
-          <span class="word3">{{ $t('device.generalParameterConfiguration') }}</span>
-        </div>
-        <div class="outer2 flex-col"></div>
-      </div>
-      <div class="list van-hairline--bottom" @click="pushPage('1')" v-if="machineType == 0">
-        <div class="title">
-          <img class="icon" src='../../../assets/device/advanced.png' />
-          <span class="word3">{{ $t('device.advancedParameterConfiguration') }}</span>
-        </div>
-        <div class="outer2 flex-col"></div>
-      </div>
-      <div class="list van-hairline--bottom" @click="pushPage('2')" v-if="machineType == 0">
-        <div class="title">
-          <img class="icon" src='../../../assets/device/debug.png' />
-          <span class="word3">{{ $t('device.debuggingParameterConfiguration') }}</span>
-        </div>
-        <div class="outer2 flex-col"></div>
+  <div class="parameter-settings">
+    <s-header :name="$t('device.parameterAdjustment')" :noback="false" />
+
+    <!-- 设备信息卡片 -->
+    <div class="device-card">
+      <div class="device-header">
+        <div class="header-indicator"></div>
+        <h3 class="device-name">
+          {{ $t("device.equipmentName") }}:{{
+            name || $t("device.equipmentNameTips")
+          }}
+        </h3>
       </div>
-      <div class="list van-hairline--bottom" @click="pushPage('3')" v-if="machineType == 0">
-        <div class="title">
-          <img class="icon" src='../../../assets/device/humanness.png' />
-          <span class="word3">{{ $t('device.humidityParameterConfiguration') }}</span>
+    </div>
+
+    <!-- 参数设置列表 -->
+    <div class="settings-list">
+      <div
+        v-for="(item, index) in menuItems"
+        :key="index"
+        class="menu-card"
+        @click="handleMenuItemClick(item)"
+      >
+        <div class="menu-content">
+          <img
+            class="menu-icon"
+            :src="getIconPath(item.icon)"
+            :alt="item.title"
+          />
+          <div class="menu-info">
+            <h4 class="menu-title">{{ item.title }}</h4>
+            <p class="menu-desc">
+              {{ item.description }}
+            </p>
+          </div>
         </div>
-        <div class="outer2 flex-col"></div>
+        <van-icon name="arrow" class="menu-arrow" />
       </div>
     </div>
   </div>
 </template>
+
 <script>
-import { onMounted, ref } from 'vue';
+import { onMounted, ref } from "vue";
 import sHeader from "@/components/SimpleHeader";
-import { useRoute, useRouter } from 'vue-router';
-import { getDeviceDetal } from '@/service/device'
-import { showFailToast } from 'vant';
-import { styleUrl } from "../../../common/js/utils";
+import { useRoute, useRouter } from "vue-router";
+import { getDeviceDetal } from "@/service/device";
+import { showFailToast } from "vant";
+import { useI18n } from "vue-i18n";
 
 export default {
   components: { sHeader },
   setup() {
+    const { t } = useI18n();
     const router = useRouter();
     const route = useRoute();
     const deviceId = route.query.deviceId;
@@ -60,28 +62,190 @@ export default {
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
-      styleUrl('paramsSet');
       await getDeviceDetalFun();
+      if (machineType === "0") {
+        menuItems.value.push(
+          {
+            id: "1",
+            icon: "advanced.png",
+            title: t("device.advancedParameterConfiguration"),
+          },
+          {
+            id: "2",
+            icon: "debug.png",
+            title: t("device.debuggingParameterConfiguration"),
+          },
+          {
+            id: "3",
+            icon: "humanness.png",
+            title: t("device.humidityParameterConfiguration"),
+          }
+        );
+      }
     });
+
+    const menuItems = ref([
+      {
+        id: "0",
+        icon: "currency1.png",
+        title: t("device.generalParameterConfiguration"),
+      }
+    ]);
+    const getIconPath = (icon) => {
+      return require(`../../../assets/device/${icon}`);
+    };
     // 获取设备列表数据
     const getDeviceDetalFun = async () => {
       const { data } = await getDeviceDetal({ id: deviceId });
-      if (data.code === '00000') {
+      if (data.code === "00000") {
         deviceDetal.value = data.data;
-      } else { showFailToast(data.message); }
-    }
+      } else {
+        showFailToast(data.message);
+      }
+    };
+
+    const handleMenuItemClick = (item) => {
+      if (item.requiresMachineType && machineType !== 0) return;
+      pushPage(item.id);
+    };
     const pushPage = (number) => {
-      router.push({ path: 'paramsSetInfo', query: { type: number, deviceId: route.query.deviceId, machineType: route.query.machineType } })
-    }
+      router.push({
+        path: "paramsSetInfo",
+        query: {
+          type: number,
+          deviceId: route.query.deviceId,
+          machineType: route.query.machineType,
+        },
+      });
+    };
     return {
       deviceDetal,
       pushPage,
       machineType,
-      name
+      menuItems,
+      handleMenuItemClick,
+      getIconPath,
+      name,
     };
   },
 };
 </script>
 <style lang="less" scoped>
-@import "../../../common/style/common";
+@primary-color: #4d6add;
+@card-bg: #ffffff;
+@text-primary: #2d3436;
+@border-color: #e4e7ec;
+
+.parameter-settings {
+  background: #f8fafb;
+  min-height: 100vh;
+}
+
+.device-card {
+  background: @card-bg;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  margin: 10px;
+  padding: 16px;
+
+  .device-header {
+    display: flex;
+    align-items: center;
+
+    .header-indicator {
+      width: 3px;
+      height: 20px;
+      background: @primary-color;
+      margin-right: 12px;
+      border-radius: 2px;
+    }
+
+    .device-name {
+      margin: 0;
+      font-size: 15px;
+      color: #404d74;
+      font-weight: 550;
+
+      // 长名称处理
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      max-width: 70vw;
+    }
+  }
+}
+
+.settings-list {
+  .menu-card {
+    background: @card-bg;
+    border-radius: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+    margin: 10px;
+    padding: 16px;
+    transition: all 0.3s ease;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    &:active:not(.disabled) {
+      transform: scale(0.98);
+      background: darken(@card-bg, 2%);
+    }
+
+    &.disabled {
+      opacity: 0.6;
+      filter: grayscale(0.8);
+      cursor: not-allowed;
+    }
+
+    .menu-content {
+      display: flex;
+      align-items: center;
+      flex: 1;
+    }
+
+    .menu-icon {
+      width: 30px;
+      height: 30px;
+      margin-right: 16px;
+      object-fit: contain;
+    }
+
+    .menu-info {
+      flex: 1;
+      .menu-title {
+        font-size: 15px;
+        color: @text-primary;
+        margin: 0 0 4px;
+      }
+      .menu-desc {
+        font-size: 12px;
+        color: #666;
+        margin: 0;
+      }
+    }
+
+    .menu-arrow {
+      color: #c0c4cc;
+      font-size: 16px;
+      margin-left: 12px;
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .menu-card {
+    padding: 12px !important;
+
+    .menu-icon {
+      width: 20px !important;
+      height: 20px !important;
+      margin-right: 12px !important;
+    }
+
+    .menu-title {
+      font-size: 14px !important;
+    }
+  }
+}
 </style>

+ 460 - 135
src/views/device/paramsSet/paramsSetInfo.vue

@@ -1,80 +1,130 @@
 <template>
-  <!-- 设备参数调整 -->
-  <div class="paramsSetPage flex-col">
-    <s-header :name="paramsTitle" :noback="false"></s-header>
-    <div class="paramsSetBox">
-      <div class="wrap2 flex-col van-hairline--bottom">
-        <div class="outer1 flex-row justify-between">
-          <div class="block1 flex-col"></div>
-          <span class="txt2">{{ $t('device.equipmentName') }}:{{ deviceDetal.name ? deviceDetal.name :
-      $t("device.equipmentNameTips")
-            }}</span>
+  <div class="parameter-adjustment">
+    <s-header :name="paramsTitle" :noback="false" />
+
+    <div class="content-container">
+      <!-- 设备信息卡片 -->
+      <div class="device-card">
+        <div class="device-header">
+          <div class="header-indicator"></div>
+          <h3 class="device-name">
+            {{ $t("device.equipmentName") }}:{{
+              deviceDetal.name || $t("device.equipmentNameTips")
+            }}
+          </h3>
         </div>
       </div>
-      <!-- <van-divider :style="{ margin: '5px 16px' }" /> -->
-      <div v-if="type == '3'">
-        <van-field :label="$t('device.numberOneTm')"
-          :model-value="(deviceDetal.numberOne == null ? $t('device.noData') : deviceDetal.numberOne + $t('device.degree'))"
-          readonly />
-        <van-field :label="$t('device.furnaceHeadTemperature')"
-          :model-value="(deviceDetal.furnaceTm == null ? $t('device.noData') : deviceDetal.furnaceTm + $t('device.degree'))"
-          readonly />
-        <van-field :label="$t('device.candyGeneratorTm')"
-          :model-value="(deviceDetal.candyGeneratorTm == null ? $t('device.noData') : deviceDetal.candyGeneratorTm + $t('device.degree'))"
-          readonly />
-        <van-field :label="$t('device.temperatureInCabinet')"
-          :model-value="(deviceDetal.cabinetTm == null ? $t('device.noData') : deviceDetal.cabinetTm + $t('device.degree'))"
-          readonly />
-        <van-field :label="$t('device.humidityInCabinet')"
-          :model-value="(deviceDetal.cabinetHd == null ? $t('device.noData') : deviceDetal.cabinetHd + $t('device.humidity'))"
-          readonly />
-        <van-field :label="$t('device.outsideTm')"
-          :model-value="(deviceDetal.outsideTm == null ? $t('device.noData') : deviceDetal.outsideTm + $t('device.degree'))"
-          readonly />
-        <van-field :label="$t('device.outsidehd')"
-          :model-value="(deviceDetal.outsideHd == null ? $t('device.noData') : deviceDetal.outsideHd + $t('device.humidity'))"
-          readonly />
-      </div>
-      <div v-if="type == '2'">
-        <van-field :label="$t('device.humidityInCabinet')"
-          :model-value="(deviceDetal.cabinetHd == null ? $t('device.noData') : deviceDetal.cabinetHd + $t('device.humidity'))"
-          readonly />
-        <van-field v-model="interval" :label="$t('device.increaseOrDecrease')" class="paramsList">
-          <template #button>
-            <van-button size="small" type="primary" class="updataButton" @click="updateInterval(0)">{{
-      $t('device.submitUpdates') }}</van-button>
-          </template>
-        </van-field>
-        <van-field :label="$t('device.winterParameters')" class="inputButton">
-          <template #input>
-            <van-button size="small" type="primary" class="updataButton" @click="updateInterval(1)">{{
-      $t('device.submitUpdates') }}</van-button>
-          </template>
-        </van-field>
-        <van-field :label="$t('device.summerParameters')" class="inputButton">
-          <template #input>
-            <van-button size="small" type="primary" class="updataButton" @click="updateInterval(2)">{{
-      $t('device.submitUpdates') }}</van-button>
-          </template>
-        </van-field>
-      </div>
-      <div class="paramsList" v-for="(item, key) in paramsList" :key="key">
-        <van-field :model-value="item.val" @update:model-value="onUpdateParameters(item, key, $event)"
-          v-if="!checkBtn(item.name)" name="phone" type="tel" :label="paramName[key]">
-          <template #button>
-            <van-button size="small" type="primary" class="updataButton" @click="updateParams(item, key)">{{
-      $t('device.submitUpdates') }}</van-button>
-          </template>
-        </van-field>
-        <div class="l-flex-RC" v-else>
-          <span class="txt3 o-pl-15 o-mtb-10" v-if="item.name != 'M502'">{{ paramName[key] }}
-          </span>
-          <van-switch v-if="item.name != 'M502'" :model-value="checked[key]" size="23px"
-            @update:model-value="onUpdateValue(item, key)">
-          </van-switch>
+
+      <!-- 参数配置区 -->
+      <div class="parameter-container">
+        <!-- 温湿度监控模式 -->
+        <div v-if="type === '3'" class="monitoring-grid">
+          <div
+            v-for="item in monitoringParams"
+            :key="item.key"
+            class="parameter-card"
+          >
+            <van-icon :name="item.icon" class="param-icon" />
+            <div class="param-info">
+              <div class="param-label">{{ $t(item.label) }}</div>
+              <div class="param-value">
+                {{ deviceDetal[item.key] ?? $t("device.noData") }}
+                <span v-if="deviceDetal[item.key] !== null">{{
+                  $t(item.unit)
+                }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 调试参数模式 -->
+        <div v-if="type === '2'" class="debug-panel">
+          <div class="parameter-card">
+            <van-icon name="weapp-nav" class="param-icon" />
+            <div class="param-info">
+              <div class="param-label">
+                {{ $t("device.humidityInCabinet") }}
+              </div>
+              <div class="param-value">
+                {{ deviceDetal.cabinetHd ?? $t("device.noData") }}
+                <span v-if="deviceDetal.cabinetHd !== null">{{
+                  $t("device.humidity")
+                }}</span>
+              </div>
+            </div>
+          </div>
+
+          <div class="control-group">
+            <van-field
+              v-model="interval"
+              :label="$t('device.increaseOrDecrease')"
+              class="smart-field"
+            >
+              <template #button>
+                <van-button
+                  size="small"
+                  type="primary"
+                  @click="updateInterval(0)"
+                  class="action-btn"
+                >
+                  {{ $t("device.submitUpdates") }}
+                </van-button>
+              </template>
+            </van-field>
+
+            <div class="preset-buttons">
+              <van-button
+                v-for="preset in presets"
+                :key="preset.type"
+                type="primary"
+                plain
+                @click="updateInterval(preset.type)"
+                class="preset-btn"
+              >
+                {{ $t(preset.label) }}
+              </van-button>
+            </div>
+          </div>
+        </div>
+
+        <!-- 通用参数列表 -->
+        <div class="parameter-list">
+          <div
+            v-for="(item, key) in paramsList"
+            :key="key"
+            class="parameter-item"
+          >
+            <van-field
+              v-if="!checkBtn(item.name)"
+              v-model="item.val"
+              :label="paramName[key]"
+              @update:model-value="onUpdateParameters(item, key, $event)"
+              class="smart-field"
+            >
+              <template #button>
+                <van-button
+                  size="small"
+                  type="primary"
+                  @click="updateParams(item, key)"
+                  class="action-btn"
+                >
+                  {{ $t("device.submitUpdates") }}
+                </van-button>
+              </template>
+            </van-field>
+
+            <div v-else class="switch-control">
+              <span class="switch-label">{{ paramName[key] }}</span>
+              <van-switch
+                :model-value="checked[key]"
+                @update:model-value="onUpdateValue(item, key)"
+                size="24px"
+                class="custom-switch"
+              />
+            </div>
+          </div>
         </div>
       </div>
-      <div style="height: 50px;"></div>
     </div>
   </div>
 </template>
@@ -83,9 +133,19 @@
 import { onMounted, ref } from "vue";
 import sHeader from "@/components/SimpleHeader";
 import { useRoute, useRouter } from "vue-router";
-import { getDeviceDetal, getParameters, updateParameters, humidityParameters } from "@/service/device";
-import { showFailToast, showSuccessToast, showConfirmDialog, showToast } from "vant";
-import { useI18n } from 'vue-i18n';
+import {
+  getDeviceDetal,
+  getParameters,
+  updateParameters,
+  humidityParameters,
+} from "@/service/device";
+import {
+  showFailToast,
+  showSuccessToast,
+  showConfirmDialog,
+  showToast,
+} from "vant";
+import { useI18n } from "vue-i18n";
 import { styleUrl } from "../../../common/js/utils";
 
 export default {
@@ -107,28 +167,78 @@ export default {
     const howLong = ref([]);
     const interval = ref("");
 
+    const monitoringParams = ref([
+      {
+        key: "numberOne",
+        label: "device.numberOneTm",
+        unit: "device.degree",
+        icon: "fire",
+      },
+      {
+        key: "furnaceTm",
+        label: "device.furnaceHeadTemperature",
+        unit: "device.degree",
+        icon: "fire",
+      },
+      {
+        key: "candyGeneratorTm",
+        label: "device.candyGeneratorTm",
+        unit: "device.degree",
+        icon: "fire",
+      },
+      {
+        key: "cabinetTm",
+        label: "device.temperatureInCabinet",
+        unit: "device.degree",
+        icon: "fire",
+      },
+      {
+        key: "cabinetHd",
+        label: "device.humidityInCabinet",
+        unit: "device.humidity",
+        icon: "weapp-nav",
+      },
+      {
+        key: "outsideTm",
+        label: "device.outsideTm",
+        unit: "device.degree",
+        icon: "fire",
+      },
+      {
+        key: "outsideHd",
+        label: "device.outsidehd",
+        unit: "device.humidity",
+        icon: "weapp-nav",
+      },
+    ]);
+
+    const presets = ref([
+      { type: 1, label: "device.winterParameters" },
+      { type: 2, label: "device.summerParameters" },
+    ]);
+
     // 初始化页面获取列表
     onMounted(async () => {
       // 加载样式
-      styleUrl('paramsSet');
+      styleUrl("paramsSet");
       type.value = route.query.type;
       if (route.query.type === "0") {
-        paramsTitle.value = t('device.generalParameterConfiguration');
+        paramsTitle.value = t("device.generalParameterConfiguration");
       }
       if (route.query.type === "1") {
-        paramsTitle.value = t('device.advancedParameterConfiguration');
+        paramsTitle.value = t("device.advancedParameterConfiguration");
       }
       if (route.query.type === "2") {
-        paramsTitle.value = t('device.debuggingParameterConfiguration');
+        paramsTitle.value = t("device.debuggingParameterConfiguration");
       }
       if (route.query.type === "3") {
-        paramsTitle.value = t('device.humidityParameterConfiguration');
+        paramsTitle.value = t("device.humidityParameterConfiguration");
       }
       getDeviceDetalFun();
     });
     // 判断是否为按钮
     const checkBtn = (name) => {
-      return name.includes('M');
+      return name.includes("M");
     };
     // 获取设备列表数据
     const getDeviceDetalFun = async () => {
@@ -161,30 +271,26 @@ export default {
           if (machineType == 1) {
             paramName.value.push(t("popParams." + paramItem.name));
           }
-          if (paramItem.val === '1') {
+          if (paramItem.val === "1") {
             checked.value.push(true);
           } else {
             checked.value.push(false);
           }
           howLong.value.push(paramItem.val != null ? paramItem.val.length : 0);
-        })
-        // console.log("longs", howLong.value)
-
+        });
       } else {
-        showToast(t('device.noParameterData'));
+        showToast(t("device.noParameterData"));
       }
     };
 
     const onUpdateParameters = (item, key, e) => {
-      // console.log(e);
-      // console.log(howLong.value[key]);
       if (howLong.value[key] != e.length) {
         isChange.value = true;
       } else {
         isChange.value = false;
       }
       item.val = e;
-    }
+    };
 
     // 更新参数值
     const updateParams = async (item, key) => {
@@ -194,43 +300,51 @@ export default {
         val: item.val,
       };
       showConfirmDialog({
-        title: t('device.tips'),
-        message: t('device.isUpdate') + paramName.value[key] + t('device.to') + item.val + '?' + (isChange.value ? t('device.attention') : ''),
-      }).then(async () => {
-        const { data } = await updateParameters(params);
-        if (data.code) {
-          showSuccessToast(t('device.modificationSucceeded'));
-        } else {
-          showFailToast(data.message);
-        }
-      }).catch(() => {
-
+        title: t("device.tips"),
+        message:
+          t("device.isUpdate") +
+          paramName.value[key] +
+          t("device.to") +
+          item.val +
+          "?" +
+          (isChange.value ? t("device.attention") : ""),
       })
-    }
+        .then(async () => {
+          const { data } = await updateParameters(params);
+          if (data.code) {
+            showSuccessToast(t("device.modificationSucceeded"));
+          } else {
+            showFailToast(data.message);
+          }
+        })
+        .catch(() => {});
+    };
     const onUpdateValue = (item, key) => {
       console.log(item);
       const params = {
         id: deviceId,
         name: item.name,
-        val: item.val == '0' ? '1' : '0',
+        val: item.val == "0" ? "1" : "0",
       };
       showConfirmDialog({
-        title: t('device.tips'),
-        message: t('device.content'),
-      }).then(async () => {
-        const { data } = await updateParameters(params);
-        if (data.code) {
-          checked.value[key] = !checked.value[key];
-          showSuccessToast(t('device.modificationSucceeded'));
-          setTimeout(() => {
-            router.go(-1);
-          }, 1000);
-        } else {
-          showFailToast(data.message);
-        }
-      }).catch(() => {
-        // on cancel
-      });
+        title: t("device.tips"),
+        message: t("device.content"),
+      })
+        .then(async () => {
+          const { data } = await updateParameters(params);
+          if (data.code) {
+            checked.value[key] = !checked.value[key];
+            showSuccessToast(t("device.modificationSucceeded"));
+            setTimeout(() => {
+              router.go(-1);
+            }, 1000);
+          } else {
+            showFailToast(data.message);
+          }
+        })
+        .catch(() => {
+          // on cancel
+        });
     };
 
     // 一键递增递减
@@ -251,19 +365,19 @@ export default {
         params.val = "summer";
       }
       showConfirmDialog({
-        title: t('device.tips'),
-        message: t('device.isUpdate') + '?',
-      }).then(async () => {
-        const { data } = await humidityParameters(params);
-        if (data.code) {
-          showSuccessToast(t('device.modificationSucceeded'));
-        } else {
-          showFailToast(data.message);
-        }
-      }).catch(() => {
-
+        title: t("device.tips"),
+        message: t("device.isUpdate") + "?",
       })
-    }
+        .then(async () => {
+          const { data } = await humidityParameters(params);
+          if (data.code) {
+            showSuccessToast(t("device.modificationSucceeded"));
+          } else {
+            showFailToast(data.message);
+          }
+        })
+        .catch(() => {});
+    };
 
     return {
       deviceDetal,
@@ -278,15 +392,226 @@ export default {
       onUpdateParameters,
       interval,
       updateInterval,
-      checkBtn
+      checkBtn,
+      monitoringParams,
+      presets,
     };
   },
 };
 </script>
 <style lang="less" scoped>
-@import "../../../common/style/common";
+@primary-color: #4d6add;
+@card-bg: #ffffff;
+@text-primary: #2d3436;
+@border-color: #e4e7ec;
+
+.parameter-adjustment {
+  background: #f8fafb;
+  min-height: 100vh;
+}
+
+.content-container {
+  background: #f5f6fa;
+  // padding: 16px;
+  height: calc(100% - 50px);
+  overflow: auto;
+  overflow-x: hidden;
+}
+
+.device-card {
+  background: @card-bg;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  margin: 10px;
+  padding: 16px;
+
+  .device-header {
+    display: flex;
+    align-items: center;
+
+    .header-indicator {
+      width: 3px;
+      height: 20px;
+      background: @primary-color;
+      margin-right: 12px;
+      border-radius: 2px;
+    }
+
+    .device-name {
+      font-size: 15px;
+      color: #404d74;
+      margin: 0;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+  }
+}
+
+.parameter-container {
+  .monitoring-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+    gap: 12px;
+  }
+
+  .parameter-card {
+    background: @card-bg;
+    border-radius: 8px;
+    margin: 0 10px;
+    padding: 16px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+    display: flex;
+    align-items: center;
+
+    .param-icon {
+      font-size: 24px;
+      color: @primary-color;
+      margin-right: 12px;
+    }
+
+    .param-label {
+      font-size: 12px;
+      color: #666;
+      margin-bottom: 4px;
+    }
+
+    .param-value {
+      font-size: 14px;
+      color: @text-primary;
+      font-weight: 500;
+
+      span {
+        color: #999;
+        margin-left: 4px;
+      }
+    }
+  }
+
+  .control-group {
+    background: @card-bg;
+    border-radius: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+    margin: 10px;
+
+    .smart-field {
+      // margin-bottom: 12px;
+
+      :deep(.van-field__label) {
+        color: @primary-color;
+        font-weight: 500;
+      }
+    }
+
+    .preset-buttons {
+      display: grid;
+      gap: 12px;
+      // margin: 10px;
+      padding: 15px;
+    }
+
+    .preset-btn {
+      width: 100%;
+    }
+  }
+
+  .parameter-list {
+    .parameter-item {
+      background: @card-bg;
+      border-radius: 8px;
+      margin: 10px;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+      border: 1px solid #e4e7ec;
+      transition: border-color 0.3s;
+
+      &:focus-within {
+        border-color: @primary-color;
+        box-shadow: 0 0 0 2px rgba(77, 194, 148, 0.1);
+      }
+
+      :deep(.van-field__label) {
+        font-weight: 500;
+      }
+
+      .switch-control {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 12px 25px;
+
+        .switch-label {
+          flex: 1;
+          padding-right: 16px;
+          color: @primary-color;
+        }
+      }
+    }
+  }
+}
+
+.action-btn {
+  background: @primary-color;
+  border: none;
+  transition: transform 0.2s;
+
+  &:active {
+    transform: scale(0.95);
+  }
+}
+
+.safe-area {
+  height: 50px;
+}
+
+:deep(.van-field__control) {
+  /* 输入框容器样式 */
+  border: 1px solid #e4e7ec;
+  border-radius: 8px;
+  padding: 5px;
+  transition: border-color 0.3s;
+}
+
+:deep(.van-field) {
+  /* 输入框容器样式 */
+  border-radius: 8px;
+  transition: border-color 0.3s;
+
+  /* label文本居中对齐 */
+  .van-field__label {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 120px;
+    margin-right: 16px;
+    color: @primary-color;
+    font-weight: 500;
+
+    /* 小屏幕适配 */
+    @media (max-width: 480px) {
+      width: 100px;
+      font-size: 13px;
+    }
+  }
+
+  /* 输入框内边距调整 */
+  .van-field__body {
+    padding: 8px 0;
+  }
+
+  /* 按钮样式微调 */
+  .van-button {
+    margin-left: 12px;
+  }
+}
+
+@media (max-width: 480px) {
+  .monitoring-grid {
+    grid-template-columns: 1fr !important;
+  }
 
-.paramsValue {
-  margin: 0 40px;
+  .parameter-card {
+    padding: 12px !important;
+  }
 }
-</style>
+</style>

+ 395 - 119
src/views/device/returnCoin/index.vue

@@ -1,150 +1,426 @@
 <template>
-    <div class="showGoodsIdx">
-        <s-header :name="$t('device.returnCoin')" :noback="false"></s-header>
-        <div class="headerCon kBordBott o-plr-10 o-ptb-16 l-flex-RC">
-            <div class="line o-mr-6"></div>
-            <div>
-                <span class="c-color c-text-14">{{ $t('device.showGoodsPage.equipmentName') }}:</span>
-                <span class="c-text-color c-text-14">{{ equipmentName }}</span>
-            </div>
+  <div class="coin-return">
+    <s-header :name="$t('device.returnCoin')" :noback="false" />
 
+    <div class="content-container">
+      <!-- 设备信息卡片 -->
+      <div class="device-card">
+        <div class="device-header">
+          <div class="header-indicator"></div>
+          <h3 class="device-name">
+            {{ $t("device.showGoodsPage.equipmentName") }}:{{ equipmentName }}
+          </h3>
         </div>
-        <div class="">
-            <van-field name="stepper" :label="$t('device.returnCoinPage.amount')" class="l-flex-RC">
-                <template #input>
-                    <van-stepper v-model="amount" max="50"/>
-                </template>
-                <template #button>
-                    <van-button size="small" type="primary" @click="applyReturnCoinClk()">{{ $t('device.returnCoinPage.returnCoinBtn')
-                        }}</van-button>
-                </template>
-            </van-field>
+      </div>
+
+      <!-- 退币操作卡片 -->
+      <div class="operation-card">
+        <div class="return-control">
+          <div class="control-header">
+            <van-icon name="wap-nav" class="control-icon" />
+            <span class="control-title">{{
+              $t("device.returnCoinPage.amount")
+            }}</span>
+          </div>
+
+          <div class="stepper-container">
+            <van-stepper
+              v-model="amount"
+              :max="50"
+              integer
+              class="custom-stepper"
+            />
+            <van-button
+              type="primary"
+              icon="refund"
+              @click="applyReturnCoinClk"
+              class="submit-btn"
+            >
+              {{ $t("device.returnCoinPage.returnCoinBtn") }}
+            </van-button>
+          </div>
         </div>
-        <van-cell :title="$t('device.returnCoinPage.returnCoinRecord')" icon="points"/>
-        <div class="outer2 flex-col" v-for="item in recordList" :key="item.id">
-            <div class="word">{{ $t('device.returnCoinPage.applyTime') }}:{{ showTime(item.createDate) }}</div>
-            <div class="word">{{ $t('device.returnCoinPage.amount') }}:{{ item.amount }}</div>
-            <div class="word">{{ $t('device.returnCoinPage.status') }}:{{ item.status == 1 ? $t('device.returnCoinPage.returnCoinSuccess') : (item.status == 2 ? $t('device.returnCoinPage.returnCoinFailed') : $t('device.returnCoinPage.waiting')) }}</div>
-            <div class="word" v-if="item.status == 2">{{ $t('device.returnCoinPage.reason') }}:{{ item.reason}}</div>
+      </div>
+
+      <!-- 记录列表卡片 -->
+      <div class="record-card">
+        <div class="card-header">
+          <van-icon name="notes" class="header-icon" />
+          <span class="header-title">{{
+            $t("device.returnCoinPage.returnCoinRecord")
+          }}</span>
         </div>
 
+        <van-list
+          v-model:loading="loading"
+          :finished="finished"
+          :finished-text="$t('public.noMore')"
+          @load="onLoad"
+        >
+          <!-- 记录项 -->
+          <div v-for="item in recordList" :key="item.id" class="record-item">
+            <div class="info-row">
+              <span class="label"
+                >{{ $t("device.returnCoinPage.applyTime") }}:</span
+              >
+              <span class="value">{{ showTime(item.createDate) }}</span>
+            </div>
+            <div class="info-row">
+              <span class="label"
+                >{{ $t("device.returnCoinPage.amount") }}:</span
+              >
+              <span class="value">{{ item.amount }}</span>
+            </div>
+            <div class="info-row">
+              <span class="label"
+                >{{ $t("device.returnCoinPage.status") }}:</span
+              >
+              <van-tag :color="getStatusColor(item.status)" class="status-tag">
+                {{ getStatusText(item.status) }}
+              </van-tag>
+            </div>
+            <div v-if="item.status == 2" class="info-row">
+              <span class="label"
+                >{{ $t("device.returnCoinPage.reason") }}:</span
+              >
+              <span class="error-text">{{ item.reason }}</span>
+            </div>
+          </div>
+        </van-list>
+      </div>
     </div>
+  </div>
 </template>
 
 <script>
 // 导入接口
-import { applyReturnCoin, getReturnCoinList } from '@/service/device/index';
+import { applyReturnCoin, returnCoinList } from "@/service/device/index";
 import sHeader from "@/components/SimpleHeader";
 import { ref } from "@vue/reactivity";
-import { onMounted } from '@vue/runtime-core';
-import { useRoute, useRouter } from 'vue-router';
-import { showConfirmDialog, showFailToast, showToast } from 'vant';
+import { onMounted } from "@vue/runtime-core";
+import { useRoute, useRouter } from "vue-router";
+import { showConfirmDialog, showFailToast, showToast } from "vant";
 import { useI18n } from "vue-i18n";
-import { getLoginUser, styleUrl } from '../../../common/js/utils';
+import { getLoginUser, styleUrl } from "../../../common/js/utils";
 import dateUtil from "../../../utils/dateUtil";
+import { reactive } from "vue";
 export default {
-    components: {
-        sHeader
-    },
-    setup() {
-        // 引入语言
-        const { t } = useI18n();
-        // 路由
-        const route = useRoute();
-        const router = useRouter();
-        const amount = ref(1);
-        const user = getLoginUser();
-        const equipmentId = ref('');
-        const equipmentName = ref('');
-        const clientId = ref('');
-        const activeNames = ref(['1']);
-        const recordList = ref([]);
-        // 刚进页面
-        onMounted(() => {
-            // 加载样式
-            styleUrl('showGoods');
-            equipmentId.value = route.query.deviceId || "";
-            const name = route.query.name || "";
-            clientId.value = route.query.clientId || "";
-            if (equipmentId) {
-                equipmentName.value = name;
-            }
-            getList(clientId.value);
+  components: {
+    sHeader,
+  },
+  setup() {
+    // 引入语言
+    const { t } = useI18n();
+    // 路由
+    const route = useRoute();
+    const router = useRouter();
+    const amount = ref(1);
+    const user = getLoginUser();
+    const equipmentId = ref("");
+    const equipmentName = ref("");
+    const clientId = ref(route.query.clientId);
+    const activeNames = ref(["1"]);
+    const recordList = ref([]);
+    const loading = ref(false);
+    const finished = ref(false);
+    const total = ref(0);
+    // 刚进页面
+    onMounted(() => {
+      // 加载样式
+      styleUrl("showGoods");
+      equipmentId.value = route.query.deviceId || "";
+      const name = route.query.name || "";
+      if (equipmentId) {
+        equipmentName.value = name;
+      }
+    });
 
-        });
+    // 页面列表查询参数
+    const searchParams = reactive({
+      status: "", // 制作状态 1:制作中 2:制作失败 0:未制作
+      clientId: clientId.value,
+      current: 0, // 页数
+      size: 10, // 页大小
+      startTime: "", // 开始时间
+      endTime: "", // 结束时间
+    });
 
-        // 获取退币记录
-        const getList = async () => { // 声明为异步函数
-            try {
-                const { data } = await getReturnCoinList({equipmentId: equipmentId.value}); // 使用await等待异步函数执行完毕并返回结果
-                // 处理获取到的data
-                recordList.value = data.data;
-            } catch (error) {
-                console.error('Error fetching data:', error);
-            }
-        }
+    // 滚动加载
+    const onLoad = () => {
+      if (!finished.value) {
+        searchParams.current = searchParams.current + 1;
+        returnCoinListFun();
+      }
+    };
 
-        // 格式化时间
-        const showTime = (time) => {
-            return dateUtil.timeZoneDate(new Date(dateUtil.formateDate(new Date(time), "yyyy/MM/dd hh:mm:ss")));
+    // 获取做糖列表数据
+    const returnCoinListFun = async () => {
+      finished.value = false;
+      const { data } = await returnCoinList(searchParams);
+      if (data.code) {
+        recordList.value = recordList.value.concat(data.data.records);
+        total.value = data.data.total;
+        if (recordList.value.length === data.data.total) {
+          finished.value = true;
         }
+        loading.value = false;
+      } else {
+        showFailToast(data.message);
+      }
+    };
 
+    // 格式化时间
+    const showTime = (time) => {
+      return dateUtil.timeZoneDate(
+        new Date(dateUtil.formateDate(new Date(time), "yyyy/MM/dd hh:mm:ss"))
+      );
+    };
 
-        // 退币申请
-        const applyReturnCoinClk = () => {
-            const params = {
-                equipmentId: equipmentId.value,
-                amount: amount.value,
-                adminId: user.id
-            };
-            showConfirmDialog({
-                title: t('alramClean.tips'),
-                message: t('device.returnCoinPage.tipsContent'),
-            }).then(async () => {
-                const { data } = await applyReturnCoin(params);
-                console.log("data>>>", data);
-                if (data.code) {
-                    showToast(t('alramClean.successfully'));
-                    setTimeout(() => {
-                        router.go(-1);
-                    }, 1500);
-                } else {
-                    showFailToast(data.message);
-                }
-            }).catch((error) => {
-                console.log(error);
-            });
-        }
+    // 退币申请
+    const applyReturnCoinClk = () => {
+      const params = {
+        equipmentId: equipmentId.value,
+        amount: amount.value,
+        adminId: user.id,
+      };
+      showConfirmDialog({
+        title: t("alramClean.tips"),
+        message: t("device.returnCoinPage.tipsContent"),
+      })
+        .then(async () => {
+          const { data } = await applyReturnCoin(params);
+          console.log("data>>>", data);
+          if (data.code) {
+            showToast(t("alramClean.successfully"));
+            setTimeout(() => {
+              router.go(-1);
+            }, 1500);
+          } else {
+            showFailToast(data.message);
+          }
+        })
+        .catch((error) => {
+          console.log(error);
+        });
+    };
 
-        return {
-            equipmentName,
-            applyReturnCoinClk,
-            amount,
-            activeNames,
-            recordList,
-            showTime
-        };
-    },
+    const getStatusText = (status) => {
+      return status === 1
+        ? t("device.returnCoinPage.returnCoinSuccess")
+        : status === 2
+        ? t("device.returnCoinPage.returnCoinFailed")
+        : t("device.returnCoinPage.waiting");
+    };
+
+    const getStatusColor = (status) => {
+      return (
+        {
+          1: "#4dc294",
+          2: "#ee0a24",
+          0: "#ff976a",
+        }[status] || "#969799"
+      );
+    };
+
+    return {
+      equipmentName,
+      applyReturnCoinClk,
+      amount,
+      activeNames,
+      recordList,
+      showTime,
+      loading,
+      finished,
+      onLoad,
+      getStatusText,
+      getStatusColor,
+    };
+  },
 };
 </script>
 
 <style lang="less" scoped>
+@primary-color: #4d6add;
+@card-bg: #ffffff;
+@text-primary: #2d3436;
+@border-color: #e4e7ec;
 
-.van-cell {
-    font-size: 20px !important;
+.content-container {
+  background: #f5f6fa;
+  // padding: 16px;
+  height: calc(100% - 55px);
+  overflow: auto;
+  overflow-x: hidden;
 }
-.outer2 {
-    box-shadow: 1px 2px 5px 3px rgba(70, 95, 198, 0.14);
-    background-color: rgba(255, 255, 255, 1);
-    border-radius: 4px;
-    margin: 20px 30px;
-    margin-bottom: 20px;
-    padding: 10px 20px;
-
-    .word {
-        font-size: 14px;
-        padding: 3px 0;
-        line-height: 20px;
+
+.coin-return {
+  background: #f8fafb;
+  min-height: 100vh;
+}
+
+.device-card {
+  display: flex;
+  align-items: center;
+  padding: 12px 15px;
+  background: #fff;
+  margin: 10px;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+  .device-header {
+    display: flex;
+    align-items: center;
+
+    .header-indicator {
+      width: 3px;
+      height: 20px;
+      background: @primary-color;
+      margin-right: 12px;
+      border-radius: 2px;
+    }
+
+    .device-name {
+      font-size: 15px;
+      color: #404d74;
+      margin: 0;
+    }
+  }
+}
+
+.operation-card {
+  background: @card-bg;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  padding: 15px;
+  margin: 10px;
+
+  .return-control {
+    .control-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 16px;
+
+      .control-icon {
+        color: @primary-color;
+        font-size: 20px;
+        margin-right: 8px;
+      }
+
+      .control-title {
+        font-size: 15px;
+        color: @text-primary;
+      }
+    }
+
+    .stepper-container {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+
+      .custom-stepper {
+        :deep(.van-stepper__input) {
+          width: 80px;
+          background: #f8fafb;
+          border-radius: 4px;
+        }
+      }
+
+      .submit-btn {
+        height: 36px;
+        padding: 0 20px;
+        box-shadow: 0 2px 6px rgba(77, 194, 148, 0.2);
+      }
+    }
+  }
+}
+
+.record-card {
+  background: @card-bg;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  padding: 16px;
+  margin: 10px;
+
+  .card-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .header-icon {
+      color: @primary-color;
+      font-size: 18px;
+      margin-right: 8px;
+    }
+
+    .header-title {
+      font-size: 15px;
+      color: @text-primary;
+      font-weight: 500;
+    }
+  }
+
+  .record-item {
+    padding: 12px 0;
+    border-bottom: 1px solid @border-color;
+
+    &:last-child {
+      border-bottom: none;
+    }
+
+    .info-row {
+      display: flex;
+      align-items: center;
+      margin: 8px 0;
+      font-size: 14px;
+
+      .label {
+        color: #666;
+        min-width: 80px;
+      }
+
+      .value {
+        color: @text-primary;
+      }
+
+      .status-tag {
+        color: white;
+        padding: 2px 8px;
+        border-radius: 4px;
+      }
+
+      .error-text {
+        color: #ee0a24;
+        flex: 1;
+      }
+    }
+  }
+
+  .empty-state {
+    text-align: center;
+    padding: 40px 0;
+
+    .empty-icon {
+      font-size: 48px;
+      color: #c8c9cc;
+      margin-bottom: 16px;
+    }
+
+    p {
+      color: #969799;
+      margin: 0;
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .stepper-container {
+    flex-direction: column;
+    align-items: stretch !important;
+    gap: 12px;
+
+    .submit-btn {
+      width: 100%;
     }
+  }
 }
-</style>
+</style>

+ 32 - 24
src/views/device/showGoods/index.vue

@@ -65,7 +65,11 @@
 
 <script>
 // 导入接口
-import { selectProducts, updateProductsShow, newUpdateProductsShow } from "@/service/device/index";
+import {
+  selectProducts,
+  updateProductsShow,
+  newUpdateProductsShow,
+} from "@/service/device/index";
 import sHeader from "@/components/SimpleHeader";
 import { ref, reactive } from "@vue/reactivity";
 import { onMounted } from "@vue/runtime-core";
@@ -158,27 +162,29 @@ export default {
         console.log(tableData.value);
         var products = tableData.value;
         var list = JSON.stringify(products);
-        machineType.value === '2'? newUpdateProductsShow({
-          productList: list,
-        }) : updateProductsShow({
-          productList: list,
-        })
-          .then((res) => {
-            console.log(res.data.message);
-            showDialog({
-              message: t("device.modificationSucceeded"),
-            }).then(() => {
-              //返回上一页
-              router.go(-1);
-            });
-            setTimeout(() => {
-              getList();
-            }, 1000);
-          })
-          .catch((error) => {
-            console.log(error);
-            showFailToast(t("device.unknownError"));
-          });
+        machineType.value === "2"
+          ? newUpdateProductsShow({
+              productList: list,
+            })
+          : updateProductsShow({
+              productList: list,
+            })
+              .then((res) => {
+                console.log(res.data.message);
+                showDialog({
+                  message: t("device.modificationSucceeded"),
+                }).then(() => {
+                  //返回上一页
+                  router.go(-1);
+                });
+                setTimeout(() => {
+                  getList();
+                }, 1000);
+              })
+              .catch((error) => {
+                console.log(error);
+                showFailToast(t("device.unknownError"));
+              });
       });
     };
     return {
@@ -196,7 +202,7 @@ export default {
 
 <style lang="less" scoped>
 .goods-management {
-  --primary-color: #4dc294;
+  --primary-color: #4d6add;
   --card-radius: 12px;
   --card-padding: 16px;
   --grid-gap: 12px;
@@ -243,9 +249,11 @@ export default {
 }
 
 .highlight {
+  margin: 0;
   font-size: 15px;
   color: #404d74;
-  font-weight: 500;
+  font-weight: 550;
+
   // 长名称处理
   overflow: hidden;
   text-overflow: ellipsis;

+ 1 - 1
src/views/device/tax/index.vue

@@ -39,7 +39,7 @@
             :rules="[
               { required: true, message: $t('tax.rateRequired') },
               {
-                pattern: /^(100(\.\d{1,2})?|1[1-9]\d(\.\d{1,2})?|200(\.0{1,2})?)$/,
+                pattern: /^(1[0-9]{2}|200)$/,
                 message: $t('tax.rateInvalid'),
               },
             ]"

+ 220 - 138
src/views/device/toDaySugarList.vue

@@ -4,10 +4,13 @@
     <s-header :name="$t('device.todaysSugarList')" :noback="false"></s-header>
 
     <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 class="device-card" v-if="deviceDetail">
+        <div class="device-header">
+          <div class="header-indicator"></div>
+          <h3 class="device-name">
+            {{ $t("device.equipmentName") }}:{{ deviceDetail?.name || "-" }}
+          </h3>
+        </div>
       </div>
 
       <!-- 统计卡片 -->
@@ -17,40 +20,62 @@
             <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-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>
+              <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" />
+            <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-popup v-model:show="showFilter" position="right" class="filter-popup">
+      <van-popup
+        v-model:show="showFilter"
+        position="right"
+        class="filter-popup"
+      >
         <div class="popup-header">
-          <h3>{{ $t('device.makeRecord.condition') }}</h3>
+          <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 class="filter-item" v-if="deviceDetail == 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>
+            <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">
+                <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>
@@ -64,10 +89,18 @@
 
           <!-- 时间范围 -->
           <div class="filter-item">
-            <label>{{ $t('device.makeRecord.time') }}</label>
+            <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" />
+              <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>
@@ -75,29 +108,55 @@
         <!-- 底部操作 -->
         <div class="popup-footer">
           <van-button type="default" @click="handleReset">重置</van-button>
-          <van-button type="primary" @click="handleConfirmFilter">筛选</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-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-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
+        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)">
+      <van-list
+        class="sugar-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">
               <!-- 商品信息 -->
@@ -105,15 +164,19 @@
                 <h4 class="product-name">{{ item.productName }}</h4>
 
                 <!-- 设备编号 -->
-                <div class="device-info" v-if="deviceDetal == null">
-                  <span class="device-label">{{ $t('device.equipmentCodeLabel') }}:</span>
+                <div class="device-info" v-if="deviceDetail == 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>
+                  <span class="time-text">{{
+                    showDateTime(item.createDate)
+                  }}</span>
                 </div>
               </div>
 
@@ -126,10 +189,14 @@
           </template>
         </van-cell>
         <!-- 失败原因弹窗 -->
-        <van-dialog v-model:show="showReasonDialog" :title="$t('device.makeRecord.reason')" confirm-button-text="">
+        <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>
+            <p>{{ selectedItem?.note || $t("device.unknownError") }}</p>
           </div>
         </van-dialog>
       </van-list>
@@ -137,40 +204,26 @@
   </div>
 </template>
 <script>
-import {
-  onMounted,
-  reactive,
-  computed,
-  ref
-} from "vue";
+import { onMounted, reactive, computed, ref } from "vue";
 import sHeader from "@/components/SimpleHeader";
 import {
   getDeviceDetal,
   selectMakeList,
-  exportMakeList
+  exportMakeList,
 } from "@/service/device";
-import {
-  showFailToast
-} from "vant";
-import {
-  getLoginUser,
-  $M_ExportFile
-} from "@/common/js/utils";
-import {
-  useRoute
-} from 'vue-router';
+import { showFailToast } from "vant";
+import { getLoginUser, $M_ExportFile } from "@/common/js/utils";
+import { useRoute } from "vue-router";
 import dateUtil from "../../utils/dateUtil";
 import { useI18n } from "vue-i18n";
 
-
-
 export default {
   setup() {
     const { t } = useI18n();
     const route = useRoute();
     const deviceId = route.query.deviceId;
     const clientId = route.query.clientId;
-    const deviceDetal = ref(null);
+    const deviceDetail = ref(null);
     const loading = ref(false);
     const finished = ref(false);
     const user = getLoginUser();
@@ -185,25 +238,37 @@ export default {
     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 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
+      adminId: user.type == 2 ? user.id : "", // 用户账户id
       status: "", // 制作状态 1:制作中 2:制作失败 0:未制作
       clientId: clientId,
       current: 0, // 页数
@@ -222,26 +287,22 @@ export default {
 
     // 获取设备数据
     const getDeviceDetalFun = async () => {
-      const {
-        data
-      } = await getDeviceDetal({
-        id: deviceId
+      const { data } = await getDeviceDetal({
+        id: deviceId,
       });
-      if (data.code === '00000') {
-        deviceDetal.value = data.data;
+      if (data.code === "00000") {
+        deviceDetail.value = data.data;
       } else {
         showFailToast(data.message);
       }
-    }
+    };
 
     // 获取做糖列表数据
     const selectSugarListFun = async () => {
       finished.value = false;
       const { data } = await selectMakeList(searchParams);
       if (data.code) {
-        list.value = list.value.concat(
-          data.data.records
-        );
+        list.value = list.value.concat(data.data.records);
         total.value = data.data.total;
         if (list.value.length === data.data.total) {
           finished.value = true;
@@ -262,69 +323,82 @@ export default {
       if (!item) {
         return "";
       }
-      const currentDate = dateUtil.formateDate(new Date(item.modifyDate), "yyyy/MM/dd hh:mm:ss");
-      return t('device.goods') + item.productName + t('device.doTime') + currentDate + t('device.byDeice') + item.clientId.slice(-6);
-    }
+      const currentDate = dateUtil.formateDate(
+        new Date(item.modifyDate),
+        "yyyy/MM/dd hh:mm:ss"
+      );
+      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"));
+      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'
-      }
-    }
+        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');
+        case "1":
+          return t("device.makeRecord.success");
+        case "2":
+          return t("device.makeRecord.warning");
         default:
-          return t('device.makeRecord.waiting');
+          return t("device.makeRecord.waiting");
       }
-    }
+    };
 
     const statusIcon = (status) => {
       switch (status) {
-        case '1':
-          return 'checked';
-        case '2':
-          return 'warning';
+        case "1":
+          return "checked";
+        case "2":
+          return "warning";
         default:
-          return 'clock';
+          return "clock";
       }
-    }
+    };
 
     // 时间选择
     const handleStartConfirm = ({ selectedValues }) => {
-      console.log(selectedValues.join('/'));
-      searchParams.startTime = selectedValues.join('/');
+      console.log(selectedValues.join("/"));
+      searchParams.startTime = selectedValues.join("/");
       showStartPicker.value = false;
-    }
+    };
 
     // 时间选择
     const handleEndConfirm = ({ selectedValues }) => {
-      console.log(selectedValues.join('/'));
-      searchParams.endTime = selectedValues.join('/');
+      console.log(selectedValues.join("/"));
+      searchParams.endTime = selectedValues.join("/");
       showEndPicker.value = false;
-    }
+    };
 
     // 制作失败原因
     const handleItemClick = (item) => {
-      if (item.status === '2') { // 仅失败状态可点击
+      if (item.status === "2") {
+        // 仅失败状态可点击
         selectedItem.value = item;
         showReasonDialog.value = true;
       }
-    }
+    };
 
     // 重置
     const handleReset = () => {
@@ -332,7 +406,7 @@ export default {
       searchParams.startTime = "";
       searchParams.endTime = "";
       searchParams.clientId = clientId;
-    }
+    };
 
     // 确认
     const handleConfirmFilter = () => {
@@ -340,19 +414,20 @@ export default {
       searchParams.current = 1;
       list.value = [];
       selectSugarListFun();
-      console.log(finished.value);
-    }
+    };
 
     // 导出
     const handleExport = async () => {
-      const { headers, data } = await exportMakeList(Object.assign({}, searchParams));
+      const { headers, data } = await exportMakeList(
+        Object.assign({}, searchParams)
+      );
       $M_ExportFile(data, headers);
-    }
+    };
 
     return {
       searchParams,
       list,
-      deviceDetal,
+      deviceDetail,
       loading,
       finished,
       selectSugarListFun,
@@ -382,7 +457,7 @@ export default {
     };
   },
   components: {
-    sHeader
+    sHeader,
   },
 };
 </script>
@@ -394,29 +469,35 @@ export default {
 
   .content-container {
     background: #f5f6fa;
-    padding: 16px;
-    height: calc(100% - 55px);
+    height: calc(100% - 50px);
     overflow: auto;
     overflow-x: hidden;
   }
 
-  .device-header {
-    display: flex;
-    align-items: center;
-    padding: 12px;
+  .device-card {
     background: #fff;
-    border-radius: 8px;
-    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
+    border-radius: 12px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+    margin: 10px;
+    padding: 16px;
 
-    .device-icon {
-      font-size: 18px;
-      color: #409eff;
-      margin-right: 8px;
-    }
+    .device-header {
+      display: flex;
+      align-items: center;
 
-    .device-name {
-      font-size: 14px;
-      color: #333;
+      .header-indicator {
+        width: 3px;
+        height: 20px;
+        background: #4d6add;
+        margin-right: 12px;
+        border-radius: 2px;
+      }
+
+      .device-name {
+        font-size: 15px;
+        color: #404d74;
+        margin: 0;
+      }
     }
   }
 
@@ -424,7 +505,7 @@ export default {
     background: #ffffff;
     border-radius: 12px;
     padding: 18px;
-    margin: 16px 0;
+    margin: 10px;
     position: relative;
     overflow: hidden;
 
@@ -507,7 +588,6 @@ export default {
           background: #3b5ab3;
           box-shadow: 0 1px 3px rgba(78, 107, 221, 0.2);
         }
-
       }
 
       // 单独设置导出按钮颜色
@@ -521,11 +601,15 @@ export default {
     }
   }
 
+  .sugar-list {
+    margin: 10px;
+  }
+
   .sugar-item {
-    margin-top: 12px;
     border-radius: 8px;
     box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
     cursor: pointer;
+    margin-bottom: 10px;
 
     :deep(.van-cell__title) {
       overflow: visible;
@@ -584,7 +668,6 @@ export default {
       }
     }
 
-
     .status-box {
       padding: 4px 10px;
       border-radius: 14px;
@@ -696,7 +779,6 @@ export default {
         }
 
         .van-cell {
-
           // 选中状态样式
           &--selected {
             background-color: #f0f3ff;

+ 3 - 0
src/views/device/viewLogs/index.vue

@@ -122,6 +122,7 @@ export default {
     // const { t } = useI18n();
     const route = useRoute();
     const deviceId = route.query.deviceId;
+    const deviceName = ref("");
     const deviceDetail = ref(null);
     const showPicker = ref(false);
     const downloading = ref(false);
@@ -153,6 +154,7 @@ export default {
     onMounted(async () => {
       // 加载样式
       await getDeviceDetailFun();
+      deviceName.value = route.query.name;
     });
 
     // 选择时间
@@ -309,6 +311,7 @@ export default {
       fileName,
       message,
       channelType,
+      deviceName,
     };
   },
 };

+ 509 - 137
src/views/discountCode/index.vue

@@ -1,117 +1,252 @@
 <template>
-  <!-- 优惠码 -->
-  <div class="discountCodePage flex-col">
-    <s-header :name="$t('discountCode.discountCode')" :noback="false"></s-header>
-    <div class="discountCodeBox flex-col">
-      <van-list v-model:loading="loading" v-model:error="error" :error-text="$t('public.requestFailed')"
-        :finished="finished" :finished-text="$t('public.noMore')" offset="300" :immediate-check="false" @load="onLoad">
-        <div v-if="isDelete" class="searchRow flex-row justify-between">
-          <div class="l-flex-RC c-text-15 c-text-color c-text-b">
-            {{ $t("advertManage.advertRule.selected") }}
-            {{ selectTotals }}
-            {{ $t("advertManage.advertRule.individual") }}
-          </div>
-        </div>
-        <div v-else 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('discountCode.total') }}<span class="discountNumber">{{ discountCodeTotal
-              }}</span>{{ $t('discountCode.discountCodesInTotal') }}</span>
-            </div>
-          </div>
-          <div class="flex-col">
-            <div class="main5 flex-row justify-between" @click="searchClick">
-              <img class="label2" src="../../assets/device/searchIcon.png" />
-            </div>
-          </div>
-        </div>
-        <div class="tabButtomBox flex-row">
-          <div class="tabBox flex-row">
-            <span :class="{ active: isUse === '0' }" @click="setIsUse('0')" style="cursor: pointer;">{{
-              $t('discountCode.notUsed') }}</span>
-            <span :class="{ active: isUse === '1' }" @click="setIsUse('1')" style="cursor: pointer;">{{
-              $t('discountCode.used') }}</span>
-          </div>
-          <div v-if="isDelete" class="buttomBox flex-row">
-            <div @click="cancelClk" class="buttonItem" style="color: #9696b1;">
-              {{ $t("discountCode.cancel") }}
-            </div>
-            <div @click="allClk" class="buttonItem" style="color: #4b6ad1">
-              {{ $t("discountCode.selectAll") }}
-            </div>
-            <div @click="noticeClk" class="buttonItem" style="color: #df5e4c">
-              {{ $t("discountCode.confirmDel") }}
+  <div class="discount-code-page">
+    <!-- 头部导航 -->
+    <s-header :name="$t('discountCode.discountCode')" :noback="false" />
+
+    <!-- 主要内容区域 -->
+    <!-- 顶部操作栏 -->
+    <div class="top-action-bar" :class="{ 'delete-mode': isDelete }">
+      <div class="status-display">
+        <template v-if="isDelete">
+          <span>{{ $t("advertManage.advertRule.selected") }}</span>
+          <span class="count-highlight">{{ selectTotals }}</span>
+          <span>{{ $t("advertManage.advertRule.individual") }}</span>
+        </template>
+        <template v-else>
+          <span>{{ $t("discountCode.total") }}</span>
+          <span class="count-highlight">{{ discountCodeTotal }}</span>
+          <span>{{ $t("discountCode.discountCodesInTotal") }}</span>
+        </template>
+      </div>
+
+      <div class="action-buttons">
+        <template v-if="isDelete">
+          <van-button
+            size="small"
+            plain
+            class="action-button"
+            @click="cancelClk"
+          >
+            {{ $t("discountCode.cancel") }}
+          </van-button>
+          <van-button
+            size="small"
+            plain
+            type="primary"
+            class="action-button"
+            @click="allClk"
+          >
+            {{ $t("discountCode.selectAll") }}
+          </van-button>
+          <van-button
+            size="small"
+            type="danger"
+            class="action-button"
+            @click="noticeClk"
+          >
+            {{ $t("discountCode.confirmDel") }}
+          </van-button>
+        </template>
+        <template v-else>
+          <van-button
+            icon="search"
+            size="small"
+            plain
+            class="action-button"
+            @click="searchClick"
+          />
+          <van-button
+            icon="plus"
+            type="primary"
+            size="small"
+            plain
+            class="action-button"
+            @click="payCode"
+          >
+            {{ $t("discountCode.apply") }}
+          </van-button>
+          <van-button
+            icon="delete"
+            size="small"
+            type="danger"
+            plain
+            class="action-button"
+            @click="isDelete = true"
+          >
+            {{ $t("discountCode.delete") }}
+          </van-button>
+          <van-button
+            icon="down"
+            size="small"
+            plain
+            type="success"
+            class="action-button"
+            @click="clickExport"
+          >
+            {{ $t("discountCode.export") }}
+          </van-button>
+        </template>
+      </div>
+    </div>
+
+    <!-- 标签切换区域 -->
+    <div class="tab-section">
+      <div class="tab-buttons">
+        <van-button
+          size="small"
+          :plain="isUse !== '0'"
+          :type="isUse === '0' ? 'primary' : 'default'"
+          @click="setIsUse('0')"
+        >
+          {{ $t("discountCode.notUsed") }}
+        </van-button>
+        <van-button
+          size="small"
+          :plain="isUse !== '1'"
+          :type="isUse === '1' ? 'primary' : 'default'"
+          @click="setIsUse('1')"
+        >
+          {{ $t("discountCode.used") }}
+        </van-button>
+      </div>
+    </div>
+
+    <div class="discount-code-list">
+      <van-list
+        v-model:loading="loading"
+        v-model:error="error"
+        :error-text="$t('public.requestFailed')"
+        :finished="finished"
+        :finished-text="$t('public.noMore')"
+        :offset="300"
+        :immediate-check="false"
+        @load="onLoad"
+      >
+        <!-- 优惠码列表 -->
+        <div
+          v-for="(item, index) in discountCodeList"
+          :key="index"
+          class="code-card"
+          :class="{
+            used: item.isUse === '1',
+            expired: isExpired(item.lastUseDate),
+          }"
+          @click="disCountCodeClick(item)"
+        >
+          <div class="code-header">
+            <div class="code-value">
+              <span class="code-label"
+                >{{ $t("discountCode.discountCode") }}:</span
+              >
+              {{ item.code }}
             </div>
           </div>
-          <div v-else class="buttomBox flex-row">
-            <div class="buttonItem" @click="payCode()" style="color: #4b6ad1">{{ $t('discountCode.apply') }}</div>
-            <div class="buttonItem" @click="isDelete = true">{{ $t('discountCode.delete') }}</div>
-            <!-- <div class="buttonItem" @click="exportClick()" style="color: #45be2d">{{ $t('discountCode.export') }}</div> -->
-            <div class="buttonItem" @click="clickExport" style="color: #45be2d">{{ $t('discountCode.export') }}</div>
-          </div>
-        </div>
-        <div class="intervalRow"></div>
-        <div class="listBox">
-          <div v-for="(item, index) in discountCodeList" :key="index" class="listItem" @click="disCountCodeClick(item)">
-            <div class="itemBox">
-              <div class="itemRow">
-                <span class="itemTitle">
-                  {{ $t('discountCode.discountCode') }}:&nbsp;
-                </span>
-                {{ item.code }}
-                <span class="itemTitle discount">
-                  {{ item.type == '1' ? $t('discountCode.deduction') : $t('discountCode.discount') }}:&nbsp;
-                </span>
-                {{ item.discount }}
-              </div>
-              <div class="itemRow" v-if="item.isUse == '0'">
-                <span class="itemTitle">
-                  {{ $t('discountCode.creationTime') }}:&nbsp;
-                </span>
-                {{ showDateTime(item.createDate) }}
-              </div>
-              <div class="itemRow" v-if="user.type < 2">
-                <span class="itemTitle">
-                  {{ $t('discountCode.affiliatedMerchants') }}:&nbsp;
-                </span>
-                {{ item.userName }}
-              </div>
-              <div class="itemRow" v-if="item.isUse !== '0'">
-                <span class="itemTitle">
-                  {{ $t('discountCode.usageTime') }}:&nbsp;
-                </span>
-                {{ showDateTime(item.useDate) }}
-              </div>
-              <div class="itemRow" v-if="item.isUse == '0'">
-                <span class="itemTitle">
-                  {{ $t('discountCode.termOfValidity') }}:&nbsp;
-                </span>{{ showDate(item.lastUseDate) }}
-                <span v-if="item.isUse === '0' && isExpired(item.lastUseDate)">
-                  {{ $t('discountCode.expired') }}
-                </span>
+
+          <div class="code-details">
+            <!-- 创建时间 -->
+            <div v-if="item.isUse === '0'" class="detail-row">
+              <van-icon name="clock" class="detail-icon" />
+              <div class="detail-content">
+                <div class="detail-label">
+                  {{ $t("discountCode.creationTime") }}:
+                </div>
+                <div class="detail-value">
+                  {{ showDateTime(item.createDate) }}
+                </div>
               </div>
-              <div class="itemRow" v-if="item.isUse !== '0'">
-                <span class="itemTitle">
-                  {{ $t('discountCode.usingTheMachine') }}:&nbsp;
-                </span>
-                {{ item.useBy }}
+            </div>
+
+            <!-- 所属商户 -->
+            <div v-if="user.type < 2" class="detail-row">
+              <van-icon name="shop" class="detail-icon" />
+              <div class="detail-content">
+                <div class="detail-label">
+                  {{ $t("discountCode.affiliatedMerchants") }}:
+                </div>
+                <div class="detail-value">{{ item.userName || "-" }}</div>
               </div>
             </div>
-            <div class="tipBox" v-if="item.isUse === '0' && isDelete == false">
-              {{ $t('discountCode.notUsed') }}
+
+            <!-- 使用时间 -->
+            <div v-if="item.isUse !== '0'" class="detail-row">
+              <van-icon name="todo-list" class="detail-icon" />
+              <div class="detail-content">
+                <div class="detail-label">
+                  {{ $t("discountCode.usageTime") }}:
+                </div>
+                <div class="detail-value">
+                  {{ showDateTime(item.useDate) }}
+                </div>
+              </div>
             </div>
-            <div class="tipBox isUseTip" v-if="item.isUse === '1' && isDelete == false">
-              {{ $t('discountCode.used') }}
+
+            <!-- 有效期 -->
+            <div v-if="item.isUse === '0'" class="detail-row">
+              <van-icon name="todo-list" class="detail-icon" />
+              <div class="detail-content">
+                <div class="detail-label">
+                  {{ $t("discountCode.termOfValidity") }}:
+                </div>
+                <div class="detail-value">
+                  {{ showDate(item.lastUseDate) }}
+                  <van-tag
+                    v-if="isExpired(item.lastUseDate)"
+                    type="danger"
+                    size="small"
+                  >
+                    {{ $t("discountCode.expired") }}
+                  </van-tag>
+                </div>
+              </div>
             </div>
-            <div v-if="isDelete" class="checkedBox">
-              <van-checkbox v-model="item.checked"></van-checkbox>
+
+            <!-- 使用设备 -->
+            <div v-if="item.isUse !== '0'" class="detail-row">
+              <van-icon name="setting" class="detail-icon" />
+              <div class="detail-content">
+                <div class="detail-label">
+                  {{ $t("discountCode.usingTheMachine") }}:
+                </div>
+                <div class="detail-value">{{ item.useBy || "-" }}</div>
+              </div>
             </div>
           </div>
+
+          <!-- 状态标签 -->
+          <div v-if="!isDelete" class="status-tag">
+            <van-tag
+              :type="item.type === '1' ? 'danger' : 'warning'"
+              size="large"
+            >
+              {{
+                item.type == "1"
+                  ? $t("discountCode.deduction")
+                  : $t("discountCode.discount")
+              }}:
+              {{ item.discount }}
+            </van-tag>
+            <van-tag v-if="item.isUse === '0'" type="primary" size="large">
+              {{ $t("discountCode.notUsed") }}
+            </van-tag>
+            <van-tag v-else-if="item.isUse === '1'" type="success" size="large">
+              {{ $t("discountCode.used") }}
+            </van-tag>
+          </div>
+
+          <!-- 删除模式下的选择框 -->
+          <van-checkbox
+            v-if="isDelete"
+            v-model="item.checked"
+            class="selection-checkbox"
+            @click.stop
+          />
         </div>
       </van-list>
     </div>
+    <!-- 搜索弹出框 -->
     <codeSearch ref="searchRef" @search="search($event)"></codeSearch>
+
+    <!-- 导出功能 -->
     <codeExport ref="exportRef" @exportCode="exportCode($event)"></codeExport>
   </div>
 </template>
@@ -119,10 +254,14 @@
 <script>
 import { onMounted, reactive, toRefs, ref, computed } from "vue";
 import sHeader from "@/components/SimpleHeader";
-import { getdiscountCodeList, discountCodeExport, deleteCode } from "@/service/discountCode";
+import {
+  getdiscountCodeList,
+  discountCodeExport,
+  deleteCode,
+} from "@/service/discountCode";
 import { showFailToast, showToast, showConfirmDialog } from "vant";
-import { getLoginUser, $M_ExportFile, styleUrl } from "@/common/js/utils";
-import codeSearch from './codeSearch.vue';
+import { getLoginUser, $M_ExportFile } from "@/common/js/utils";
+import codeSearch from "./codeSearch.vue";
 import codeExport from "./codeExport.vue";
 import dateUtil from "@/utils/dateUtil";
 import { useRouter } from "vue-router";
@@ -144,13 +283,12 @@ export default {
       adminId: "", // 用户账户id
       current: 1, // 页数
       size: 20, // 页大小
-      isUse: '0', // 是否使用,0:未使用;1:已使用
+      isUse: "0", // 是否使用,0:未使用;1:已使用
       // type: null // 优惠码类型 0或null:折扣优惠码;1:抵扣价优惠码
     });
     // 初始化页面获取列表
     onMounted(async () => {
       //加载样式
-      styleUrl('discountCode');
       // 初始化列表及页码
       const user = getLoginUser();
       if (user) {
@@ -173,12 +311,19 @@ export default {
       discountCodeList.value = [];
       searchParams.current = 1;
       getList();
-    }
+    };
     // 滚动加载
-    const onLoad = () => { if (!finished.value) { searchParams.current = searchParams.current + 1; getList(); } };
+    const onLoad = () => {
+      if (!finished.value) {
+        searchParams.current = searchParams.current + 1;
+        getList();
+      }
+    };
     // 获取设备列表数据
     const getList = async () => {
-      const { data } = await getdiscountCodeList(Object.assign({}, searchParams));
+      const { data } = await getdiscountCodeList(
+        Object.assign({}, searchParams)
+      );
       if (data.code === "00000") {
         // 加入删除选中状态
         if (data.data.records.length > 0) {
@@ -187,31 +332,56 @@ export default {
           });
         }
         // 列表值叠加
-        discountCodeList.value = discountCodeList.value.concat(data.data.records);
+        discountCodeList.value = discountCodeList.value.concat(
+          data.data.records
+        );
         // console.log(discountCodeList.value);
         discountCodeTotal.value = data.data.total;
-        if (discountCodeList.value.length === data.data.total) { finished.value = true; }
+        if (discountCodeList.value.length === data.data.total) {
+          finished.value = true;
+        }
         loading.value = false;
-      } else { showFailToast(data.message); }
+      } else {
+        showFailToast(data.message);
+      }
     };
     // 搜索点击
-    const searchClick = () => { searchRef.value.showSearch(); };
+    const searchClick = () => {
+      searchRef.value.showSearch();
+    };
     // 导出点击
-    const clickExport = () => { exportRef.value.showExport(); }
+    const clickExport = () => {
+      exportRef.value.showExport();
+    };
     // 切换tab
-    const setIsUse = (data) => { searchParams.isUse = data; searchGetList(); }
+    const setIsUse = (data) => {
+      searchParams.isUse = data;
+      searchGetList();
+    };
     // 点击优惠码
-    const disCountCodeClick = (data) => { console.log('disCountCodeClick', data); }
+    const disCountCodeClick = (data) => {
+      console.log("disCountCodeClick", data);
+    };
     // 时间格式化
-    const showDate = (date) => { return date ? dateUtil.formateDate(new Date(date), "yyyy/MM/dd") : ''; }
-    const showDateTime = (date) => { return date ? dateUtil.formateDate(new Date(date), "yyyy/MM/dd hh:mm:ss") : ''; }
+    const showDate = (date) => {
+      return date ? dateUtil.formateDate(new Date(date), "yyyy/MM/dd") : "";
+    };
+    const showDateTime = (date) => {
+      return date
+        ? dateUtil.formateDate(new Date(date), "yyyy/MM/dd hh:mm:ss")
+        : "";
+    };
     // 优惠码导出
     const exportClick = async () => {
       searchParams.size = 3000;
-      const { headers, data } = await discountCodeExport(Object.assign({}, searchParams));
+      const { headers, data } = await discountCodeExport(
+        Object.assign({}, searchParams)
+      );
       $M_ExportFile(data, headers);
-    }
-    const payCode = () => { router.push({ path: '/payCode' }); };
+    };
+    const payCode = () => {
+      router.push({ path: "/payCode" });
+    };
 
     const isExpired = ref((lastUseDate) => {
       const currentDate = new Date();
@@ -258,8 +428,8 @@ export default {
         return;
       }
       showConfirmDialog({
-        title: t('advertManage.delPopTitle'),
-        message: t('advertManage.delPopContent'),
+        title: t("advertManage.delPopTitle"),
+        message: t("advertManage.delPopContent"),
       })
         .then(() => {
           const ids = [];
@@ -268,24 +438,26 @@ export default {
               ids.push(item.id);
             }
           });
-          deleteCode(ids).then((res) => {
-            console.log(res);
-            if (res.data.code === '00000') {
+          deleteCode(ids)
+            .then((res) => {
+              console.log(res);
+              if (res.data.code === "00000") {
+                isDelete.value = false;
+                showToast(t("discountCode.deletionSucceeded"));
+                setTimeout(() => {
+                  discountCodeList.value = [];
+                  getList();
+                }, 1500);
+              }
+            })
+            .catch(() => {
               isDelete.value = false;
-              showToast(t('discountCode.deletionSucceeded'));
-              setTimeout(() => {
-                discountCodeList.value = [];
-                getList();
-              }, 1500);
-            }
-          }).catch(() => {
-            isDelete.value = false;
-            showToast('ERROR');
-          });
+              showToast("ERROR");
+            });
         })
         .catch(() => {
           // showToast('ERROR');
-        })
+        });
     };
 
     return {
@@ -322,5 +494,205 @@ export default {
 </script>
 
 <style lang="less" scoped>
-@import "../../common/style/common.less";
+@primary-color: #4d6add;
+@error-color: #ff4d4f;
+
+.discount-code-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f7fa;
+}
+
+.top-action-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px;
+  margin: 12px;
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+
+  &.delete-mode {
+    background-color: #fff8f6;
+    border-left: 4px solid @error-color;
+  }
+
+  .status-display {
+    font-size: 14px;
+    color: #666;
+
+    .count-highlight {
+      font-weight: bold;
+      font-size: 16px;
+      color: @primary-color;
+      margin: 0 4px;
+    }
+  }
+
+  .action-buttons {
+    display: flex;
+    gap: 8px;
+  }
+
+  .action-button {
+    min-width: auto;
+    padding: 5px 10px;
+  }
+}
+
+.tab-section {
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+  padding: 15px;
+  margin: 12px;
+
+  .tab-buttons {
+    display: flex;
+    gap: 12px;
+
+    .van-button {
+      flex: 1;
+    }
+  }
+}
+
+.discount-code-list {
+  flex: 1;
+  overflow-y: auto;
+}
+
+.code-card {
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  padding: 16px;
+  position: relative;
+  overflow: hidden;
+  transition: all 0.3s;
+  margin: 12px;
+
+  &:hover {
+    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+    transform: translateY(-2px);
+  }
+
+  &.used {
+    background-color: #f6ffed;
+  }
+
+  &.expired {
+    border-left: 4px solid @error-color;
+    opacity: 0.8;
+  }
+
+  .code-header {
+    display: flex;
+    // justify-content: space-between;
+    gap: 10px;
+    align-items: center;
+    padding-bottom: 12px;
+    margin-bottom: 12px;
+    border-bottom: 1px dashed #f0f0f0;
+  }
+
+  .code-value {
+    font-size: 16px;
+    font-weight: bold;
+    color: #333;
+
+    .code-label {
+      font-weight: normal;
+      color: #666;
+      margin-right: 8px;
+    }
+  }
+
+  .code-type {
+    .van-tag {
+      border-radius: 4px;
+      font-weight: 500;
+    }
+  }
+
+  .code-details {
+    .detail-row {
+      display: flex;
+      margin-bottom: 10px;
+      align-items: flex-start;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+
+    .detail-icon {
+      font-size: 16px;
+      color: #999;
+      margin-right: 10px;
+      margin-top: 3px;
+      min-width: 16px;
+    }
+
+    .detail-content {
+      flex: 1;
+    }
+
+    .detail-label {
+      font-size: 13px;
+      color: #999;
+      margin-bottom: 2px;
+    }
+
+    .detail-value {
+      font-size: 14px;
+      color: #333;
+      line-height: 1.4;
+    }
+  }
+
+  .status-tag {
+    display: flex;
+    gap: 5px;
+    position: absolute;
+    top: 10px;
+    right: 10px;
+    z-index: 2;
+  }
+
+  .selection-checkbox {
+    position: absolute;
+    bottom: 10px;
+    right: 10px;
+    z-index: 2;
+  }
+}
+
+@media (max-width: 480px) {
+  .top-action-bar {
+    padding: 12px;
+    flex-direction: column;
+    align-items: stretch;
+    gap: 12px;
+
+    .action-buttons {
+      justify-content: space-between;
+    }
+  }
+
+  .code-card {
+    padding: 12px;
+
+    .code-value {
+      font-size: 15px;
+    }
+
+    .status-tag .van-tag {
+      font-size: 12px;
+      padding: 2px 6px;
+    }
+  }
+}
 </style>

+ 612 - 105
src/views/discountCode/payCode.vue

@@ -1,85 +1,274 @@
 <template>
-  <!-- 购买优惠码 -->
-  <div class="discountCodePage flex-col">
-    <s-header :name="$t('discountCode.applDiscCode')" :noback="false"></s-header>
-    <div class="discountCodeBox flex-col">
-      <van-field name="radio" :label="$t('discountCode.addMode')">
-        <template #input>
-          <van-radio-group v-model="addMode" direction="horizontal">
-            <van-radio name="1">{{ $t('discountCode.randomAdd') }}</van-radio>
-            <van-radio name="2">{{ $t('discountCode.oneceAdd') }}</van-radio>
-          </van-radio-group>
-        </template>
-      </van-field>
-      <van-form @submit="onSubmit">
-        <van-field v-model="number" name="number" :label="$t('discountCode.numberOfDiscountCodesLabel')"
-          :placeholder="$t('discountCode.numberOfDiscountCodesPlaceholder')" v-if="addMode == '1'">
-          <template #button>
-            <span>{{ $t('discountCode.aSingleGenerationCannotExceed') }}</span>
-          </template>
-        </van-field>
-        <van-field v-if='addMode === "2"' v-model="codeNum" type="number" :label="$t('discountCode.codeNum')"
-          maxlength="6" :placeholder="$t('discountCode.codeNumPlaceholder')">
-        </van-field>
-        <van-field v-model="month" name="number" :label="$t('discountCode.termOfValidity')"
-          :placeholder="$t('discountCode.pleaseEnterTheValidityPeriod')">
-          <template #button>
-            <span>{{ $t('discountCode.Months') }}<span>{{ $t('discountCode.noMoreThanMonths') }}</span></span>
-          </template>
-        </van-field>
-        <!-- <van-field readonly v-model="validDays" :label="$t('discountCode.validDays')" placeholder="" /> -->
-        <div class="van-cell van-field">
-          <div class="van-cell__title van-field__label"><span>{{ $t('discountCode.type') }}</span></div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="type" direction="horizontal">
-              <van-radio name="0" icon-size="18px">{{ $t('discountCode.discount2') }}</van-radio>
-              <van-radio name="1" icon-size="18px">{{ $t('discountCode.deductionRoll') }}</van-radio>
+  <div class="purchase-discount-page">
+    <!-- 头部导航 -->
+    <s-header :name="$t('discountCode.applDiscCode')" :noback="false" />
+
+    <!-- 主要内容区域 -->
+    <div class="purchase-container">
+      <!-- 添加方式选择 -->
+      <div class="form-section">
+        <div class="section-header">
+          <van-icon name="setting" class="section-icon" />
+          <span>{{ $t("discountCode.addMode") }}</span>
+        </div>
+
+        <van-radio-group v-model="addMode" class="mode-selector">
+          <van-radio name="1" icon-size="22px" class="mode-radio">
+            <div class="mode-content">
+              <div class="mode-icon">
+                <van-icon name="setting" size="24" />
+              </div>
+              <div class="mode-text">
+                <div class="mode-title">{{ $t("discountCode.randomAdd") }}</div>
+              </div>
+            </div>
+          </van-radio>
+
+          <div class="divider"></div>
+
+          <van-radio name="2" icon-size="22px" class="mode-radio">
+            <div class="mode-content">
+              <div class="mode-icon">
+                <van-icon name="setting-o" size="24" />
+              </div>
+              <div class="mode-text">
+                <div class="mode-title">{{ $t("discountCode.oneceAdd") }}</div>
+              </div>
+            </div>
+          </van-radio>
+        </van-radio-group>
+      </div>
+
+      <!-- 优惠码详细信息表单 -->
+      <van-form @submit="onSubmit" class="purchase-form">
+        <!-- 随机生成数量 -->
+        <div class="form-section" v-if="addMode === '1'">
+          <div class="section-header">
+            <van-icon name="records" class="section-icon" />
+            <span>{{ $t("discountCode.numberOfDiscountCodesLabel") }}</span>
+          </div>
+
+          <van-field
+            v-model="number"
+            name="number"
+            type="number"
+            :placeholder="$t('discountCode.numberOfDiscountCodesPlaceholder')"
+          >
+            <template #extra>
+              <div class="field-hint">
+                {{ $t("discountCode.aSingleGenerationCannotExceed") }}
+              </div>
+            </template>
+          </van-field>
+        </div>
+
+        <!-- 手动输入优惠码 -->
+        <div class="form-section" v-if="addMode === '2'">
+          <div class="section-header">
+            <van-icon name="records" class="section-icon" />
+            <span>{{ $t("discountCode.codeNum") }}</span>
+          </div>
+
+          <van-field
+            v-model="codeNum"
+            type="text"
+            maxlength="6"
+            :placeholder="$t('discountCode.codeNumPlaceholder')"
+          />
+        </div>
+
+        <!-- 有效期设置 -->
+        <div class="form-section">
+          <div class="section-header">
+            <van-icon name="clock" class="section-icon" />
+            <span>{{ $t("discountCode.termOfValidity") }}</span>
+          </div>
+
+          <van-field
+            v-model="month"
+            name="month"
+            type="number"
+            :placeholder="$t('discountCode.pleaseEnterTheValidityPeriod')"
+          >
+            <template #extra>
+              <div class="field-hint">
+                {{ $t("discountCode.Months") }}
+                <span class="highlight">{{
+                  $t("discountCode.noMoreThanMonths")
+                }}</span>
+              </div>
+            </template>
+          </van-field>
+        </div>
+
+        <!-- 优惠类型选择 -->
+        <div class="form-section">
+          <div class="section-header">
+            <van-icon name="discount" class="section-icon" />
+            <span>{{ $t("discountCode.type") }}</span>
+          </div>
+
+          <div class="discount-type">
+            <van-radio-group v-model="type" class="type-selector">
+              <div class="type-option">
+                <van-radio name="0" class="type-radio">
+                  <div class="type-content">
+                    <div class="type-icon">
+                      <van-icon name="coupon" color="#4b6ad1" />
+                    </div>
+                    <div class="type-text">
+                      <div class="type-title">
+                        {{ $t("discountCode.discount2") }}
+                      </div>
+                    </div>
+                  </div>
+                </van-radio>
+              </div>
+
+              <div class="type-option">
+                <van-radio name="1" class="type-radio">
+                  <div class="type-content">
+                    <div class="type-icon">
+                      <van-icon name="coupon" color="#e59a6d" />
+                    </div>
+                    <div class="type-text">
+                      <div class="type-title">
+                        {{ $t("discountCode.deductionRoll") }}
+                      </div>
+                    </div>
+                  </div>
+                </van-radio>
+              </div>
             </van-radio-group>
           </div>
         </div>
-        <van-field v-if='type === "1"' v-model="discount" type="number" name="number"
-          :label="$t('discountCode.deductionPriceLabdel')" :placeholder="$t('discountCode.deductionPricePlaceholder')">
-        </van-field>
-        <van-field v-if='type === "0"' v-model="discount" type="number" name="number"
-          :label="$t('discountCode.discount') + '(0-10)'" :placeholder="$t('discountCode.enterNumber')">
-          <template #button v-if="user.ifForeign === '1' && discount != ''">
-            <span>{{ $t('discountCode.sameAs') + (10 - discount) * 10 }}% off</span>
-          </template>
-        </van-field>
-        <div class="van-cell van-field" v-if='type === "0" && discount === "0" && promoCodeOpen != "0"'>
-          <div class="van-cell__title van-field__label"><span>{{ $t('discountCode.paymentMethod') }}</span></div>
-          <div class="van-cell__value van-field__value radioBox">
-            <van-radio-group v-model="frpCode" direction="horizontal">
-              <van-radio name="WEIXIN_NATIVE" icon-size="18px">{{ $t('discountCode.weChat') }}</van-radio>
-              <van-radio name="ALIPAY_NATIVE" icon-size="18px">{{ $t('discountCode.alipay') }}</van-radio>
+
+        <!-- 折扣/抵扣金额 -->
+        <div class="form-section">
+          <div class="section-header">
+            <van-icon name="discount" class="section-icon" />
+            <span>{{
+              type === "0"
+                ? $t("discountCode.discount") + " (0-10)"
+                : $t("discountCode.deductionPriceLabdel")
+            }}</span>
+          </div>
+
+          <van-field
+            v-model="discount"
+            type="number"
+            name="discount"
+            :placeholder="
+              type === '0'
+                ? $t('discountCode.enterNumber')
+                : $t('discountCode.deductionPricePlaceholder')
+            "
+          >
+            <template
+              #extra
+              v-if="user.ifForeign === '1' && discount !== '' && type === '0'"
+            >
+              <div class="discount-equivalent">
+                {{ $t("discountCode.sameAs") }}
+                <span class="highlight">{{ (10 - discount) * 10 }}% off</span>
+              </div>
+            </template>
+          </van-field>
+        </div>
+
+        <!-- 支付方式选择 -->
+        <div
+          class="form-section"
+          v-if="type === '0' && discount === '0' && promoCodeOpen !== '0'"
+        >
+          <div class="section-header">
+            <van-icon name="card" class="section-icon" />
+            <span>{{ $t("discountCode.paymentMethod") }}</span>
+          </div>
+
+          <div class="discount-type">
+            <van-radio-group v-model="frpCode" class="type-selector">
+              <div class="type-option">
+                <van-radio name="WEIXIN_NATIVE" class="type-radio">
+                  <div class="type-content">
+                    <div class="type-icon">
+                      <van-icon name="wechat" color="#07c160" />
+                    </div>
+                    <div class="type-text">
+                      <div class="type-title">
+                        {{ $t("discountCode.weChat") }}
+                      </div>
+                    </div>
+                  </div>
+                </van-radio>
+              </div>
+
+              <div class="type-option">
+                <van-radio name="ALIPAY_NATIVE" class="type-radio">
+                  <div class="type-content">
+                    <div class="type-icon">
+                      <van-icon name="alipay" color="#1775f9" />
+                    </div>
+                    <div class="type-text">
+                      <div class="type-title">
+                        {{ $t("discountCode.alipay") }}
+                      </div>
+                    </div>
+                  </div>
+                </van-radio>
+              </div>
             </van-radio-group>
           </div>
         </div>
-        <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('discountCode.apply') }}</van-button>
-        </van-row>
+
+        <!-- 提交按钮 -->
+        <div class="submit-section">
+          <van-button
+            round
+            block
+            type="primary"
+            native-type="submit"
+            icon="success"
+            size="large"
+            class="submit-button"
+          >
+            {{ $t("discountCode.apply") }}
+          </van-button>
+        </div>
       </van-form>
     </div>
-    <van-popup v-model:show="payCodeType" round class="payCodePopup" @close="payCodeClose">
-      <div style="text-align: center; font-size: 14px; padding: 1em; line-height: 1.8;">
-        <div>price: {{ ewmObj.price || '' }}</div>
-        <div>sn: {{ ewmObj.sn || '' }}</div>
-        <img :src="ewmObj.image" />
+
+    <!-- 支付二维码弹窗 -->
+    <van-popup
+      v-model:show="payCodeType"
+      round
+      class="payment-popup"
+    >
+      <div class="payment-details">
+        <div class="payment-item">
+          <span class="payment-label">价格</span>
+          <span class="payment-value">{{ ("¥" + ewmObj.price) || "" }}</span>
+        </div>
+
+        <div class="qrcode-container">
+          <img :src="ewmObj.image" class="qrcode-image" alt="支付二维码" />
+          <div class="payment-tips">支付二维码</div>
+        </div>
+
+        <div class="close-button">
+          <van-button round plain type="default" @click="payCodeClose">
+            关闭
+          </van-button>
+        </div>
       </div>
     </van-popup>
   </div>
 </template>
-
 <script>
 import sHeader from "@/components/SimpleHeader";
 import { onMounted, reactive, toRefs, ref, watch } from "vue";
 import { getLoginUser, Format_calcuDecial, styleUrl } from "@/common/js/utils";
-import {
-  showFailToast,
-  showSuccessToast,
-  showToast
-} from "vant";
+import { showFailToast, showSuccessToast, showToast } from "vant";
 import { addCode } from "@/service/discountCode";
 import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
@@ -92,29 +281,29 @@ export default {
     const { t } = useI18n();
     const router = useRouter();
     let payParams = reactive({
-      addMode: '1', // 生成方式
-      codeNum: '', // 优惠码
-      number: '', // 优惠码个数
-      month: '', // 有效期
-      type: '0', // 优惠卷类型
-      discount: '', // 折扣
-      frpCode: 'WEIXIN_NATIVE', // 支付方式
+      addMode: "1", // 生成方式
+      codeNum: "", // 优惠码
+      number: "", // 优惠码个数
+      month: "", // 有效期
+      type: "0", // 优惠卷类型
+      discount: "", // 折扣
+      frpCode: "WEIXIN_NATIVE", // 支付方式
     });
     const payCodeType = ref(false);
     const ewmObj = ref(null);
-    const promoCodeOpen = ref(''); // 0折优惠码开通状态
+    const promoCodeOpen = ref(""); // 0折优惠码开通状态
     const user = getLoginUser();
 
     // 初始化页面
     onMounted(async () => {
       //加载样式
-      styleUrl('discountCode');
+      styleUrl("discountCode");
       payCodeType.value = false;
-      payParams.number = '';
-      payParams.month = '';
-      payParams.type = '0';
-      payParams.discount = '';
-      payParams.frpCode = 'WEIXIN_NATIVE';
+      payParams.number = "";
+      payParams.month = "";
+      payParams.type = "0";
+      payParams.discount = "";
+      payParams.frpCode = "WEIXIN_NATIVE";
       if (user) {
         payParams.adminId = user.id;
         promoCodeOpen.value = user.promoCodeOpen;
@@ -124,56 +313,90 @@ export default {
 
     // 申请优惠码
     const onSubmit = async () => {
-      if (payParams.addMode === '1') {
-        payParams.codeNum = '';
-        if (payParams.number === '') { showToast(t('discountCode.numberOfDiscountCodesPlaceholder')); return; }
+      if (payParams.addMode === "1") {
+        payParams.codeNum = "";
+        if (payParams.number === "") {
+          showToast(t("discountCode.numberOfDiscountCodesPlaceholder"));
+          return;
+        }
       } else {
         payParams.number = "1";
-        if (payParams.codeNum.length != 6) { showToast(t('discountCode.codeNumPlaceholder')); return; }
+        if (payParams.codeNum.length != 6) {
+          showToast(t("discountCode.codeNumPlaceholder"));
+          return;
+        }
       }
-      if (payParams.month === '') { showToast(t('discountCode.pleaseEnterTheValidityPeriod')); return; }
-      if (payParams.type === '1' && payParams.discount === '') { showToast(t('discountCode.deductionPricePlaceholder')); return; }
-      if (payParams.type === '0' && payParams.discount === '') { showToast(t('discountCode.pleaseEnterDiscount')); return; }
-      if (parseInt(payParams.number) > 200) { showToast(t('discountCode.theNumberOfDiscountCodesCannotExceed')); return; }
-      if (parseInt(payParams.month) > 3) { showToast(t('discountCode.theValidityPeriodCannotExceedMonths')); return; }
-      if (payParams.type === '0' && parseInt(payParams.discount) > 10) { showToast(t('discountCode.discountCannotBeGreaterThan')); return; }
-      if (payParams.type === '1' && payParams.discount == 0) {
-        showToast(t('discountCode.deductionRollNoZero'));
+      if (payParams.month === "") {
+        showToast(t("discountCode.pleaseEnterTheValidityPeriod"));
+        return;
+      }
+      if (payParams.type === "1" && payParams.discount === "") {
+        showToast(t("discountCode.deductionPricePlaceholder"));
+        return;
+      }
+      if (payParams.type === "0" && payParams.discount === "") {
+        showToast(t("discountCode.pleaseEnterDiscount"));
+        return;
+      }
+      if (parseInt(payParams.number) > 200) {
+        showToast(t("discountCode.theNumberOfDiscountCodesCannotExceed"));
+        return;
+      }
+      if (parseInt(payParams.month) > 3) {
+        showToast(t("discountCode.theValidityPeriodCannotExceedMonths"));
+        return;
+      }
+      if (payParams.type === "0" && parseInt(payParams.discount) > 10) {
+        showToast(t("discountCode.discountCannotBeGreaterThan"));
+        return;
+      }
+      if (payParams.type === "1" && payParams.discount == 0) {
+        showToast(t("discountCode.deductionRollNoZero"));
         return;
       }
       console.log("payParams", payParams);
       const { data } = await addCode(Object.assign({}, payParams));
-      if (payParams.type === '0' && payParams.discount === '0' && promoCodeOpen.value != '0') {
+      if (
+        payParams.type === "0" &&
+        payParams.discount === "0" &&
+        promoCodeOpen.value != "0"
+      ) {
         console.log("data", data);
         ewmObj.value = data.data;
         payCodeType.value = true;
       } else {
         console.log("data", data);
-        if (data.code === '00000') {
-          showSuccessToast(t('discountCode.successfulProductionOfDiscountCode'));
+        if (data.code === "00000") {
+          showSuccessToast(
+            t("discountCode.successfulProductionOfDiscountCode")
+          );
           // router.push({ path: '/discountCode' });
           setTimeout(() => {
             router.go(-1);
           }, 1500);
         } else {
-          if (data.code === 'A0002') {
-            showFailToast(t('discountCode.existsCode'));
+          if (data.code === "A0002") {
+            showFailToast(t("discountCode.existsCode"));
           } else {
             showFailToast(data.message);
           }
         }
       }
-    }
+    };
     const payCodeClose = () => {
-      // router.push({ path: '/discountCode' }); 
-      router.go(-1);
-    }
+      payCodeType.value = false;
+    };
     // 有效天数
-    const validDays = ref('0' + t('discountCode.days'));
+    const validDays = ref("0" + t("discountCode.days"));
     // 监听有效期,计算有效天数
-    watch(() => payParams.month, (newVal) => {
-      validDays.value = Format_calcuDecial(newVal, 30) + t('discountCode.days');
-    }, { immediate: true, deep: true });
+    watch(
+      () => payParams.month,
+      (newVal) => {
+        validDays.value =
+          Format_calcuDecial(newVal, 30) + t("discountCode.days");
+      },
+      { immediate: true, deep: true }
+    );
     return {
       ...toRefs(payParams),
       payCodeType,
@@ -189,5 +412,289 @@ export default {
 </script>
 
 <style lang="less" scoped>
-@import "../../common/style/common.less";
+@primary-color: #4d6add;
+@error-color: #ff4d4f;
+.purchase-discount-page {
+  background-color: #f5f7fa;
+  min-height: 100vh;
+}
+
+.purchase-container {
+  background: #f5f6fa;
+  height: calc(100% - 50px);
+  overflow: auto;
+  overflow-x: hidden;
+}
+
+.form-section {
+  background-color: white;
+  border-radius: 16px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+  padding: 16px;
+  margin: 12px;
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  padding-bottom: 12px;
+  margin-bottom: 12px;
+  border-bottom: 1px solid #f0f0f0;
+  font-size: 16px;
+  font-weight: 500;
+  color: #333;
+
+  .section-icon {
+    margin-right: 10px;
+    color: @primary-color;
+    font-size: 18px;
+  }
+}
+
+.mode-selector {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .mode-radio {
+    margin-bottom: 12px;
+    padding: 0;
+
+    :deep(.van-radio__label) {
+      width: 100%;
+      margin-left: 30px;
+    }
+  }
+
+  .mode-content {
+    display: flex;
+    align-items: center;
+    width: 100%;
+  }
+
+  .mode-icon {
+    flex: 0 0 40px;
+    height: 40px;
+    border-radius: 10px;
+    background-color: #f0f5ff;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 15px;
+
+    .van-icon {
+      color: @primary-color;
+    }
+  }
+
+  .mode-title {
+    font-size: 15px;
+    font-weight: 500;
+    color: #333;
+    margin-bottom: 4px;
+  }
+
+  .mode-desc {
+    font-size: 12px;
+    color: #999;
+    line-height: 1.4;
+  }
+
+  .divider {
+    height: 1px;
+    background-color: #f0f0f0;
+    margin: 10px 0;
+  }
+}
+
+.purchase-form {
+  // margin: 12px;
+
+  .van-cell {
+    border-radius: 10px;
+    background-color: #fafafa;
+    margin-bottom: 12px;
+  }
+
+  .field-hint {
+    font-size: 12px;
+    color: #999;
+
+    .highlight {
+      color: @error-color;
+      font-weight: 500;
+    }
+  }
+}
+
+.discount-type {
+  padding: 0 5px;
+
+  .type-option {
+    background-color: #f9fafb;
+    border-radius: 10px;
+    overflow: hidden;
+    margin-bottom: 12px;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  .type-radio {
+    width: 100%;
+    padding: 12px 15px;
+  }
+
+  .type-content {
+    display: flex;
+    align-items: center;
+  }
+
+  .type-icon {
+    flex: 0 0 40px;
+    height: 40px;
+    border-radius: 10px;
+    background-color: rgba(75, 106, 209, 0.1);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 15px;
+
+    .van-icon {
+      font-size: 20px;
+    }
+  }
+
+  .type-text {
+    flex: 1;
+  }
+
+  .type-title {
+    font-size: 15px;
+    font-weight: 500;
+    color: #333;
+  }
+
+  .type-desc {
+    font-size: 12px;
+    color: #888;
+    margin-top: 4px;
+  }
+}
+
+.discount-equivalent {
+  font-size: 13px;
+  color: #666;
+
+  .highlight {
+    color: @primary-color;
+    font-weight: 500;
+  }
+}
+
+.payment-selector {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.payment-option {
+  flex: 1 1 45%;
+
+  :deep(.van-radio__label) {
+    padding: 0;
+    margin: 0;
+  }
+
+  .payment-content {
+    background: #f9fafb;
+    border-radius: 10px;
+    padding: 15px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    text-align: center;
+  }
+
+  .payment-icon {
+    width: 40px;
+    height: 40px;
+    margin-bottom: 8px;
+  }
+}
+
+.submit-section {
+  margin: 20px 10px;
+
+  .submit-button {
+    height: 50px;
+    font-size: 16px;
+    font-weight: 500;
+    box-shadow: 0 4px 12px rgba(75, 106, 209, 0.3);
+
+    &:active {
+      opacity: 0.9;
+    }
+  }
+}
+
+.payment-popup {
+  width: 90%;
+  max-width: 300px;
+  padding: 20px;
+
+  .payment-details {
+    text-align: center;
+  }
+
+  .payment-item {
+    margin-bottom: 12px;
+
+    .payment-label {
+      font-weight: 500;
+      color: #666;
+      margin-right: 8px;
+    }
+
+    .payment-value {
+      font-weight: 600;
+      color: @primary-color;
+    }
+  }
+
+  .qrcode-container {
+    margin: 20px auto;
+    padding: 10px;
+    border: 1px solid #f0f0f0;
+    border-radius: 8px;
+    max-width: 250px;
+  }
+
+  .qrcode-image {
+    width: 100%;
+    max-width: 200px;
+    height: auto;
+  }
+
+  .payment-tips {
+    font-size: 14px;
+    color: #666;
+    margin-top: 10px;
+  }
+
+  .close-button {
+    margin-top: 20px;
+
+    .van-button {
+      padding: 0 25px;
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .payment-option {
+    flex: 1 1 100%;
+  }
+}
 </style>

+ 541 - 234
src/views/distributionSet/detail.vue

@@ -1,180 +1,274 @@
 <template>
-  <div class="distributionDetailIdx">
-    <s-header :name="title" :noback="false"></s-header>
-    <div class="cust_vantBorder">
-      <div class="kBordBott">
-        <template v-if="clientId">
-          <van-field disabled colon :border="false" v-model="cofficentForm.clientId" clearable label-width="110"
-            name="clientId" :label="$t('distributionSet.addDist.clientId')"
-            :placeholder="$t('distributionSet.addDist.autoBrought')" />
-        </template>
-        <template v-else>
-          <van-field colon :border="false" required @click-input="busiInpClk" readonly clearable label-width="110"
-            v-model="cofficentForm.clientId" :placeholder="$t('distributionSet.addDist.clientIdPlace')"
-            :label="$t('distributionSet.addDist.clientId')" name="clientId"
-            :rules="[{ required: true, message: $t('distributionSet.addDist.clientIdPlace') }]">
-            <template #right-icon>
-              <div class="l-flex-RC">
-                <van-icon v-if="cofficentForm.clientId" @click="cofficentForm.clientId = ''" class="o-mr-6"
-                  name="clear" />
-                <van-icon @click="busiInpClk" name="arrow-down" />
-              </div>
-            </template>
-          </van-field>
-        </template>
-        <div class="l-flex-RC o-p-5 c-text-13 c-text-b">
-          <div v-if="proportionCheck"
-            style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-            {{ $t('distributionSet.addDist.pendingRecord') }}
-          </div>
-          <div v-else style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-            {{ $t('distributionSet.addDist.distInof') }}
-          </div>
-        </div>
-      </div>
-      <div v-if="proportionCheck">
-        <div class="l-flex-RC" style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-          <div class="c-text-13">
-            &nbsp;&nbsp;{{ $t('device.proportionOfPlatformDistributionLabel') }}:
-          </div>
-          <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.adminProportion }}%</div>
-        </div>
-        <div class="l-flex-RC" style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-          <div class="c-text-13">
-            &nbsp;&nbsp;{{ $t('distributionSet.addDist.myDistProport') }}:
-          </div>
-          <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.proportion }}%</div>
-        </div>
-        <div v-if="proportionCheck.agencyId" class="l-flex-RC" style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-          <div class="l-flex-RC o-pr-20">
-            <div class="c-text-13">
-              &nbsp;&nbsp;{{ $t('device.accountNoOfDistributorLabel') }}:
-            </div>
-            <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.agencyName }}</div>
-          </div>
-          <div class="l-flex-RC">
-            <div class="c-text-13" >
-              &nbsp;&nbsp;{{ $t('device.distributionProportionLabel') }}:
-            </div>
-            <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.agencyProportion }}%</div>
-          </div>
+  <div class="distribution-detail">
+    <s-header :name="title" :noback="false" />
+
+    <div class="content-container">
+      <!-- 主要内容区域 -->
+      <div class="detail-container">
+        <!-- 客户ID输入区域 -->
+        <div class="input-section">
+          <template v-if="clientId">
+            <van-field
+              disabled
+              v-model="cofficentForm.clientId"
+              :label="$t('distributionSet.addDist.clientId')"
+            />
+          </template>
+          <template v-else>
+            <van-field
+              :border="false"
+              required
+              readonly
+              v-model="cofficentForm.clientId"
+              :label="$t('distributionSet.addDist.clientId')"
+              :placeholder="$t('distributionSet.addDist.clientIdPlace')"
+              :rules="[
+                {
+                  required: true,
+                  message: $t('distributionSet.addDist.clientIdPlace'),
+                },
+              ]"
+              @click-input="busiInpClk"
+            >
+              <template #right-icon>
+                <div class="field-icons">
+                  <van-icon
+                    v-if="cofficentForm.clientId"
+                    @click.stop="cofficentForm.clientId = ''"
+                    name="clear"
+                  />
+                  <van-icon @click.stop="busiInpClk" name="arrow-down" />
+                </div>
+              </template>
+            </van-field>
+          </template>
         </div>
-        <div v-if="proportionCheck.merchantId" class="l-flex-RC" style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-          <div class="l-flex-RC o-pr-20">
-            <div class="c-text-13">
-              &nbsp;&nbsp;{{ $t('device.accountNoOfDistributorLabel') }}:
-            </div>
-            <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.merchantName }}</div>
-          </div>
-          <div class="l-flex-RC">
-            <div class="c-text-13" >
-              &nbsp;&nbsp;{{ $t('device.distributionProportionLabel') }}:
-            </div>
-            <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.merchantProportion }}%</div>
+
+        <!-- 信息展示区域 -->
+        <div class="info-section">
+          <div class="section-title">
+            <van-icon name="todo-list-o" size="20" />
+            {{
+              proportionCheck
+                ? $t("distributionSet.addDist.pendingRecord")
+                : $t("distributionSet.addDist.distInof")
+            }}
           </div>
-        </div>
-        <div v-if="proportionCheck.personageId" class="l-flex-RC" style="padding: var(--van-cell-vertical-padding) var(--van-cell-horizontal-padding);">
-          <div class="l-flex-RC o-pr-20">
-            <div class="c-text-13">
-              &nbsp;&nbsp;{{ $t('device.accountNoOfDistributorLabel') }}:
+
+          <!-- 比例展示模式 -->
+          <template v-if="proportionCheck">
+            <div class="distributor-group">
+              <div class="proportion-row">
+                <span>{{
+                  $t("device.proportionOfPlatformDistributionLabel")
+                }}</span>
+                <span class="percentage"
+                  >{{ proportionCheck.adminProportion }}%</span
+                >
+              </div>
+              <div class="proportion-row">
+                <span>{{ $t("distributionSet.addDist.myDistProport") }}</span>
+                <span class="percentage"
+                  >{{ proportionCheck.proportion }}%</span
+                >
+              </div>
             </div>
-            <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.personageName }}</div>
-          </div>
-          <div class="l-flex-RC">
-            <div class="c-text-13" >
-              &nbsp;&nbsp;{{ $t('device.distributionProportionLabel') }}:
+
+            <!-- 动态分销商信息 -->
+            <div
+              v-for="distributor in activeDistributors"
+              :key="distributor.type"
+              class="distributor-group"
+            >
+              <div class="distributor-row">
+                <span>{{ $t("device.accountNoOfDistributorLabel") }}</span>
+                <span class="value">{{ distributor.name }}</span>
+              </div>
+              <div class="distributor-row">
+                <span>{{ $t("device.distributionProportionLabel") }}</span>
+                <span class="percentage">{{ distributor.proportion }}%</span>
+              </div>
             </div>
-            <div class="c-text-14 c-text-b c-text-color">{{ proportionCheck.personageProportion }}%</div>
-          </div>
-        </div>
-        <div style="margin: 50px;">
-          <van-button round block type="primary" @click="cancelDistri(proportionCheck.id)">
-            {{ $t('device.cancelForApproval') }}
-          </van-button>
-        </div>
-      </div>
-      <div v-else>
-        <van-form show-error-message @submit="onSubmit">
-          <div class="noPaddingTopCell l-flex-RC">
-            <van-field type="digit" colon :border="false" required v-model="cofficentForm.distProp" clearable
-              label-width="110" name="distProp" :label="$t('device.proportionOfPlatformDistributionLabel')"
-              :placeholder="$t('device.proportionOfPlatformDistributionPlaceholder')"
-              :rules="[{ required: true, message: $t('device.proportionOfPlatformDistributionPlaceholder') }, { required: true, validator: distPropVali, message: $t('distributionSet.addDist.platDistRange'), trigger: ['onChange', 'onBlur', 'onSubmit'] }]" />
-            <span class="o-pl-10 c-text-18 c-text-b" style="color: #434D74;">%</span>
-          </div>
-          <div class="l-flex-RC">
-            <div class="c-text-13 c-text-b c-text-color"
-              style="padding: 15px">
-              &nbsp;&nbsp;{{ $t('distributionSet.addDist.myDistProport') }}:
+
+            <div class="action-button">
+              <van-button
+                round
+                block
+                type="primary"
+                @click="cancelDistri(proportionCheck.id)"
+              >
+                {{ $t("device.cancelForApproval") }}
+              </van-button>
             </div>
-            <div class="c-text-13 o-pl-2 c-text-b c-text-color">{{ myDistProp }}%</div>
-          </div>
-          <div v-for="(item, index) in distPropList" :key="index" class="kBordBott">
-            <van-field colon :border="false" required v-model="item.name1" clearable label-width="110" name="name1"
-              :label="$t('device.accountNoOfDistributorLabel')"
-              :placeholder="$t('device.accountNoOfDistributorPlaceholder')"
-              :rules="[{ required: true, message: $t('device.accountNoOfDistributorPlaceholder') }]" />
-            <div class="l-flex-between noPaddingRCell">
-              <div class="l-flex-RC">
-                <van-field type="digit" colon :border="false" required v-model="item.name2" clearable label-width="110"
-                  name="name2" :label="$t('device.distributionProportionLabel')"
-                  :placeholder="$t('device.distributionProportionPlaceholder')"
-                  :rules="[{ required: true, message: $t('device.distributionProportionPlaceholder') }, { required: true, validator: distPropVali, message: $t('distributionSet.addDist.distPropRange'), trigger: ['onChange', 'onBlur', 'onSubmit'] }]" />
-                <span class="o-pl-10 c-text-18 c-text-b" style="color: #434D74;">%</span>
+          </template>
+
+          <!-- 表单编辑模式 -->
+          <van-form v-else @submit="onSubmit" class="edit-form">
+            <div class="form-group">
+              <div class="distributor-input">
+                <div class="input-with-unit">
+                  <van-field
+                    type="digit"
+                    clearable
+                    required
+                    v-model="cofficentForm.distProp"
+                    :label="$t('device.proportionOfPlatformDistributionLabel')"
+                    :placeholder="
+                      $t('device.proportionOfPlatformDistributionPlaceholder')
+                    "
+                    :rules="[
+                      {
+                        required: true,
+                        message: $t(
+                          'device.proportionOfPlatformDistributionPlaceholder'
+                        ),
+                      },
+                      {
+                        required: true,
+                        validator: distPropVali,
+                        message: $t('distributionSet.addDist.platDistRange'),
+                        trigger: ['onChange', 'onBlur', 'onSubmit'],
+                      },
+                    ]"
+                  >
+                    <template #extra>
+                      <span class="unit">%</span>
+                    </template>
+                  </van-field>
+                </div>
+
+                <div class="my-proportion">
+                  <span class="label">{{
+                    $t("distributionSet.addDist.myDistProport")
+                  }}</span>
+                  <span class="percentage o-pl-25">{{ myDistProp }} %</span>
+                </div>
               </div>
-              <div @click="toDele(index)" class="l-flex-RC" style="color: #FE5D55;">
-                <van-icon size="18" name="delete-o" />
-                <span class="c-text-12">{{ $t('device.delete') }}</span>
+
+              <!-- 动态分销商输入 -->
+              <div
+                v-for="(item, index) in distPropList"
+                :key="index"
+                class="distributor-input"
+              >
+                <van-field
+                  clearable
+                  required
+                  v-model="item.name1"
+                  :label="$t('device.accountNoOfDistributorLabel')"
+                  :placeholder="$t('device.accountNoOfDistributorPlaceholder')"
+                  :rules="[
+                    {
+                      required: true,
+                      message: $t('device.accountNoOfDistributorPlaceholder'),
+                    },
+                  ]"
+                />
+
+                <div class="proportion-input">
+                  <div class="input-with-unit">
+                    <van-field
+                      type="digit"
+                      required
+                      clearable
+                      v-model="item.name2"
+                      :label="$t('device.distributionProportionLabel')"
+                      :placeholder="
+                        $t('device.distributionProportionPlaceholder')
+                      "
+                      :rules="[
+                        {
+                          required: true,
+                          message: $t(
+                            'device.distributionProportionPlaceholder'
+                          ),
+                        },
+                        {
+                          required: true,
+                          validator: distPropVali,
+                          message: $t('distributionSet.addDist.distPropRange'),
+                          trigger: ['onChange', 'onBlur', 'onSubmit'],
+                        },
+                      ]"
+                    >
+                      <template #extra>
+                        <span class="unit">%</span>
+                      </template></van-field
+                    >
+                  </div>
+                  <div class="delete-btn" @click="toDele(index)">
+                    <van-icon name="delete-o" size="20" />
+                  </div>
+                </div>
+              </div>
+
+              <div
+                v-if="distPropList.length <= 2"
+                class="add-button"
+                @click="toAdd"
+              >
+                +{{ $t("device.continueToAddDistributors") }}
               </div>
             </div>
-          </div>
-          <div v-if="distPropList.length <= 2" @click="toAdd"
-            class="kBordBott l-flex-center o-ptb-20 c-text-14 c-text-w6">
-            +{{ $t('device.continueToAddDistributors') }}
-          </div>
-          <div style="margin: 50px;">
-            <van-button round block type="primary" native-type="submit">
-              {{ $t('device.submitForApproval') }}
-            </van-button>
-          </div>
-        </van-form>
+
+            <div class="action-button">
+              <van-button round block type="primary" native-type="submit">
+                {{ $t("device.submitForApproval") }}
+              </van-button>
+            </div>
+          </van-form>
+        </div>
       </div>
     </div>
-    <!-- 设备编码选择框 -->
+
+    <!-- 设备编码选择弹窗 -->
     <van-popup v-model:show="busiPopShow" position="bottom">
-      <van-picker :title="$t('distributionSet.addDist.clientIdPlace')" :columns="busiPopList"
-        :columns-field-names="busiPopFieldName" @confirm="busiPopConfirm" @cancel="busiPopCancel" />
+      <van-picker
+        :title="$t('distributionSet.addDist.clientIdPlace')"
+        :columns="busiPopList"
+        :columns-field-names="busiPopFieldName"
+        @confirm="busiPopConfirm"
+        @cancel="busiPopCancel"
+      />
     </van-popup>
   </div>
 </template>
 
 <script>
-import { getEquipmentList } from '@/service/typeSelectList';
-import { Api_getDistriDetail, Api_getCheckPending, Api_cancelDistri } from "@/service/distributionSet";
-import { saveProportion, getProportion } from '@/service/device';
+import { getEquipmentList } from "@/service/typeSelectList";
+import {
+  Api_getDistriDetail,
+  Api_getCheckPending,
+  Api_cancelDistri,
+} from "@/service/distributionSet";
+import { saveProportion, getProportion } from "@/service/device";
 import sHeader from "@/components/SimpleHeader";
-import { computed, onMounted, reactive, ref } from 'vue';
+import { computed, onMounted, reactive, ref } from "vue";
 import { getLoginUser } from "@/common/js/utils";
 import { useRoute, useRouter } from "vue-router";
-import { useI18n } from 'vue-i18n';
-import { styleUrl } from '../../common/js/utils';
-import { showToast, showConfirmDialog, showSuccessToast, showFailToast } from "vant";
+import { useI18n } from "vue-i18n";
+import {
+  showToast,
+  showConfirmDialog,
+  showSuccessToast,
+  showFailToast,
+} from "vant";
 
 export default {
   components: { sHeader },
   setup() {
     const route = useRoute();
     // 定义设备id,如果路由中有,那就是设备编辑里面进来的
-    const clientId = ref('');
+    const clientId = ref("");
     // 待审核记录
     const proportionCheck = ref(null);
-    const id = route.query.id || '';
-    const type = route.query.type || '';
+    const id = route.query.id || "";
+    const type = route.query.type || "";
     onMounted(() => {
       // 加载样式
-      styleUrl('distributionSet');
+      // styleUrl('distributionSet');
 
-      clientId.value = route.query.clientId || '';
+      clientId.value = route.query.clientId || "";
       cofficentForm.clientId = clientId.value;
       // 如果没有携带编码进来,那就请求接口让用户选择机器编码
       if (!clientId.value) {
@@ -189,7 +283,6 @@ export default {
         }
         getCheckPending();
       }
-      console.log("待审核记录1", proportionCheck.value);
     });
     // 获取分销详情填充信息
     // 获取分销信息填充
@@ -200,122 +293,120 @@ export default {
       } else {
         res = await getProportion({ adminId: user.id, clientId: id });
       }
-      // console.log('res', res)
       const data = res.data;
-      if (data.code === '00000') {
+      if (data.code === "00000") {
         cofficentForm.clientId = data.data.clientId;
         cofficentForm.distProp = data.data.adminProportion;
         // 存在第一个分销人账号和分销比,填充
-        if (data.data.agencyName && data.data.agencyProportion !== '') {
+        if (data.data.agencyName && data.data.agencyProportion !== "") {
           distPropList[0] = {};
-          distPropList[0]['name1'] = data.data.agencyName;
-          distPropList[0]['name2'] = data.data.agencyProportion;
-        }// 存在第二个分销人账号和分销比,填充
-        if (data.data.merchantName && data.data.merchantProportion !== '') {
+          distPropList[0]["name1"] = data.data.agencyName;
+          distPropList[0]["name2"] = data.data.agencyProportion;
+        } // 存在第二个分销人账号和分销比,填充
+        if (data.data.merchantName && data.data.merchantProportion !== "") {
           distPropList[1] = {};
-          distPropList[1]['name1'] = data.data.merchantName;
-          distPropList[1]['name2'] = data.data.merchantProportion;
-        }// 存在第三个分销人账号和分销比,填充
-        if (data.data.personageName && data.data.personageProportion !== '') {
+          distPropList[1]["name1"] = data.data.merchantName;
+          distPropList[1]["name2"] = data.data.merchantProportion;
+        } // 存在第三个分销人账号和分销比,填充
+        if (data.data.personageName && data.data.personageProportion !== "") {
           distPropList[2] = {};
-          distPropList[2]['name1'] = data.data.personageName;
-          distPropList[2]['name2'] = data.data.personageProportion;
+          distPropList[2]["name1"] = data.data.personageName;
+          distPropList[2]["name2"] = data.data.personageProportion;
         }
-        // console.log('distPropList1111', distPropList)
       }
-    }
+    };
     const { t } = useI18n();
-    const title = ref(t('distributionSet.addDist.title'));
+    const title = ref(t("distributionSet.addDist.title"));
     // 获取登陆用户
     const user = getLoginUser();
     const router = useRouter();
     // 提交审批参数
     const cofficentForm = reactive({
-      clientId: '',
-      distProp: '',
-    })
+      clientId: "",
+      distProp: "",
+    });
     // 我的分销比例
     const myDistProp = computed(() => {
       // 计算出分销人的总分销
       const totalProp = distPropList.reduce((pre, cur) => {
         return Number(cur.name2 || 0) + pre;
       }, 0);
-      return (100 - Number(cofficentForm.distProp || 0) - Number(totalProp));
+      return 100 - Number(cofficentForm.distProp || 0) - Number(totalProp);
     });
     // 分销人列表
-    const distPropList = reactive([
-
-    ])
+    const distPropList = reactive([]);
     // 点击提交审批按钮
     const onSubmit = () => {
-      if (cofficentForm.distProp == '0') {
-        showToast(t('distributionSet.addDist.noZero'));
+      if (cofficentForm.distProp == "0") {
+        showToast(t("distributionSet.addDist.noZero"));
         return;
       }
       if (distPropList.length > 0) {
-        if (distPropList[0] && distPropList[0]['name2'] == '0') {
-          showToast(t('distributionSet.addDist.zeroDis'));
+        if (distPropList[0] && distPropList[0]["name2"] == "0") {
+          showToast(t("distributionSet.addDist.zeroDis"));
           return;
         }
-        if (distPropList[1] && distPropList[1]['name2'] == '0') {
-          showToast(t('distributionSet.addDist.zeroDis'));
+        if (distPropList[1] && distPropList[1]["name2"] == "0") {
+          showToast(t("distributionSet.addDist.zeroDis"));
           return;
         }
-        if (distPropList[2] && distPropList[2]['name2'] == '0') {
-          showToast(t('distributionSet.addDist.zeroDis'));
+        if (distPropList[2] && distPropList[2]["name2"] == "0") {
+          showToast(t("distributionSet.addDist.zeroDis"));
           return;
         }
       }
-      console.log("分销比例", cofficentForm.distProp)
       let param = {
         adminId: user.id,
         clientId: cofficentForm.clientId,
         type: distPropList.length,
         proportion: myDistProp.value,
-        agencyName: distPropList[0] ? distPropList[0]['name1'] : '',
-        merchantName: distPropList[1] ? distPropList[1]['name1'] : '',
-        personageName: distPropList[2] ? distPropList[2]['name1'] : '',
-        agencyProportion: distPropList[0] ? distPropList[0]['name2'] : '',
-        merchantProportion: distPropList[1] ? distPropList[1]['name2'] : '',
-        personageProportion: distPropList[2] ? distPropList[2]['name2'] : '',
+        agencyName: distPropList[0] ? distPropList[0]["name1"] : "",
+        merchantName: distPropList[1] ? distPropList[1]["name1"] : "",
+        personageName: distPropList[2] ? distPropList[2]["name1"] : "",
+        agencyProportion: distPropList[0] ? distPropList[0]["name2"] : "",
+        merchantProportion: distPropList[1] ? distPropList[1]["name2"] : "",
+        personageProportion: distPropList[2] ? distPropList[2]["name2"] : "",
         adminProportion: cofficentForm.distProp,
-      }
-      saveProportion(param).then(res => {
-        if (res.data.code === '00000') {
-          showToast(t('distributionSet.addDist.submitSuccess'));
+      };
+      saveProportion(param).then((res) => {
+        if (res.data.code === "00000") {
+          showToast(t("distributionSet.addDist.submitSuccess"));
           setTimeout(() => {
             router.back();
           }, 1000);
-        } else if (res.data.code === 'B0001') {
+        } else if (res.data.code === "B0001") {
           // showToast(t('distributionSet.addDist.hasExamine'));
           showToast(res.data.message);
         } else {
-          showToast(t('distributionSet.addDist.submitFailed'));
+          showToast(t("distributionSet.addDist.submitFailed"));
         }
-      })
+      });
     };
     // 点击添加分销人
     const toAdd = () => {
       distPropList.push({
-        name1: '',
-        name2: ''
-      })
-    }
+        name1: "",
+        name2: "",
+      });
+    };
     // 点击删除
     const toDele = (idx) => {
       distPropList.splice(idx, 1);
-    }
+    };
     // 分销比例规则
     const distPropVali = (val) => /^(\d{1,2}|100)$/.test(val);
     // 获取设备列表
     const getDeviceListFun = async () => {
       const { data } = await getEquipmentList({ adminId: user.id });
-      if (data.code === '00000') {
-        busiPopList.value = data.data.map(item => {
-          return { name: item.name != null ? item.name : item.clientId, clientId: item.clientId }
+      if (data.code === "00000") {
+        busiPopList.value = data.data.map((item) => {
+          return {
+            name: item.name != null ? item.name : item.clientId,
+            clientId: item.clientId,
+          };
         });
       }
-    }
+    };
     // 设备编码选择框
     const busiPopShow = ref(false);
     const busiPopList = ref([]);
@@ -328,7 +419,6 @@ export default {
     };
     // 点击确定按钮
     const busiPopConfirm = ({ selectedOptions }) => {
-      // console.log(selectedOptions[0]);
       busiPopShow.value = false;
       cofficentForm.clientId = selectedOptions[0].clientId;
 
@@ -342,45 +432,69 @@ export default {
 
     // 获取已提交的审核记录
     const getCheckPending = async () => {
-      const { data } = await Api_getCheckPending({ adminId: user.id, clientId: cofficentForm.clientId });
-      if (data.code === '00000') {
+      const { data } = await Api_getCheckPending({
+        adminId: user.id,
+        clientId: cofficentForm.clientId,
+      });
+      if (data.code === "00000") {
         // 存在审核记录,则不能再提交
         proportionCheck.value = data.data;
-        console.log("待审核记录", proportionCheck.value);
       }
-    }
+    };
 
     // 撤销分销申请
     const cancelDistri = (value) => {
       showConfirmDialog({
-        title: t('user.tips'),
-        message: t('distributionSet.changeTips'),
-      }).then(async () => {
-        const { data } = await Api_cancelDistri({ id: value });
-        if (data.code == "00000") {
-          showSuccessToast(t('distributionSet.cancelSuccess'));
-          setTimeout(() => {
-            proportionCheck.value = [];
-            if (!clientId.value) {
-              getDeviceListFun();
-            } else {
-              if (id) {
-                // 如果是从分销列表进来的
-                getProportionFun(1, id, type);
+        title: t("user.tips"),
+        message: t("distributionSet.changeTips"),
+      })
+        .then(async () => {
+          const { data } = await Api_cancelDistri({ id: value });
+          if (data.code == "00000") {
+            showSuccessToast(t("distributionSet.cancelSuccess"));
+            setTimeout(() => {
+              proportionCheck.value = [];
+              if (!clientId.value) {
+                getDeviceListFun();
               } else {
-                // 如果设备操作里进来的
-                getProportionFun(2, clientId.value);
+                if (id) {
+                  // 如果是从分销列表进来的
+                  getProportionFun(1, id, type);
+                } else {
+                  // 如果设备操作里进来的
+                  getProportionFun(2, clientId.value);
+                }
+                getCheckPending();
               }
-              getCheckPending();
-            }
-          }, 1000);
-        } else {
-          showFailToast(data.message);
-        }
-      }).catch((error) => {
-        console.error(error);
-      })
-    }
+            }, 1000);
+          } else {
+            showFailToast(data.message);
+          }
+        })
+        .catch((error) => {
+          console.error(error);
+        });
+    };
+
+    const activeDistributors = computed(() => {
+      return [
+        {
+          type: "agency",
+          name: proportionCheck.value.agencyName,
+          proportion: proportionCheck.value.agencyProportion,
+        },
+        {
+          type: "merchant",
+          name: proportionCheck.value.merchantName,
+          proportion: proportionCheck.value.merchantProportion,
+        },
+        {
+          type: "personage",
+          name: proportionCheck.value.personageName,
+          proportion: proportionCheck.value.personageProportion,
+        },
+      ].filter((d) => d.name && d.proportion);
+    });
 
     return {
       onSubmit,
@@ -400,9 +514,202 @@ export default {
       clientId,
       proportionCheck,
       cancelDistri,
+      activeDistributors,
     };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+@primary-color: #4d6add;
+@text-color: #29b664;
+@text-primary: #434d74;
+@border-color: #e4e7ec;
+@error-color: #fe5d55;
+
+.distribution-detail {
+  background: #f8fafb;
+  min-height: 100vh;
+
+  .content-container {
+    background: #f5f6fa;
+    height: calc(100% - 50px);
+    overflow: auto;
+    overflow-x: hidden;
+  }
+
+  .detail-container {
+    margin: 12px;
+    background: white;
+    border-radius: 12px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
   }
 }
-</script>
 
-<style lang="less" scoped></style>
+.input-section {
+  padding: 10px;
+
+  border-bottom: 1px solid @border-color;
+
+  .field-icons {
+    display: flex;
+    gap: 8px;
+    align-items: center;
+
+    .van-icon {
+      color: #999;
+      font-size: 18px;
+
+      &:active {
+        color: @primary-color;
+      }
+    }
+  }
+}
+
+.info-section {
+  padding: 16px;
+
+  .section-title {
+    padding: 12px 0;
+    font-weight: 500;
+    font-size: 15px;
+    color: @text-primary;
+    border-bottom: 1px solid @border-color;
+    margin-bottom: 16px;
+  }
+}
+
+// 通用样式
+.proportion-row,
+.distributor-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 0;
+
+  .percentage {
+    color: @primary-color;
+    font-weight: 500;
+  }
+}
+
+.distributor-group {
+  margin: 12px 0;
+  padding: 12px;
+  background: #f8fafb;
+  border-radius: 8px;
+}
+
+:deep(.van-field__control) {
+  /* 输入框容器样式 */
+  border: 1px solid #e4e7ec;
+  border-radius: 8px;
+  padding: 5px;
+  transition: border-color 0.3s;
+}
+
+:deep(.van-field) {
+  /* 输入框容器样式 */
+  padding: 10px 8px;
+
+  /* label文本居中对齐 */
+  .van-field__label {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: @primary-color;
+    font-weight: 500;
+    width: 100px;
+
+    /* 小屏幕适配 */
+    @media (max-width: 480px) {
+      width: 100px;
+      font-size: 13px;
+    }
+  }
+
+  .unit {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+
+.edit-form {
+  .input-with-unit {
+    display: flex;
+    align-items: center;
+
+    .van-field {
+      flex: 1;
+    }
+
+    .unit {
+      color: @text-primary;
+      padding-left: 8px;
+    }
+  }
+
+  .my-proportion {
+    padding: 10px 20px;
+    color: @text-color;
+    font-size: 20px;
+
+    .label {
+      font-size: 14px;
+    }
+
+    .percentage {
+      font-weight: 500;
+      font-size: 14px;
+    }
+  }
+}
+
+.distributor-input {
+  margin: 10px 0;
+  border: 1px solid @border-color;
+  border-radius: 8px;
+  padding: 12px 0;
+
+  .proportion-input {
+    display: flex;
+    gap: 5px;
+    align-items: center;
+
+    .delete-btn {
+      color: @error-color;
+      display: flex;
+      align-items: center;
+      gap: 4px;
+      padding: 0 8px;
+    }
+  }
+}
+
+.add-button {
+  color: @primary-color;
+  text-align: center;
+  padding: 12px;
+  border: 1px dashed @primary-color;
+  border-radius: 8px;
+  margin: 16px 0;
+  cursor: pointer;
+}
+
+.action-button {
+  margin: 40px 16px;
+}
+
+@media (max-width: 480px) {
+  .detail-container {
+    border-radius: 8px;
+  }
+
+  .input-section,
+  .info-section {
+    padding: 10px;
+  }
+}
+</style>

+ 1 - 1
src/views/home/index.vue

@@ -463,7 +463,7 @@ export default {
           };
 
           option.dataZoom[0]["startValue"] =
-            data.data.categories[data.data.categories.length - 4];
+            data.data.categories[data.data.categories.length - 5];
           option.dataZoom[0]["endValue"] =
             data.data.categories[data.data.categories.length - 1];
           // option.dataZoom[0].start =

File diff suppressed because it is too large
+ 660 - 618
src/views/purse/index.vue