li.shaoyi 1 주 전
부모
커밋
f2fddb01fb
68개의 변경된 파일1469개의 추가작업 그리고 695개의 파일을 삭제
  1. 2 2
      app/package.json
  2. 5 0
      oem/digital/img/icons/chart.svg
  3. 1 1
      oem/digital/img/icons/contract.svg
  4. 10 0
      oem/digital/img/icons/deposit.svg
  5. 2 0
      oem/digital/img/icons/eye-hidden.svg
  6. 6 0
      oem/digital/img/icons/eye.svg
  7. 4 4
      oem/digital/img/icons/home.svg
  8. 2 0
      oem/digital/img/icons/setting.svg
  9. 1 1
      oem/digital/img/icons/spot.svg
  10. 20 0
      oem/digital/img/icons/success.svg
  11. 8 0
      oem/digital/img/icons/transfer.svg
  12. 4 4
      oem/digital/img/icons/wallet.svg
  13. 6 0
      oem/digital/img/icons/withdraw.svg
  14. 2 2
      oem/snhl/config/appconfig.json
  15. 39 16
      src/packages/digital/assets/themes/default/default.less
  16. 22 14
      src/packages/digital/assets/themes/global/global.less
  17. 23 0
      src/packages/digital/components/grid/index.less
  18. 36 0
      src/packages/digital/components/grid/index.vue
  19. 7 0
      src/packages/digital/components/grid/types.ts
  20. 0 5
      src/packages/digital/components/iconbar/index.less
  21. 0 50
      src/packages/digital/components/iconbar/index.vue
  22. 19 0
      src/packages/digital/components/search/index.less
  23. 18 0
      src/packages/digital/components/search/index.vue
  24. 20 0
      src/packages/digital/components/switch-tab/index.less
  25. 13 0
      src/packages/digital/components/switch-tab/index.vue
  26. 64 0
      src/packages/digital/components/wallet-total/index.less
  27. 61 0
      src/packages/digital/components/wallet-total/index.vue
  28. 67 65
      src/packages/digital/views/contract/components/position/detail/index.vue
  29. 5 5
      src/packages/digital/views/contract/components/position/detail/tpsl/index.vue
  30. 21 19
      src/packages/digital/views/contract/components/statement/list/index.vue
  31. 83 26
      src/packages/digital/views/contract/detail/index.vue
  32. 11 10
      src/packages/digital/views/contract/goods/chart/index.vue
  33. 30 2
      src/packages/digital/views/contract/goods/detail/index.less
  34. 77 44
      src/packages/digital/views/contract/goods/detail/index.vue
  35. 2 1
      src/packages/digital/views/contract/goods/list/index.less
  36. 21 27
      src/packages/digital/views/contract/goods/list/index.vue
  37. 29 8
      src/packages/digital/views/home/index.less
  38. 42 16
      src/packages/digital/views/home/index.vue
  39. 35 100
      src/packages/digital/views/home/main/index.less
  40. 65 135
      src/packages/digital/views/home/main/index.vue
  41. 38 0
      src/packages/digital/views/notice/list/components/detail/index.less
  42. 54 0
      src/packages/digital/views/notice/list/components/detail/index.vue
  43. 62 0
      src/packages/digital/views/notice/list/index.vue
  44. 3 0
      src/packages/digital/views/setting/index.less
  45. 15 6
      src/packages/digital/views/setting/index.vue
  46. 4 0
      src/packages/digital/views/setting/luanguage/index.less
  47. 53 0
      src/packages/digital/views/setting/luanguage/index.vue
  48. 4 7
      src/packages/digital/views/spot/goods/chart/index.vue
  49. 7 12
      src/packages/digital/views/spot/goods/detail/index.vue
  50. 4 9
      src/packages/digital/views/spot/goods/list/index.vue
  51. 4 2
      src/packages/digital/views/wallet/components/spot/index.less
  52. 5 5
      src/packages/digital/views/wallet/components/spot/index.vue
  53. 2 4
      src/packages/digital/views/wallet/currency/index.vue
  54. 31 28
      src/packages/digital/views/wallet/deposit/index.vue
  55. 40 22
      src/packages/digital/views/wallet/index.vue
  56. 11 9
      src/packages/digital/views/wallet/withdraw/index.vue
  57. 4 0
      src/packages/mobile/components/base/stepper/index.less
  58. 6 2
      src/packages/mobile/components/base/stepper/index.vue
  59. 7 0
      src/packages/mobile/components/base/submitbar/index.less
  60. 16 0
      src/packages/mobile/components/base/submitbar/index.vue
  61. 2 2
      src/packages/mobile/components/base/switch-tab/index.vue
  62. 9 19
      src/packages/mobile/components/layouts/block/index.less
  63. 24 2
      src/packages/mobile/components/layouts/block/index.vue
  64. 1 1
      src/packages/mobile/components/layouts/scroll-view/index.vue
  65. 93 0
      src/packages/mobile/components/modules/article/index.less
  66. 71 0
      src/packages/mobile/components/modules/article/index.vue
  67. 7 8
      src/packages/mobile/views/account/authresult/Index.vue
  68. 9 0
      vue.config.js

+ 2 - 2
app/package.json

@@ -1,6 +1,6 @@
 {
-  "name": "trading",
-  "version": "1.0.11",
+  "name": "trading-beta",
+  "version": "1.0.12",
   "main": "main.js",
   "dependencies": {
     "electron-updater": "^6.1.4",

+ 5 - 0
oem/digital/img/icons/chart.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Vector">
+<path id="Vector_2" d="M6.28571 9.71429V8C6.28571 7.84845 6.22551 7.7031 6.11835 7.59594C6.01118 7.48878 5.86584 7.42857 5.71429 7.42857C5.56273 7.42857 5.41739 7.48878 5.31022 7.59594C5.20306 7.7031 5.14286 7.84845 5.14286 8V9.71429C4.51171 9.71429 4 10.2254 4 10.8571V13.1429C4 13.7746 4.51171 14.2857 5.14286 14.2857V16C5.14286 16.1516 5.20306 16.2969 5.31022 16.4041C5.41739 16.5112 5.56273 16.5714 5.71429 16.5714C5.86584 16.5714 6.01118 16.5112 6.11835 16.4041C6.22551 16.2969 6.28571 16.1516 6.28571 16V14.2857C6.91686 14.2857 7.42857 13.7746 7.42857 13.1429V10.8571C7.42857 10.2254 6.91686 9.71429 6.28571 9.71429ZM13.1429 7.42857H12V4.57143C12 4.25557 11.7433 4 11.4286 4C11.1127 4 10.8571 4.25557 10.8571 4.57143V7.42857H9.71429C9.08314 7.42857 8.57143 7.93971 8.57143 8.57143V15.4286C8.57143 16.0603 9.08314 16.5714 9.71429 16.5714H10.8571V19.4286C10.8571 19.7444 11.1127 20 11.4286 20C11.7433 20 12 19.7444 12 19.4286V16.5714H13.1429C13.7746 16.5714 14.2857 16.0603 14.2857 15.4286V8.57143C14.2857 7.93971 13.7746 7.42857 13.1429 7.42857ZM18.8571 8.57143V6.85714C18.8571 6.54243 18.6004 6.28571 18.2857 6.28571C17.9699 6.28571 17.7143 6.54243 17.7143 6.85714V8.57143C17.0826 8.57143 16.5714 9.08257 16.5714 9.71429V13.1429C16.5714 13.7746 17.0826 14.2857 17.7143 14.2857V16C17.7143 16.3159 17.9699 16.5714 18.2857 16.5714C18.6004 16.5714 18.8571 16.3159 18.8571 16V14.2857C19.4889 14.2857 20 13.7746 20 13.1429V9.71429C20 9.08257 19.4889 8.57143 18.8571 8.57143Z" fill="#777777"/>
+</g>
+</svg>

+ 1 - 1
oem/digital/img/icons/contract.svg

@@ -1,5 +1,5 @@
 <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g id="Vector">
-<path id="Vector_2" d="M0.80957 6.66211C2.02915 2.40906 6.34637 -0.240576 10.7441 0.68457C12.6024 1.07554 14.2768 2.07644 15.501 3.52832C16.613 4.84732 17.2937 6.46984 17.4629 8.17871H14.625V8.17969C14.4539 8.17432 14.2831 8.20023 14.1221 8.25879C13.9532 8.32021 13.7978 8.41495 13.666 8.53711L10.4375 11.3564L7.5625 8.33398L7.56055 8.33301L7.46094 8.23828C7.21751 8.02957 6.90912 7.90897 6.58594 7.89844C6.21681 7.88649 5.85749 8.01932 5.58496 8.26855C5.3124 8.51798 5.14776 8.86453 5.12695 9.2334C5.10623 9.60214 5.22997 9.96475 5.47266 10.2432L5.47949 10.251L5.48633 10.2578L9.31152 14.2998C9.82633 14.8489 10.6846 14.8866 11.2461 14.3857L11.2451 14.3848L15.0928 11.0322H17.2178C16.7986 12.7728 15.8367 14.3413 14.4619 15.5039C12.9353 16.7948 11.0002 17.5022 9.00098 17.5L8.56445 17.4883C4.24857 17.2615 0.784469 13.8225 0.517578 9.51562H3.31641C3.62657 9.50033 3.92184 9.37688 4.15332 9.16699L4.1543 9.16797L7.39258 6.33789L10.248 9.33691V9.33789C10.3707 9.48397 10.5222 9.60317 10.6924 9.68945C10.8626 9.7757 11.0488 9.82727 11.2393 9.83984C11.4298 9.85236 11.6216 9.82598 11.8018 9.7627C11.9818 9.69944 12.1473 9.60049 12.2881 9.47168C12.4289 9.34282 12.5421 9.18646 12.6211 9.0127C12.7 8.83892 12.7436 8.65078 12.748 8.45996C12.7525 8.26903 12.7183 8.07871 12.6475 7.90137C12.5781 7.72778 12.4745 7.57003 12.3438 7.43652H12.3447L8.51953 3.39355C8.27267 3.13023 7.93291 2.97351 7.57227 2.95703C7.21249 2.94067 6.86035 3.06509 6.59082 3.30371L2.73828 6.66211H0.80957Z" stroke="#AAAAAA"/>
+<path id="Vector_2" d="M0.80957 6.66211C2.02915 2.40906 6.34637 -0.240576 10.7441 0.68457C12.6024 1.07554 14.2768 2.07644 15.501 3.52832C16.613 4.84732 17.2937 6.46984 17.4629 8.17871H14.625V8.17969C14.4539 8.17432 14.2831 8.20023 14.1221 8.25879C13.9532 8.32021 13.7978 8.41495 13.666 8.53711L10.4375 11.3564L7.5625 8.33398L7.56055 8.33301L7.46094 8.23828C7.21751 8.02957 6.90912 7.90897 6.58594 7.89844C6.21681 7.88649 5.85749 8.01932 5.58496 8.26855C5.3124 8.51798 5.14776 8.86453 5.12695 9.2334C5.10623 9.60214 5.22997 9.96475 5.47266 10.2432L5.47949 10.251L5.48633 10.2578L9.31152 14.2998C9.82633 14.8489 10.6846 14.8866 11.2461 14.3857L11.2451 14.3848L15.0928 11.0322H17.2178C16.7986 12.7729 15.8367 14.3413 14.4619 15.5039C12.9353 16.7948 11.0002 17.5022 9.00098 17.5L8.56445 17.4883C4.24857 17.2615 0.784469 13.8225 0.517578 9.51562H3.31641C3.62657 9.50033 3.92184 9.37688 4.15332 9.16699L4.1543 9.16797L7.39258 6.33789L10.248 9.33691V9.33789C10.3707 9.48397 10.5222 9.60318 10.6924 9.68945C10.8626 9.77571 11.0488 9.82727 11.2393 9.83984C11.4298 9.85236 11.6216 9.82598 11.8018 9.7627C11.9818 9.69944 12.1473 9.60049 12.2881 9.47168C12.4289 9.34282 12.5421 9.18646 12.6211 9.0127C12.7 8.83892 12.7436 8.65078 12.748 8.45996C12.7525 8.26903 12.7183 8.07871 12.6475 7.90137C12.5781 7.72777 12.4745 7.57003 12.3438 7.43652H12.3447L8.51953 3.39355C8.27267 3.13023 7.93291 2.97351 7.57227 2.95703C7.21249 2.94067 6.86035 3.06509 6.59082 3.30371L2.73828 6.66211H0.80957Z" stroke="#777777"/>
 </g>
 </svg>

+ 10 - 0
oem/digital/img/icons/deposit.svg

@@ -0,0 +1,10 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 31">
+<path id="Vector" d="M12 17.5901C11.55 17.5901 11.25 17.292 11.25 16.8448V10.1367C11.25 9.68951 11.55 9.39136 12 9.39136C12.45 9.39136 12.75 9.68948 12.75 10.1367V16.8448C12.6 17.2919 12.3 17.5901 12 17.5901V17.5901Z" fill="#F8BB49"/>
+<path id="Vector_2" d="M15.2998 12.5217H8.54982C8.09982 12.5217 7.7998 12.2236 7.7998 11.7764C7.7998 11.3292 8.09979 11.0311 8.5498 11.0311H15.2998C15.7498 11.0311 16.0498 11.3292 16.0498 11.7764C16.0498 12.2236 15.7498 12.5217 15.2998 12.5217ZM15.2998 15.205H8.54982C8.09982 15.205 7.7998 14.9069 7.7998 14.4597C7.7998 14.0124 8.09979 13.7143 8.5498 13.7143H15.2998C15.7498 13.7143 16.0498 14.0124 16.0498 14.4597C16.0498 14.9068 15.7498 15.205 15.2998 15.205ZM11.9998 10.882C11.8498 10.882 11.5498 10.882 11.3998 10.733L8.24981 7.60253C7.94982 7.3044 7.94982 6.85717 8.24981 6.55904C8.5498 6.26092 8.9998 6.26092 9.29982 6.55904L12.4498 9.68947C12.7498 9.98759 12.7498 10.4348 12.4498 10.733C12.2998 10.882 12.1498 10.882 11.9998 10.882H11.9998Z" fill="#F8BB49"/>
+<path id="Vector_3" d="M11.9998 10.882C11.8498 10.882 11.5498 10.882 11.3998 10.733C11.0998 10.4348 11.0998 9.98762 11.3998 9.68947L14.5498 6.55904C14.8498 6.26092 15.2998 6.26092 15.5998 6.55904C15.8998 6.85717 15.8998 7.30438 15.5998 7.60253L12.4498 10.733C12.2998 10.882 12.1498 10.882 11.9998 10.882Z" fill="#F8BB49"/>
+<path id="Vector_4" d="M12 23.8509C5.40002 23.8509 0 18.4844 0 11.9255C0 5.36648 5.40002 0 12 0C18.6 0 24 5.36646 24 11.9255C24 12.5217 24 13.118 23.85 13.8633C23.85 14.3105 23.4 14.6087 22.95 14.4596C22.5 14.4596 22.2 14.0124 22.35 13.5652C22.5 12.9689 22.5 12.5217 22.5 11.9255C22.5 6.11179 17.85 1.49067 12 1.49067C6.14999 1.49067 1.50001 6.26085 1.50001 12.0745C1.50001 17.8882 6.14999 22.5093 12 22.5093C12.6 22.5093 13.35 22.5093 13.95 22.3602C14.4 22.2112 14.7 22.5093 14.85 22.9565C15 23.4037 14.7 23.7019 14.25 23.8509H12Z" fill="#F8BB49"/>
+<path id="Vector_5" d="M23.25 20.2734H15.75C15.3 20.2734 15 19.9753 15 19.5281C15 19.0809 15.3 18.7827 15.75 18.7827H23.25C23.7 18.7827 24 19.0808 24 19.5281C24 19.9753 23.55 20.2734 23.25 20.2734L23.25 20.2734Z" fill="#F8BB49"/>
+<path id="Vector_6" d="M19.5 24C19.05 24 18.75 23.7019 18.75 23.2546V15.8012C18.75 15.354 19.05 15.0559 19.5 15.0559C19.95 15.0559 20.25 15.354 20.25 15.8012V23.2547C20.25 23.5528 19.8 24 19.5 24Z" fill="#F8BB49"/>
+</g>
+</svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 0
oem/digital/img/icons/eye-hidden.svg


+ 6 - 0
oem/digital/img/icons/eye.svg

@@ -0,0 +1,6 @@
+<svg width="26" height="22" viewBox="0 0 26 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 4159">
+<path id="Vector" d="M21.8594 10.3C21.7095 10.0278 18.7486 4 13.0141 4C7.27954 4 4.31858 10.0278 4.16866 10.3C3.94378 10.7278 3.94378 11.2722 4.16866 11.7C4.31858 11.9722 7.27954 18 13.0141 18C18.7486 18 21.7095 11.9722 21.8594 11.7C22.0469 11.2722 22.0469 10.7278 21.8594 10.3ZM13.0141 16.4444C8.14159 16.4444 5.51796 11 5.51796 11C5.51796 11 8.14159 5.55556 13.0141 5.55556C17.8865 5.55556 20.5102 11 20.5102 11C20.5102 11 17.8865 16.4444 13.0141 16.4444Z" fill="#0D121B"/>
+<path id="Vector_2" d="M13.0136 7.88867C11.3644 7.88867 10.0151 9.28867 10.0151 10.9998C10.0151 12.7109 11.3644 14.1109 13.0136 14.1109C14.6627 14.1109 16.012 12.7109 16.012 10.9998C16.012 9.28867 14.6627 7.88867 13.0136 7.88867ZM13.0136 12.5553C12.189 12.5553 11.5144 11.8553 11.5144 10.9998C11.5144 10.1442 12.189 9.44423 13.0136 9.44423C13.8381 9.44423 14.5128 10.1442 14.5128 10.9998C14.5128 11.8553 13.8381 12.5553 13.0136 12.5553Z" fill="#0D121B"/>
+</g>
+</svg>

+ 4 - 4
oem/digital/img/icons/home.svg

@@ -1,11 +1,11 @@
 <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g id="Group 4166">
 <g id="Exclude">
-<mask id="path-1-inside-1_2003_1410" fill="white">
-<path d="M7.26172 0.554683C8.30232 -0.184894 9.69768 -0.184894 10.7383 0.554683L16.7383 4.81933C17.5298 5.38207 18 6.29342 18 7.26464V14.9961C18 16.6529 16.6569 17.9961 15 17.9961H3C1.34315 17.9961 0 16.6529 0 14.9961V7.26464C0 6.29342 0.47015 5.38207 1.26172 4.81933L7.26172 0.554683Z"/>
+<mask id="path-1-inside-1_2022_4074" fill="white">
+<path d="M7.26172 0.554688C8.30232 -0.18489 9.69768 -0.18489 10.7383 0.554688L16.7383 4.81934C17.5298 5.38207 18 6.29342 18 7.26465V14.9961C18 16.6529 16.6569 17.9961 15 17.9961H3C1.34315 17.9961 0 16.6529 0 14.9961V7.26465C0 6.29342 0.47015 5.38207 1.26172 4.81934L7.26172 0.554688Z"/>
 </mask>
-<path d="M7.26172 0.554683L6.68241 -0.260423L6.68238 -0.260402L7.26172 0.554683ZM10.7383 0.554683L11.3176 -0.260402L11.3176 -0.260423L10.7383 0.554683ZM16.7383 4.81933L17.3177 4.0043L17.3176 4.00425L16.7383 4.81933ZM15 17.9961V18.9961V17.9961ZM3 17.9961V18.9961V17.9961ZM1.26172 4.81933L0.682377 4.00425L0.682302 4.0043L1.26172 4.81933ZM7.26172 0.554683L7.84103 1.36979C8.53477 0.876737 9.46523 0.876737 10.159 1.36979L10.7383 0.554683L11.3176 -0.260423C9.93012 -1.24653 8.06988 -1.24653 6.68241 -0.260423L7.26172 0.554683ZM10.7383 0.554683L10.1589 1.36977L16.1589 5.63442L16.7383 4.81933L17.3176 4.00425L11.3176 -0.260402L10.7383 0.554683ZM16.7383 4.81933L16.1589 5.63436C16.6865 6.00946 17 6.61706 17 7.26464H18H19C19 5.96977 18.3732 4.75467 17.3177 4.0043L16.7383 4.81933ZM18 7.26464H17V14.9961H18H19V7.26464H18ZM18 14.9961H17C17 16.1007 16.1046 16.9961 15 16.9961V17.9961V18.9961C17.2091 18.9961 19 17.2052 19 14.9961H18ZM15 17.9961V16.9961H3V17.9961V18.9961H15V17.9961ZM3 17.9961V16.9961C1.89543 16.9961 1 16.1007 1 14.9961H0H-1C-1 17.2052 0.790861 18.9961 3 18.9961V17.9961ZM0 14.9961H1V7.26464H0H-1V14.9961H0ZM0 7.26464H1C1 6.61706 1.31351 6.00946 1.84114 5.63436L1.26172 4.81933L0.682302 4.0043C-0.373205 4.75467 -1 5.96977 -1 7.26464H0ZM1.26172 4.81933L1.84106 5.63442L7.84106 1.36977L7.26172 0.554683L6.68238 -0.260402L0.682377 4.00425L1.26172 4.81933Z" fill="#AAAAAA" mask="url(#path-1-inside-1_2003_1410)"/>
+<path d="M7.26172 0.554688L6.68241 -0.260418L6.68238 -0.260397L7.26172 0.554688ZM10.7383 0.554688L11.3176 -0.260397L11.3176 -0.260418L10.7383 0.554688ZM16.7383 4.81934L17.3177 4.0043L17.3176 4.00425L16.7383 4.81934ZM15 17.9961L15 18.9961H15L15 17.9961ZM3 17.9961L3 18.9961H3V17.9961ZM1.26172 4.81934L0.682377 4.00425L0.682302 4.0043L1.26172 4.81934ZM7.26172 0.554688L7.84103 1.36979C8.53477 0.876742 9.46523 0.876742 10.159 1.36979L10.7383 0.554688L11.3176 -0.260418C9.93012 -1.24652 8.06988 -1.24652 6.68241 -0.260418L7.26172 0.554688ZM10.7383 0.554688L10.1589 1.36977L16.1589 5.63442L16.7383 4.81934L17.3176 4.00425L11.3176 -0.260397L10.7383 0.554688ZM16.7383 4.81934L16.1589 5.63437C16.6865 6.00947 17 6.61706 17 7.26465H18H19C19 5.96978 18.3732 4.75468 17.3177 4.0043L16.7383 4.81934ZM18 7.26465H17V14.9961H18H19V7.26465H18ZM18 14.9961H17C17 16.1007 16.1046 16.9961 15 16.9961L15 17.9961L15 18.9961C17.2091 18.9961 19 17.2052 19 14.9961H18ZM15 17.9961V16.9961H3V17.9961V18.9961H15V17.9961ZM3 17.9961L3 16.9961C1.89543 16.9961 1 16.1007 1 14.9961H0H-1C-1 17.2052 0.790861 18.9961 3 18.9961L3 17.9961ZM0 14.9961H1V7.26465H0H-1V14.9961H0ZM0 7.26465H1C1 6.61706 1.31351 6.00947 1.84114 5.63437L1.26172 4.81934L0.682302 4.0043C-0.373205 4.75468 -1 5.96978 -1 7.26465H0ZM1.26172 4.81934L1.84106 5.63442L7.84106 1.36977L7.26172 0.554688L6.68238 -0.260397L0.682377 4.00425L1.26172 4.81934Z" fill="#777777" mask="url(#path-1-inside-1_2022_4074)"/>
 </g>
-<rect id="Rectangle 34625216" x="5" y="12" width="8" height="2" fill="#AAAAAA"/>
+<rect id="Rectangle 34625216" x="5" y="12" width="8" height="2" fill="#777777"/>
 </g>
 </svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 0
oem/digital/img/icons/setting.svg


+ 1 - 1
oem/digital/img/icons/spot.svg

@@ -1,5 +1,5 @@
 <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g id="Vector">
-<path id="Vector_2" d="M9 0.5C13.6944 0.5 17.5 4.30564 17.5 9C17.5 13.6944 13.6944 17.5 9 17.5C4.30564 17.5 0.5 13.6944 0.5 9C0.5 4.30564 4.30564 0.5 9 0.5ZM8.95703 4.50098C8.70453 4.50098 8.45498 4.5556 8.22559 4.66113C8.05334 4.74041 7.8953 4.84719 7.75781 4.97656L7.62793 5.11328L5.31152 7.81836C5.04002 8.13544 4.89062 8.5396 4.89062 8.95703C4.89063 9.3744 5.04009 9.77767 5.31152 10.0947L7.62793 12.8008C7.79222 12.9927 7.99612 13.1473 8.22559 13.2529C8.45497 13.3585 8.70454 13.4131 8.95703 13.4131C9.20953 13.4131 9.45909 13.3585 9.68848 13.2529C9.91794 13.1473 10.1218 12.9927 10.2861 12.8008L12.6035 10.0947C12.8748 9.77771 13.0244 9.37429 13.0244 8.95703C13.0244 8.5396 12.875 8.13544 12.6035 7.81836L10.2861 5.11328C10.1218 4.92141 9.91794 4.76674 9.68848 4.66113C9.45908 4.55559 9.20954 4.50098 8.95703 4.50098ZM10.2861 8.95605L8.95703 10.5088L7.62695 8.95605L8.95703 7.40234L10.2861 8.95605Z" stroke="#AAAAAA"/>
+<path id="Vector_2" d="M9 0.5C13.6944 0.5 17.5 4.30564 17.5 9C17.5 13.6944 13.6944 17.5 9 17.5C4.30564 17.5 0.5 13.6944 0.5 9C0.5 4.30564 4.30564 0.5 9 0.5ZM8.95703 4.50098C8.70453 4.50098 8.45498 4.5556 8.22559 4.66113C8.05334 4.74041 7.8953 4.84719 7.75781 4.97656L7.62793 5.11328L5.31152 7.81836C5.04002 8.13544 4.89062 8.5396 4.89062 8.95703C4.89063 9.3744 5.04009 9.77767 5.31152 10.0947L7.62793 12.8008C7.79222 12.9927 7.99612 13.1473 8.22559 13.2529C8.45497 13.3585 8.70454 13.4131 8.95703 13.4131C9.20953 13.4131 9.45909 13.3585 9.68848 13.2529C9.91794 13.1473 10.1218 12.9927 10.2861 12.8008L12.6035 10.0947C12.8748 9.77771 13.0244 9.37429 13.0244 8.95703C13.0244 8.5396 12.875 8.13544 12.6035 7.81836L10.2861 5.11328C10.1218 4.92141 9.91794 4.76674 9.68848 4.66113C9.45908 4.55559 9.20954 4.50098 8.95703 4.50098ZM10.2861 8.95605L8.95703 10.5088L7.62695 8.95605L8.95703 7.40234L10.2861 8.95605Z" stroke="#777777"/>
 </g>
 </svg>

+ 20 - 0
oem/digital/img/icons/success.svg

@@ -0,0 +1,20 @@
+<svg width="16" height="11" viewBox="0 0 16 11" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 185">
+<path id="Vector" d="M5.62203 11C5.39937 11 5.17672 10.9164 5.0075 10.7509L0.255162 6.10225C-0.085054 5.76946 -0.085054 5.23107 0.255162 4.89828C0.595378 4.56548 1.14578 4.56548 1.486 4.89828L6.23833 9.54865C6.57855 9.88145 6.57855 10.4198 6.23833 10.7526C6.06734 10.9182 5.84468 11 5.62203 11Z" fill="url(#paint0_linear_56_4469)"/>
+<path id="Vector_2" d="M5.62164 11C5.39899 11 5.17633 10.9164 5.00712 10.7508C4.6669 10.418 4.6669 9.87966 5.00712 9.54687L14.5136 0.249594C14.8538 -0.083198 15.4042 -0.083198 15.7444 0.249594C16.0846 0.582386 16.0846 1.12078 15.7444 1.45357L6.23795 10.7508C6.06695 10.9181 5.8443 11 5.62164 11Z" fill="url(#paint1_linear_56_4469)"/>
+</g>
+<defs>
+<linearGradient id="paint0_linear_56_4469" x1="6.4935" y1="11" x2="6.4935" y2="4.64868" gradientUnits="userSpaceOnUse">
+<stop stop-color="#F7941D"/>
+<stop offset="0.236" stop-color="#F7941D"/>
+<stop offset="0.764" stop-color="#F8BB49"/>
+<stop offset="1" stop-color="#F8BB49"/>
+</linearGradient>
+<linearGradient id="paint1_linear_56_4469" x1="15.9996" y1="11" x2="15.9996" y2="0" gradientUnits="userSpaceOnUse">
+<stop stop-color="#F7941D"/>
+<stop offset="0.236" stop-color="#F7941D"/>
+<stop offset="0.764" stop-color="#F8BB49"/>
+<stop offset="1" stop-color="#F8BB49"/>
+</linearGradient>
+</defs>
+</svg>

+ 8 - 0
oem/digital/img/icons/transfer.svg

@@ -0,0 +1,8 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 36">
+<path id="Vector" d="M17.711 24H4.67709C4.09428 24 2.60562 23.8571 1.36897 22.5251C0.936888 22.0671 0.592636 21.5151 0.357257 20.9029C0.121878 20.2907 0.000315775 19.631 0 18.9644L0 5.03564C0 2.25927 2.09773 0 4.67709 0H19.2046C21.7839 0 23.8816 2.25927 23.8816 5.03564V11.1425C23.8816 11.616 23.5255 12 23.0858 12C22.646 12 22.2899 11.616 22.2899 11.1425V5.03564C22.2899 3.20509 20.9063 1.71382 19.2055 1.71382H4.67611C2.97536 1.71382 1.59081 3.20509 1.59081 5.03564V18.9644C1.59081 19.8513 1.91189 20.6858 2.49568 21.3131C3.33244 22.2153 4.37644 22.2862 4.67611 22.2862H17.7101C18.1489 22.2862 18.506 22.6702 18.506 23.1425C18.506 23.616 18.1489 24 17.7101 24" fill="#F8BB49"/>
+<path id="Vector_2" d="M6.65807 16.2861H1.5918V14.5712H6.65807C7.81591 14.5712 8.75678 13.4181 8.75678 11.9999C8.75678 10.5817 7.81591 9.42865 6.65807 9.42865H1.5918V7.71265H6.65807C8.69354 7.71265 10.3486 9.63592 10.3486 11.9988C10.3486 14.3617 8.69451 16.285 6.65807 16.285" fill="#F8BB49"/>
+<path id="Vector_3" d="M6.51936 12.0823C6.51936 12.7195 6.03972 13.2364 5.44845 13.2364C4.85806 13.2364 4.37842 12.7185 4.37842 12.0823C4.37842 11.446 4.85719 10.9282 5.44845 10.9282C6.04059 10.9282 6.51936 11.4451 6.51936 12.0823ZM23.2867 15.9282H14.7247C14.5937 15.9295 14.4648 15.8905 14.3521 15.8154C14.2395 15.7404 14.1472 15.6321 14.0855 15.5024C14.0263 15.3751 14.0013 15.2313 14.0134 15.0883C14.0254 14.9454 14.0741 14.8094 14.1535 14.6968L16.2944 11.6212C16.4116 11.4569 16.5805 11.3493 16.7662 11.3205C16.952 11.2917 17.1405 11.344 17.2929 11.4666C17.6086 11.7211 17.6723 12.2037 17.436 12.5433L16.1514 14.3894H23.2867C23.68 14.3894 24.0001 14.734 24.0001 15.1588C24.0001 15.5836 23.68 15.9282 23.2867 15.9282Z" fill="#F8BB49"/>
+<path id="Vector_4" d="M21.1957 22.9282C21.0451 22.9286 20.8983 22.8703 20.776 22.7617C20.7029 22.6985 20.6409 22.6175 20.5939 22.5236C20.5469 22.4298 20.5159 22.325 20.5028 22.2158C20.4896 22.1066 20.4946 21.9952 20.5174 21.8884C20.5403 21.7817 20.5805 21.6818 20.6355 21.5949L21.8971 19.5945H14.8895C14.5033 19.5945 14.189 19.2222 14.189 18.7619C14.189 18.3016 14.5033 17.9282 14.8895 17.9282H23.2983C23.5638 17.9282 23.807 18.1064 23.9261 18.3886C23.9841 18.5265 24.0087 18.6824 23.9969 18.8372C23.985 18.9921 23.9372 19.1395 23.8593 19.2615L21.7566 22.5952C21.6896 22.6997 21.6043 22.784 21.5071 22.8417C21.41 22.8994 21.3034 22.929 21.1957 22.9282Z" fill="#F8BB49"/>
+</g>
+</svg>

+ 4 - 4
oem/digital/img/icons/wallet.svg

@@ -1,7 +1,7 @@
-<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g id="Group 26">
-<path id="Vector" d="M11.6455 7.01758H18.5V10.9824H11.6455C11.2143 10.9823 10.81 10.8178 10.501 10.5156C10.1923 10.2137 10.0284 9.82355 10.0283 9.41211V8.58789C10.0284 8.17642 10.1922 7.78628 10.501 7.48438L10.502 7.4834C10.7695 7.22023 11.114 7.06112 11.4854 7.02539L11.6455 7.01758Z" stroke="#AAAAAA"/>
-<circle id="Ellipse 1" cx="12" cy="9" r="1" fill="#AAAAAA"/>
-<path id="Vector_2" d="M2.86816 0.5H16.1348C17.4475 0.500218 18.5 1.54519 18.5 2.80469V4.5H11.7607C10.699 4.5 9.68441 5.00295 8.94238 5.72852C8.20095 6.45352 7.68164 7.45046 7.68164 8.5V9.5C7.68164 10.5495 8.20095 11.5465 8.94238 12.2715C9.68441 12.9971 10.699 13.5 11.7607 13.5H18.5V15.1953C18.5 16.4567 17.4476 17.5 16.1318 17.5H2.86816C1.55056 17.5 0.5 16.4549 0.5 15.1953V2.80469L0.511719 2.57031C0.632295 1.41651 1.63473 0.5 2.86816 0.5Z" stroke="#AAAAAA"/>
+<path id="Vector" d="M11.0322 7.01758H17.5V10.9824H11.0322C10.6394 10.9824 10.2666 10.8238 9.97656 10.5244C9.6861 10.2246 9.52646 9.83229 9.52637 9.41211V8.58789C9.52643 8.16767 9.68608 7.77542 9.97656 7.47559L9.97754 7.47363C10.2643 7.17613 10.6388 7.01762 11.0322 7.01758Z" stroke="#777777"/>
+<ellipse id="Ellipse 1" cx="11.3683" cy="9" rx="0.947368" ry="1" fill="#777777"/>
+<path id="Vector_2" d="M2.7168 0.5H15.2852C16.4896 0.5 17.5 1.51951 17.5 2.80469V4.5H11.1416C10.1181 4.50001 9.14747 5.01063 8.44336 5.7373C7.7384 6.46494 7.25098 7.4599 7.25098 8.5V9.5C7.25098 10.5401 7.7384 11.5351 8.44336 12.2627C9.14747 12.9894 10.1181 13.5 11.1416 13.5H17.5V15.1953C17.5 16.4822 16.4902 17.4999 15.2832 17.5H2.7168C1.50806 17.4999 0.5 16.4804 0.5 15.1953V2.80469L0.511719 2.56641C0.627121 1.39435 1.58523 0.500134 2.7168 0.5Z" stroke="#777777"/>
 </g>
 </svg>

+ 6 - 0
oem/digital/img/icons/withdraw.svg

@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 35">
+<path id="Vector" d="M0.0898438 12C0.0898438 12.5653 0.552866 13.0279 1.11878 13.0279H5.38888L4.12843 14.287C3.72971 14.6853 3.72971 15.3405 4.12843 15.7388C4.33422 15.9444 4.59145 16.0343 4.86155 16.0343C5.13164 16.0343 5.38888 15.9315 5.59467 15.7388L8.60431 12.7195V12.7067L8.66862 12.6424C8.68148 12.6296 8.68148 12.6167 8.69435 12.6039C8.70721 12.591 8.72007 12.5782 8.72007 12.5653C8.73293 12.5525 8.73293 12.5396 8.74579 12.5139C8.75865 12.5011 8.75865 12.4883 8.77151 12.4754C8.78438 12.4626 8.78438 12.4497 8.79724 12.424C8.8101 12.4112 8.8101 12.3983 8.82296 12.3855C8.82296 12.3726 8.83582 12.3598 8.83582 12.3341C8.83582 12.3212 8.84869 12.3084 8.84869 12.2827C8.84869 12.2698 8.86155 12.257 8.86155 12.2313C8.86155 12.2184 8.87441 12.1927 8.87441 12.1799C8.87441 12.1671 8.87441 12.1414 8.88727 12.1285V12.09C8.90013 12.0257 8.90013 11.9486 8.88727 11.8844V11.8459C8.88727 11.833 8.88727 11.8073 8.87441 11.7945C8.87441 11.7816 8.86155 11.7559 8.86155 11.7431C8.86155 11.7302 8.84869 11.7174 8.84869 11.6917C8.84869 11.6788 8.83582 11.6531 8.83582 11.6403C8.83582 11.6274 8.82296 11.6146 8.82296 11.5889C8.82296 11.576 8.8101 11.5632 8.79724 11.5503C8.78438 11.5375 8.78438 11.5247 8.77151 11.499C8.75865 11.4861 8.75865 11.4733 8.74579 11.4604C8.73293 11.4476 8.73293 11.4347 8.72007 11.4219C8.70721 11.409 8.69434 11.3962 8.68148 11.3705C8.66862 11.3576 8.66862 11.3448 8.65576 11.3319C8.63004 11.3062 8.61717 11.2805 8.59145 11.2548L5.5818 8.24842C5.18309 7.85014 4.52714 7.85014 4.12843 8.24842C3.72971 8.64671 3.72971 9.30196 4.12843 9.70024L5.38888 10.9593H1.11878C0.552866 10.9722 0.0898438 11.4219 0.0898438 12Z" fill="#F8BB49"/>
+<path id="Vector_2" d="M24 12C24 10.3683 22.8553 8.99358 21.3248 8.65953V1.02784C21.3248 0.462527 20.8617 0 20.2958 0H1.02894C0.463022 0 0 0.462527 0 1.02784V6.39829C0 6.9636 0.463022 7.42612 1.02894 7.42612C1.59486 7.42612 2.05788 6.9636 2.05788 6.39829V2.05567H19.2669V8.56959H17.299C16.3859 8.56959 15.5241 8.92934 14.8682 9.57173C14.2251 10.2141 13.865 11.0749 13.865 12C13.865 13.8887 15.4084 15.4304 17.299 15.4304H19.2669V21.9443H2.05788V17.6017C2.05788 17.0364 1.59486 16.5739 1.02894 16.5739C0.463022 16.5739 0 17.0364 0 17.6017V22.9722C0 23.5375 0.463022 24 1.02894 24H20.2958C20.8617 24 21.3248 23.5375 21.3248 22.9722V15.3533C21.955 15.212 22.5338 14.9036 22.9968 14.4283C23.6399 13.773 24 12.9122 24 12ZM21.5434 12.9636C21.2862 13.2206 20.9389 13.3619 20.5788 13.3619H17.299C16.5402 13.3619 15.9228 12.7452 15.9228 11.9872C15.9228 11.6274 16.0643 11.2805 16.3215 11.0236C16.5788 10.7666 16.926 10.6253 17.2862 10.6253H20.5659C21.3248 10.6253 21.9421 11.242 21.9421 12C21.9421 12.3597 21.8006 12.7066 21.5434 12.9636Z" fill="#F8BB49"/>
+</g>
+</svg>

+ 2 - 2
oem/snhl/config/appconfig.json

@@ -1,8 +1,8 @@
 {
   "appId": "com.snhl.release",
   "appName": "三农互联",
-  "version": "1.0.11",
-  "versionCode": "100011",
+  "version": "1.0.12",
+  "versionCode": "100012",
   "apiUrl": "http://192.168.31.158:16240/cfg?key=dev_104",
   "tradeChannel": "ws",
   "modules": [

+ 39 - 16
src/packages/digital/assets/themes/default/default.less

@@ -1,7 +1,8 @@
 :root {
     --app-bg-color: #0d121b;
     --page-bg-color: var(--app-bg-color);
-    --font-default-color: #f5f5f5;
+    --block-bg-color: #131b28;
+    --font-default-color: #e8e8e8;
 
     /* 字体大小规范 */
     --font-x-large: 18px;
@@ -11,18 +12,17 @@
     --font-x-small: 10px;
 
     /* 颜色规范 */
-    --color-default: #fff;
-    --color-primary: #e55b24;
+    --color-default: #e8e8e8;
+    --color-primary: #f7941d;
     --color-secondary: #04c786;
-    --color-info: #999;
-    --color-border: #eee;
-    --color-up: #0baf1f;
-    --color-down: #ff3333;
+    --color-info: #777;
+    --color-up: #10aa79;
+    --color-down: #de603b;
 
     /* 导航栏 */
     --navbar-height: 44px;
     --navbar-color: #fff;
-    --navbar-background: var(--van-nav-bar-background);
+    --navbar-background: transparent;
     --navbar-backbutton-color: #fff;
 
     /* 标签栏 */
@@ -33,23 +33,46 @@
     /* 内容边距 */
     --content-inset: 12px;
 
+    /* Vant */
+    --van-border-color: #161b24;
+
     /* Vant-Button */
-    --van-button-default-background: #37393e;
-    --van-button-default-border-color: var(--van-button-default-background);
-    --van-button-primary-background: #e55b24;
+    --van-button-default-color: #0d121b;
+    --van-button-default-background: var(--color-info);
+    --van-button-default-border-color: var(--van-button-primary-background);
+
+    --van-button-primary-color: #0d121b;
+    --van-button-primary-background: linear-gradient(to top, #f7941d 0%, #f7941d 24%, #f8bb49 76%, #f8bb49 100%);
     --van-button-primary-border-color: var(--van-button-primary-background);
 
+    --van-button-success-color: #0d121b;
+    --van-button-success-background: linear-gradient(to top, #089266 0%, #2BD59E 76%, #23CD96 100%);
+    --van-button-success-border-color: var(--van-button-success-background);
+
+    --van-button-danger-color: #0d121b;
+    --van-button-danger-background: linear-gradient(to top, #CD451D 0%, #EE8161 76%, #EB7451 100%);
+    --van-button-danger-border-color: var(--van-button-danger-background);
+
     /* Vant-Checkbox */
     --van-checkbox-checked-icon-color: var(--color-primary);
 
     /* Vant-Radio */
     --van-radio-checked-icon-color: var(--color-primary);
 
-    /* Vant-Tabs */
-    --van-tabs-bottom-bar-color: var(--color-primary);
+    /* Vant-Dialog */
+    --van-dialog-confirm-button-text-color: var(--color-primary);
+
+    /* Vant-CellGroup */
+    --van-cell-group-background: var(--block-bg-color);
+    --van-cell-background: var(--block-bg-color);
+    --van-cell-text-color: var(--color-default);
+    --van-cell-active-color: #1d2533;
 }
 
-.van-dialog {
-    --van-dialog-confirm-button-text-color: var(--color-primary);
-    --van-button-default-background: #1c1c1e;
+.van-button {
+    min-width: 60px;
+
+    &--plain {
+        --van-button-default-color: var(--color-default);
+    }
 }

+ 22 - 14
src/packages/digital/assets/themes/global/global.less

@@ -4,8 +4,8 @@
     }
 
     &-block {
-        padding-top: var(--van-padding-md);
-        padding-bottom: var(--van-padding-md);
+        padding-top: 12px;
+        padding-bottom: 12px;
 
         &+& {
             padding-top: 0;
@@ -115,7 +115,7 @@
         .text-small {
             font-size: 12px;
             font-weight: normal;
-            color: #666;
+            color: var(--color-info);
         }
 
         .van-checkbox {
@@ -138,8 +138,13 @@
     table {
         width: 100%;
         line-height: 1.7;
-        border-bottom: 1px solid #171f2d;
-        padding: 10px 12px;
+        background-color: var(--block-bg-color);
+        border-radius: 6px;
+        padding: 12px;
+
+        &:not(:first-child) {
+            margin-top: 12px;
+        }
 
         tbody {
             font-size: 13px;
@@ -161,8 +166,8 @@
     }
 
     .text-small {
-        font-weight: normal;
-        color: #666;
+        font-size: 12px;
+        color: var(--color-info);
     }
 }
 
@@ -212,17 +217,20 @@
 
 .g-tabs {
     --van-tabs-nav-background: transparent;
-    --van-tab-active-text-color: var(--color-primary);
-
-    .van-tabs {
-        &__line {
-            display: none;
-        }
-    }
+    --van-border-width: 0;
+    --van-tabs-default-color: var(--color-info);
 
     .van-tab {
         &--active {
             --van-tab-font-size: 16px;
+            --van-tabs-default-color: transparent;
+
+            color: var(--color-primary);
+        }
+
+        &--shrink {
+            padding: 0;
+            padding-right: 24px;
         }
     }
 }

+ 23 - 0
src/packages/digital/components/grid/index.less

@@ -0,0 +1,23 @@
+.app-grid {
+    &-list {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 12px;
+        line-height: 1;
+    }
+
+    &-item {
+        flex: 1 1 calc(~'25% - 12px');
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        color: #f9bc4a;
+        background-color: var(--block-bg-color);
+        border-radius: 6px;
+        padding: 10px 0;
+
+        &__text {
+            margin-top: 8px;
+        }
+    }
+}

+ 36 - 0
src/packages/digital/components/grid/index.vue

@@ -0,0 +1,36 @@
+<template>
+    <app-block class="app-grid">
+        <ul class="app-grid-list">
+            <template v-for="(item, index) in items" :key="index">
+                <li class="app-grid-item" @click="$router.push(item.to)">
+                    <div class="app-grid-item__icon">
+                        <slot name="icon" :value="item.icon">
+                            <img :src="item.icon" />
+                        </slot>
+                    </div>
+                    <div class="app-grid-item__text">
+                        <slot name="text" :value="item.text">
+                            <span>{{ item.text }}</span>
+                        </slot>
+                    </div>
+                </li>
+            </template>
+        </ul>
+    </app-block>
+</template>
+
+<script lang="ts" setup>
+import { PropType } from 'vue'
+import { GridItem } from './types'
+
+defineProps({
+    items: {
+        type: Array as PropType<GridItem[]>,
+        required: true
+    }
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 7 - 0
src/packages/digital/components/grid/types.ts

@@ -0,0 +1,7 @@
+import { RouteLocationRaw } from 'vue-router'
+
+export interface GridItem {
+    text: string;
+    icon: string;
+    to: RouteLocationRaw;
+}

+ 0 - 5
src/packages/digital/components/iconbar/index.less

@@ -1,5 +0,0 @@
-.app-iconbar {
-    &--inset {
-        padding: var(--van-padding-md);
-    }
-}

+ 0 - 50
src/packages/digital/components/iconbar/index.vue

@@ -1,50 +0,0 @@
-<template>
-    <div :class="className">
-        <Grid :border="false" :column-num="filteredIcons.length" v-if="filteredIcons.length">
-            <template v-for="(item, index) in filteredIcons" :key="index">
-                <GridItem :icon="item.icon" :text="item.text" :to="item.to" />
-            </template>
-        </Grid>
-    </div>
-</template>
-
-<script lang="ts" setup>
-import { shallowReactive, PropType, computed } from 'vue'
-import { Grid, GridItem } from 'vant'
-import { i18n } from '@/stores'
-
-const props = defineProps({
-    query: {
-        type: Object
-    },
-    routes: {
-        type: Array as PropType<string[]>,
-        default: () => ([])
-    },
-    inset: {
-        type: Boolean,
-        default: false
-    }
-})
-
-const { global: { t } } = i18n
-
-const className = {
-    'app-iconbar': true,
-    'app-iconbar--inset': props.inset
-}
-
-const icons = shallowReactive([
-    { icon: 'pending-payment', text: t('digital.wallet-deposit'), to: { name: 'wallet-deposit' } },
-    { icon: 'paid', text: t('digital.wallet-withdraw'), to: { name: 'wallet-withdraw' } },
-    { icon: 'peer-pay', text: t('digital.wallet-transfer'), to: { name: 'wallet-transfer' } },
-    { icon: 'chart-trending-o', text: t('digital.spot-goods-detail'), to: { name: 'spot-goods-detail' } },
-    { icon: 'setting-o', text: t('digital.wallet-setting'), to: { name: 'setting' } },
-])
-
-const filteredIcons = computed(() => icons.filter((e) => props.routes.includes(e.to.name)))
-</script>
-
-<style lang="less">
-@import './index.less';
-</style>

+ 19 - 0
src/packages/digital/components/search/index.less

@@ -0,0 +1,19 @@
+.app-search {
+    --van-search-background: transparent;
+    --van-search-content-background: var(--block-bg-color);
+    --van-radius-sm: 6px;
+    --van-search-input-height: 40px;
+    --van-search-padding: 10px var(--van-padding-md);
+
+    .van-search {
+        &__content {
+            border: 1px solid rgba(248, 187, 73, 0.2);
+        }
+
+        &__field {
+            .van-field__left-icon {
+                display: inline-flex;
+            }
+        }
+    }
+}

+ 18 - 0
src/packages/digital/components/search/index.vue

@@ -0,0 +1,18 @@
+<template>
+    <Search class="app-search" :placeholder="t('digital.search')">
+        <template #left-icon>
+            <img :src="'./img/icons/search.svg'" :title="t('digital.search')" />
+        </template>
+    </Search>
+</template>
+
+<script lang="ts" setup>
+import { Search } from 'vant'
+import { i18n } from '@/stores'
+
+const t = i18n.global.t
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 20 - 0
src/packages/digital/components/switch-tab/index.less

@@ -0,0 +1,20 @@
+.app-switch-tab-v2 {
+    border-radius: 6px;
+
+    .app-switch {
+        &-tabs {
+            height: 44px;
+            background-color: transparent;
+            border-radius: 6px;
+            padding: 5px;
+
+            &__item {
+                font-size: 16px;
+
+                &.active {
+                    background: var(--van-button-primary-background);
+                }
+            }
+        }
+    }
+}

+ 13 - 0
src/packages/digital/components/switch-tab/index.vue

@@ -0,0 +1,13 @@
+<template>
+    <app-block class="app-switch-tab-v2" background>
+        <app-switch-tab width="100%" v-bind="$attrs" />
+    </app-block>
+</template>
+
+<script lang="ts" setup>
+import AppSwitchTab from '@mobile/components/base/switch-tab/index.vue'
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 64 - 0
src/packages/digital/components/wallet-total/index.less

@@ -0,0 +1,64 @@
+.wallet-total {
+    display: flex;
+    flex-direction: column;
+    padding: 0 var(--van-padding-md);
+
+    &__inner {
+        color: #0d121b;
+        background: linear-gradient(to top, #F7941D 0%, #F7941D 24%, #F8BB49 76%, #F8BB49 100%);
+        border-radius: 10px;
+        padding: 12px;
+    }
+
+    &__header {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+
+    &__title {
+        display: flex;
+        align-items: center;
+
+        img {
+            margin-left: 8px;
+        }
+    }
+
+    &__balance {
+        flex: 1;
+        font-size: 36px;
+        font-weight: bold;
+        text-align: center;
+        padding: 32px 0;
+    }
+
+    &__footer {
+        ul {
+            display: flex;
+
+            li {
+                flex: 1;
+                text-align: center;
+
+                &:first-child {
+                    text-align: left;
+                    border-right: 1px solid #DF8D1E;
+                }
+
+                &:last-child {
+                    text-align: right;
+                    border-left: 1px solid #DF8D1E;
+                }
+
+                span {
+                    display: block;
+
+                    &.text-small {
+                        color: rgba(13, 18, 27, 0.7);
+                    }
+                }
+            }
+        }
+    }
+}

+ 61 - 0
src/packages/digital/components/wallet-total/index.vue

@@ -0,0 +1,61 @@
+<template>
+    <div class="wallet-total">
+        <div class="wallet-total__inner">
+            <div class="wallet-total__header">
+                <div class="wallet-total__title">
+                    <span>总计 {{ currency }}</span>
+                    <img :src="`./img/icons/${isBalanceVisible ? 'eye' : 'eye-hidden'}.svg`"
+                        @click="isBalanceVisible = !isBalanceVisible" />
+                </div>
+            </div>
+            <div class="wallet-total__balance">
+                <span v-if="isBalanceVisible">{{ totalBalance }}</span>
+                <span v-else>**********</span>
+            </div>
+            <div class="wallet-total__footer">
+
+            </div>
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, computed, watch, onActivated } from 'vue'
+import { useAccountStore } from '@/stores'
+import { useSpotAccountStore } from '../../views/wallet/components/spot/composables'
+import { Decimal } from 'decimal.js'
+
+const props = defineProps({
+    currency: {
+        type: String,
+        default: 'USDT'
+    },
+    balance: String,
+})
+
+const accountStore = useAccountStore()
+const spotAccountStore = useSpotAccountStore()
+const isBalanceVisible = shallowRef(true)
+
+const totalBalance = computed(() => {
+    if (props.balance) return props.balance
+
+    const { balance = 0, currencydecimalplace = 2 } = accountStore.getAccountItem({ currencyid: 102 }) ?? {}
+    const { currentbalance = 0 } = spotAccountStore.getAccountItem({ currencyid: 102 }) ?? {}
+
+    return Decimal(balance).plus(currentbalance).toFixed(currencydecimalplace)
+})
+
+watch(isBalanceVisible, (val) => {
+    localStorage.setItem('isBalanceVisible', JSON.stringify(val))
+})
+
+onActivated(() => {
+    const visible = localStorage.getItem('isBalanceVisible') || 'true'
+    isBalanceVisible.value = JSON.parse(visible)
+})
+</script>
+
+<style lang="less">
+@import "./index.less";
+</style>

+ 67 - 65
src/packages/digital/views/contract/components/position/detail/index.vue

@@ -1,69 +1,71 @@
 <template>
-    <app-pull-refresh ref="pullRefreshRef" class="g-detail-list" v-model:loading="loading" @refresh="onRefresh">
-        <table v-for="(item, index) in tableList" :key="index">
-            <thead>
-                <tr>
-                    <td>
-                        {{ item.goodscode }}/{{ goodsname(item.goodscode) }}
-                    </td>
-                    <td :class="item.buyorsell === 0 ? 'g-price-up' : 'g-price-down'">
-                        {{ getBuyOrSellName(item.buyorsell, 1) }}
-                    </td>
-                </tr>
-            </thead>
-            <tbody>
-                <tr>
-                    <td class="text-small">
-                        {{ t('account.profitLoss') + `(${currency(item.currencyid)})` }}
-                    </td>
-                    <td :class="item.profitLossClass">
-                        <b>{{ formatDecimal(item.profitLoss, item.decimalplace) }}</b>
-                    </td>
-                </tr>
-                <tr>
-                    <td class="text-small">
-                        {{ t('position.goods.enableqty') + `(${item.goodscode})` }}
-                    </td>
-                    <td>
-                        {{ item.holderqty - item.freezeqty }}
-                    </td>
-                </tr>
-                <tr>
-                    <td class="text-small">
-                        {{ t('position.goods.holderprice') + `(${currency(item.currencyid)})` }}
-                    </td>
-                    <td>{{ item.holderprice }}</td>
-                </tr>
-                <tr v-if="item.tpsl_tpflag">
-                    <td class="text-small">{{ `${t('digital.tpprice')}(${currency(item.currencyid)})` }}</td>
-                    <td>{{ formatDecimal(item.tpsl_tpprice, item.decimalplace) }}</td>
-                </tr>
-                <tr v-if="item.tpsl_slflag">
-                    <td class="text-small">{{ `${t('digital.slprice')}(${currency(item.currencyid)})` }}</td>
-                    <td>{{ formatDecimal(item.tpsl_slprice, item.decimalplace) }}</td>
-                </tr>
-                <tr>
-                    <td class="text-small">{{ t('position.goods.tradetime') }}</td>
-                    <td>{{ formatDate(item.tradetime) }}</td>
-                </tr>
-            </tbody>
-            <tfoot>
-                <tr>
-                    <td colspan="2">
-                        <Button size="small" @click="showComponent('Close', index)">
-                            {{ t('operation.close') }}
-                        </Button>
-                        <Button size="small" @click="showComponent('MarketClose', index)">
-                            {{ t('digital.marketclose') }}
-                        </Button>
-                        <Button size="small" @click="showComponent('TPSL', index)"
-                            v-if="item?.tpslflag === 1 && item.riskcontrolmode === 2">
-                            {{ t('digital.spsl') }}
-                        </Button>
-                    </td>
-                </tr>
-            </tfoot>
-        </table>
+    <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" @refresh="onRefresh">
+        <app-block class="g-detail-list">
+            <table v-for="(item, index) in tableList" :key="index">
+                <thead>
+                    <tr>
+                        <td>
+                            {{ item.goodscode }}/{{ goodsname(item.goodscode) }}
+                        </td>
+                        <td :class="item.buyorsell === 0 ? 'g-price-up' : 'g-price-down'">
+                            {{ getBuyOrSellName(item.buyorsell, 1) }}
+                        </td>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr>
+                        <td class="text-small">
+                            {{ t('account.profitLoss') + `(${currency(item.currencyid)})` }}
+                        </td>
+                        <td :class="item.profitLossClass">
+                            <b>{{ formatDecimal(item.profitLoss, item.decimalplace) }}</b>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="text-small">
+                            {{ t('position.goods.enableqty') + `(${item.goodscode})` }}
+                        </td>
+                        <td>
+                            {{ item.holderqty - item.freezeqty }}
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="text-small">
+                            {{ t('position.goods.holderprice') + `(${currency(item.currencyid)})` }}
+                        </td>
+                        <td>{{ item.holderprice }}</td>
+                    </tr>
+                    <tr v-if="item.tpsl_tpflag">
+                        <td class="text-small">{{ `${t('digital.tpprice')}(${currency(item.currencyid)})` }}</td>
+                        <td>{{ formatDecimal(item.tpsl_tpprice, item.decimalplace) }}</td>
+                    </tr>
+                    <tr v-if="item.tpsl_slflag">
+                        <td class="text-small">{{ `${t('digital.slprice')}(${currency(item.currencyid)})` }}</td>
+                        <td>{{ formatDecimal(item.tpsl_slprice, item.decimalplace) }}</td>
+                    </tr>
+                    <tr>
+                        <td class="text-small">{{ t('position.goods.tradetime') }}</td>
+                        <td>{{ formatDate(item.tradetime) }}</td>
+                    </tr>
+                </tbody>
+                <tfoot>
+                    <tr>
+                        <td colspan="2">
+                            <Button size="small" @click="showComponent('Close', index)">
+                                {{ t('operation.close') }}
+                            </Button>
+                            <Button type="primary" size="small" @click="showComponent('MarketClose', index)">
+                                {{ t('digital.marketclose') }}
+                            </Button>
+                            <Button type="primary" size="small" @click="showComponent('TPSL', index)"
+                                v-if="item?.tpslflag === 1 && item.riskcontrolmode === 2">
+                                {{ t('digital.spsl') }}
+                            </Button>
+                        </td>
+                    </tr>
+                </tfoot>
+            </table>
+        </app-block>
         <component ref="componentRef" v-bind="{ selectedRow: tableList[rowIndex] }" :is="componentMap.get(componentId)"
             @closed="closeComponent" v-if="componentId" />
     </app-pull-refresh>

+ 5 - 5
src/packages/digital/views/contract/components/position/detail/tpsl/index.vue

@@ -7,7 +7,8 @@
             </template>
             <Form ref="formRef" class="g-form__container" @submit="onCloseSumit">
                 <CellGroup :title="t('position.goods.subtitle')" inset>
-                    <Cell :title="t('digital.goodscode')" :value="`${selectedRow.goodscode}/${goodsname}`" />
+                    <Cell :title="t('digital.goodscode')"
+                        :value="`${selectedRow.goodscode}/${goodsname}`" />
                     <Cell :title="t('position.transfer.buyorsell')"
                         :value="getBuyOrSellName(selectedRow.buyorsell, 1)" />
                     <Cell
@@ -118,7 +119,7 @@ const formData = reactive<Partial<Proto.HolderTPSLSetReq>>({
 
 const quote = computed(() => futuresStore.getQuoteItem({ goodsid: props.selectedRow.goodsid }))
 
-const goodsname = computed(() => futuresStore.getI18nGoodsName(props.selectedRow.goodscode))
+const goodsname = computed (() => futuresStore.getI18nGoodsName(props.selectedRow.goodscode))
 
 const pricePoints = computed(() => {
     const { decimalplace = 0, buyslpoint, buytppoint, sellslpoint, selltppoint } = quote.value ?? {}
@@ -211,9 +212,8 @@ const closed = (isRefresh = false) => {
 }
 
 onMounted(() => {
-    const { tpsl_tpprice, tpsl_slprice } = props.selectedRow
-    const tpPrice = tpsl_tpprice || Number(tpsl_tpprice) || pricePoints.value.takeProfit
-    const slPrice = tpsl_slprice || Number(tpsl_slprice) || pricePoints.value.stopLoss
+    const tpPrice = Number(props.selectedRow.tpsl_tpprice) || pricePoints.value.takeProfit
+    const slPrice = Number(props.selectedRow.tpsl_slprice) || pricePoints.value.stopLoss
 
     formData.TPPrice = Decimal(tpPrice).toNumber()
     formData.SLPrice = Decimal(slPrice).toNumber()

+ 21 - 19
src/packages/digital/views/contract/components/statement/list/index.vue

@@ -1,24 +1,26 @@
 <template>
-    <app-pull-refresh ref="pullRefreshRef" class="g-detail-list" v-model:loading="loading" v-model:pageIndex="pageIndex"
+    <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" v-model:pageIndex="pageIndex"
         :page-count="pageCount" @refresh="onRefresh">
-        <table v-for="(item, index) in dataList" :key="index">
-            <tbody>
-                <tr>
-                    <td class="text-small">{{ t('banksign.capital.amount') }}</td>
-                    <td :class="item.amount > 0 ? 'g-price-up' : 'g-price-down'">
-                        <b>{{ (item.amount > 0 ? '+' : '') + item.amount }}</b>
-                    </td>
-                </tr>
-                <tr>
-                    <td class="text-small">{{ t('banksign.capital.operatetypename') }}</td>
-                    <td>{{ getEnumName(item) }}</td>
-                </tr>
-                <tr>
-                    <td class="text-small">{{ t('banksign.capital.createtime') }}</td>
-                    <td>{{ formatDate(item.createtime) }}</td>
-                </tr>
-            </tbody>
-        </table>
+        <app-block class="g-detail-list">
+            <table v-for="(item, index) in dataList" :key="index">
+                <tbody>
+                    <tr>
+                        <td class="text-small">{{ t('banksign.capital.amount') }}</td>
+                        <td :class="item.amount > 0 ? 'g-price-up' : 'g-price-down'">
+                            <b>{{ (item.amount > 0 ? '+' : '') + item.amount }}</b>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="text-small">{{ t('banksign.capital.operatetypename') }}</td>
+                        <td>{{ getEnumName(item) }}</td>
+                    </tr>
+                    <tr>
+                        <td class="text-small">{{ t('banksign.capital.createtime') }}</td>
+                        <td>{{ formatDate(item.createtime) }}</td>
+                    </tr>
+                </tbody>
+            </table>
+        </app-block>
     </app-pull-refresh>
 </template>
 

+ 83 - 26
src/packages/digital/views/contract/detail/index.vue

@@ -4,24 +4,81 @@
         <template #header>
             <app-navbar :title="t('digital.contractdetails')" />
         </template>
-        <Grid :border="false" :column-num="quotes.length ? 2 : 1">
+        <app-wallet-total :currency="getGoodsCurrencyItemName(accountItem.currencyid)"
+            :balance="accountItem.currentbalance.toFixed(accountItem.currencydecimalplace)" v-if="accountItem" />
+        <!-- <Grid :border="false" :column-num="quotes.length ? 2 : 1">
             <GridItem icon="peer-pay" :text="$t('digital.wallet-transfer')"
                 :to="{ name: 'wallet-transfer', query: { id: accountid } }" />
             <GridItem icon="chart-trending-o" :text="$t('digital.spot-goods-detail')"
                 @click="navigateToContractDetail()" v-if="quotes.length" />
-        </Grid>
+        </Grid> -->
+        <div class="wallet-total" v-if="accountItem">
+            <div class="wallet-total__inner">
+                <div class="wallet-total__header">
+                    <div class="wallet-total__title">
+                        <span>
+                            {{ `${t('digital.currencydecimalplace')}
+                            (${getGoodsCurrencyItemName(accountItem.currencyid)})` }}
+                        </span>
+                        <img :src="`./img/icons/${isBalanceVisible ? 'eye' : 'eye-hidden'}.svg`"
+                            @click="isBalanceVisible = !isBalanceVisible" />
+                    </div>
+                    <div>
+                        <span :class="handlePriceColor(accountItem.profitLoss)">
+                            {{ accountItem.profitLoss.toFixed(accountItem.currencydecimalplace) }}
+                        </span>
+                        <span :class="accountItem.hazardRatioColor">
+                            {{ parsePercent(accountItem.hazardRatio) }}
+                        </span>
+                    </div>
+                </div>
+                <div class="wallet-total__balance">
+                    <span v-if="isBalanceVisible">
+                        {{ accountItem.currentbalance.toFixed(accountItem.currencydecimalplace) }}
+                    </span>
+                    <span v-else>**********</span>
+                </div>
+                <div class="wallet-total__footer">
+                    <ul>
+                        <li>
+                            <span class="text-small">
+                                {{ $t('mine.availableFunds') + `(${getGoodsCurrencyItemName(accountItem.currencyid)})` }}
+                            </span>
+                            <span>{{ accountItem.avaiableBalance.toFixed(accountItem.currencydecimalplace) }}</span>
+                        </li>
+                        <li>
+                            <span class="text-small">
+                                {{ $t('account.usedMargin') + `(${getGoodsCurrencyItemName(accountItem.currencyid)})` }}
+                            </span>
+                            <span>{{ accountItem.usedmargin.toFixed(accountItem.currencydecimalplace) }}</span>
+                        </li>
+                        <li>
+                            <span class="text-small">
+                                {{ $t('account.freeze') + `(${getGoodsCurrencyItemName(accountItem.currencyid)})` }}
+                            </span>
+                            <span>{{ accountItem.freezeMargin.toFixed(accountItem.currencydecimalplace) }}</span>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <app-grid :items="gridItems" />
         <div class="g-detail-table" v-if="accountItem">
             <table cellspacing="0" cellpadding="0">
                 <tbody>
                     <tr>
                         <td>
-                            <span class="text-small">{{ $t('digital.currencydecimalplace')+`(${currency(accountItem.currencyid)})` }}</span>
+                            <span class="text-small">
+                                {{ $t('digital.currencydecimalplace')
+                                    + `(${getGoodsCurrencyItemName(accountItem.currencyid)})` }}
+                            </span>
                             <span>{{ accountItem.currentbalance.toFixed(accountItem.currencydecimalplace) }}</span>
                         </td>
                         <td>
                             <span class="text-small">{{ $t('account.profitLoss') }}</span>
-                            <span :class="handlePriceColor(accountItem.profitLoss)">{{
-                                accountItem.profitLoss.toFixed(accountItem.currencydecimalplace) }}</span>
+                            <span :class="handlePriceColor(accountItem.profitLoss)">
+                                {{ accountItem.profitLoss.toFixed(accountItem.currencydecimalplace) }}
+                            </span>
                         </td>
                         <td>
                             <span class="text-small">{{ $t('account.riskRate') }}</span>
@@ -32,27 +89,14 @@
                     </tr>
                     <tr>
                         <td>
-                            <span class="text-small">{{
-                                $t('mine.availableFunds') + `(${currency(accountItem.currencyid)})`
-                            }}</span>
-                            <span>{{ accountItem.avaiableBalance.toFixed(accountItem.currencydecimalplace) }}</span>
-                        </td>
-                        <td>
-                            <span class="text-small">{{ $t('account.usedMargin') +
-                                `(${currency(accountItem.currencyid)})`
-                                }}</span>
-                            <span>{{ accountItem.usedmargin.toFixed(accountItem.currencydecimalplace) }}</span>
-                        </td>
-                        <td>
-                            <span class="text-small">{{ $t('account.freeze') + `(${currency(accountItem.currencyid)})`
-                            }}</span>
-                            <span>{{ accountItem.freezeMargin.toFixed(accountItem.currencydecimalplace) }}</span>
+
                         </td>
+
                     </tr>
                 </tbody>
             </table>
         </div>
-        <Tabs>
+        <Tabs class="g-tabs" type="card" shrink>
             <Tab :title="t('digital.position')">
                 <contract-position :params="{ accids: accountid.toString() }" />
             </Tab>
@@ -79,10 +123,13 @@
 
 <script lang="ts" setup>
 import { shallowRef, computed } from 'vue'
-import { Cell, CellGroup, Tab, Tabs, Grid, GridItem, ActionSheet } from 'vant'
+import { Cell, CellGroup, Tab, Tabs, Grid, ActionSheet } from 'vant'
 import { handlePriceColor, parsePercent } from '@/filters'
 import { useNavigation } from '@mobile/router/navigation'
 import { i18n, useAccountStore, useFuturesStore } from '@/stores'
+import { GridItem } from '@/packages/digital/components/grid/types'
+import AppGrid from '@/packages/digital/components/grid/index.vue'
+import AppWalletTotal from '@/packages/digital/components/wallet-total/index.vue'
 import ContractOrder from '../components/order/index.vue'
 import ContractTrade from '../components/trade/index.vue'
 import ContractPosition from '../components/position/detail/index.vue'
@@ -98,14 +145,24 @@ const accountStore = useAccountStore()
 
 const accountid = getQueryStringToNumber('id')
 const showSheet = shallowRef(false)
+const isBalanceVisible = shallowRef(true)
 
-const currency = (id: number) => {
-    return getGoodsCurrencyItemName(id)
-}
+const gridItems = computed<GridItem[]>(() => ([
+    {
+        text: t('digital.wallet-transfer'),
+        icon: './img/icons/deposit.svg',
+        to: { name: 'digital.wallet-transfer' }
+    },
+    {
+        text: t('digital.spot-goods-detail'),
+        icon: './img/icons/withdraw.svg',
+        to: { name: 'wallet-withdraw-currency' }
+    }
+]))
 
 const goodsname = (code: string) => {
     return futuresStore.getI18nGoodsName(code)
-} 
+}
 
 // 合约账户
 const accountItem = computed(() => accountStore.getAccountItem({ accountid }))

+ 11 - 10
src/packages/digital/views/contract/goods/chart/index.vue

@@ -1,6 +1,6 @@
 <!-- 合约 - 交易下单 - 图表 -->
 <template>
-    <app-view class="contract-goods-chart g-layout">
+    <app-view class="contract-goods-chart">
         <template #header>
             <app-navbar :title="quote ? `${quote.goodscode}/${goodsname}` : `${t('operation.chart')}`" />
         </template>
@@ -51,25 +51,26 @@
             <goods-chart v-bind="{ theme: 'Dark', goodsCode: quote.goodscode }" />
         </template>
         <template #footer>
-            <Row class="g-layout-block g-layout-block--inset" gutter="10">
-                <Col span="12">
-                <Button type="success" block @click="routerBack({ buyOrSell: BuyOrSell.Buy })">{{ $t('digital.buy') }}</Button>
-                </Col>
-                <Col span="12">
-                <Button type="danger" block @click="routerBack({ buyOrSell: BuyOrSell.Sell })">{{ $t('digital.sell') }}</Button>
-                </Col>
-            </Row>
+            <app-submitbar>
+                <Button type="success" block @click="routerBack({ buyOrSell: BuyOrSell.Buy })">
+                    {{ $t('digital.buy') }}
+                </Button>
+                <Button type="danger" block @click="routerBack({ buyOrSell: BuyOrSell.Sell })">
+                    {{ $t('digital.sell') }}
+                </Button>
+            </app-submitbar>
         </template>
     </app-view>
 </template>
 
 <script lang="ts" setup>
 import { computed, defineAsyncComponent } from 'vue'
-import { Button, Col, Row } from 'vant'
+import { Button } from 'vant'
 import { handleNumberValue, parsePercent } from '@/filters'
 import { BuyOrSell } from '@/constants/order'
 import { useNavigation } from '@mobile/router/navigation'
 import { useFuturesStore } from '@/stores'
+import AppSubmitbar from '@mobile/components/base/submitbar/index.vue'
 
 const GoodsChart = defineAsyncComponent(() => import('@mobile/components/modules/hqchart/index.vue'))
 

+ 30 - 2
src/packages/digital/views/contract/goods/detail/index.less

@@ -1,6 +1,34 @@
 .contract-goods-detail {
-    &__price {
+    .trade-panel {
         display: flex;
-        justify-content: space-around;
+        border-radius: 6px;
+        padding: 5px;
+
+        &-item {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+
+            h3 {
+                padding: 12px 0;
+            }
+        }
+    }
+
+    .trade-info {
+        ul {
+            li {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                font-size: 12px;
+                line-height: 1.7;
+
+                span:first-child {
+                    color: var(--color-info);
+                }
+            }
+        }
     }
 }

+ 77 - 44
src/packages/digital/views/contract/goods/detail/index.vue

@@ -1,74 +1,106 @@
 <template>
-    <app-view class="contract-goods-detail g-layout g-form" v-model:show="showModal">
+    <app-view class="contract-goods-detail g-form" v-model:show="showModal">
         <template #header>
             <app-navbar :title="quote ? `${quote.goodscode}/${goodsname}` : `${t('digital.spot-goods-detail')}`">
-                <template #footer>
-                    <Cell>
-                        <template #title v-if="quote">
-                            <h3>
-                                <span>{{ quote.presettle }}</span>
-                                <span style="color: #999;font-size: 14px;margin-left: 2px;">{{ enumName }}</span>
-                            </h3>
-                            <span :class="quote.lastColor">{{ parsePercent(quote.change) }}</span>
-                        </template>
-                        <template #right-icon>
-                            <span @click="routerToChart">{{ $t('operation.chart') }}</span>
-                        </template>
-                    </Cell>
+                <template #footer v-if="quote">
+                    <CellGroup inset>
+                        <Cell>
+                            <template #title>
+                                <h3>
+                                    <span>{{ quote.presettle }}</span>
+                                    <span style="color: #999;font-size: 14px;margin-left: 4px;">{{ enumName }}</span>
+                                </h3>
+                                <span :class="quote.lastColor">{{ parsePercent(quote.change) }}</span>
+                            </template>
+                            <template #right-icon>
+                                <img :src="'./img/icons/chart.svg'" @click="routerToChart" />
+                            </template>
+                        </Cell>
+                    </CellGroup>
                 </template>
             </app-navbar>
         </template>
-        <div class="contract-goods-detail__price g-layout-block g-layout-block--inset">
-            <h3 :class="quote?.askColor">{{ handleNumberValue(quote?.ask) }}</h3>
-            <h3 :class="quote?.bidColor">{{ handleNumberValue(quote?.bid) }}</h3>
-        </div>
-        <Row class="g-layout-block g-layout-block--inset" gutter="10">
-            <Col span="12">
-            <Button :type="formData.BuyOrSell === BuyOrSell.Buy ? 'success' : 'default'" size="small" block
-                @click="buyOrSell(BuyOrSell.Buy)">{{ $t('digital.buy') }}</Button>
-            </Col>
-            <Col span="12">
-            <Button :type="formData.BuyOrSell === BuyOrSell.Sell ? 'danger' : 'default'" size="small" block
-                @click="buyOrSell(BuyOrSell.Sell)">{{ $t('digital.sell') }}</Button>
-            </Col>
-        </Row>
+        <app-block class="trade-panel" background>
+            <div class="trade-panel-item">
+                <h3 :class="quote?.askColor">{{ handleNumberValue(quote?.ask) }}</h3>
+                <Button :type="formData.BuyOrSell === BuyOrSell.Buy ? 'success' : 'default'"
+                    :plain="formData.BuyOrSell !== BuyOrSell.Buy" size="small" block
+                    @click="buyOrSell(BuyOrSell.Buy)">{{
+                        $t('digital.buy') }}</Button>
+            </div>
+            <div class="trade-panel-item">
+                <h3 :class="quote?.bidColor">{{ handleNumberValue(quote?.bid) }}</h3>
+                <Button :type="formData.BuyOrSell === BuyOrSell.Sell ? 'danger' : 'default'"
+                    :plain="formData.BuyOrSell !== BuyOrSell.Sell" size="small" block
+                    @click="buyOrSell(BuyOrSell.Sell)">{{
+                        $t('digital.sell') }}</Button>
+            </div>
+        </app-block>
         <Form ref="formRef" class="g-form__container" @submit="onSubmit">
             <CellGroup inset>
-                <Field :label="t('digital.pricemode')" is-link>
+                <Field :label="t('digital.pricemode')" label-align="top" arrow-direction="down" center is-link>
                     <template #input>
                         <app-select v-model="formData.PriceMode" :options="options" />
                     </template>
                 </Field>
+            </CellGroup>
+            <CellGroup inset>
                 <Field v-if="formData.PriceMode === PriceMode.Limit" name="OrderPrice" :rules="formRules.OrderPrice"
-                    :label="t('digital.orderprice') + enumName">
+                    :label="`${t('digital.orderprice')}(${enumName})`" label-align="top">
                     <template #input>
                         <app-stepper v-model="formData.OrderPrice" :min="0" :decimal-length="quote?.decimalplace"
                             :step="quote?.decimalvalue" />
                     </template>
                 </Field>
-                <Cell v-else :value="t('digital.optimal')" />
-                <Field name="OrderQty" :rules="formRules.OrderQty" :label="t('quote.goods.orderqty')">
+                <Cell v-else :title="`${t('digital.orderprice')}(${enumName})`" :value="t('digital.optimal')" />
+            </CellGroup>
+            <CellGroup inset>
+                <Field name="OrderQty" :rules="formRules.OrderQty"
+                    :label="`${t('quote.goods.orderqty')}(${quote?.goodscode})`" label-align="top">
                     <template #input>
                         <app-stepper v-model="formData.OrderQty" :min="0" />
                     </template>
                 </Field>
-                <Cell :title="t('digital.openAmount')" :value="formatDecimal(calculations.openAmount) + enumName" />
             </CellGroup>
             <CellGroup inset>
-                <Cell :title="t('digital.maxBalance')" :value="formatDecimal(calculations.maxBalance)" />
-                <Cell :title="t('digital.maxBuyQty')"
-                    :value="handleNoneValue(formatDecimal(calculations.maxBuyQty, 0))" />
-                <Cell :title="t('digital.buyEstimatedFee')" :value="formatDecimal(calculations.buyEstimatedFee)" />
+                <Cell :title="t('digital.openAmount')" :value="formatDecimal(calculations.openAmount) + enumName" />
             </CellGroup>
         </Form>
-        <Row class="g-layout-block g-layout-block--inset">
-            <Col span="24">
+        <app-block class="trade-info">
+            <ul>
+                <li>
+                    <span class="text-small">
+                        {{ t('digital.maxBalance') }}
+                    </span>
+                    <span>
+                        {{ formatDecimal(calculations.maxBalance) }}
+                    </span>
+                </li>
+                <li>
+                    <span class="text-small">
+                        {{ t('digital.maxBuyQty') }}
+                    </span>
+                    <span>
+                        {{ handleNoneValue(formatDecimal(calculations.maxBuyQty, 0)) }}
+                    </span>
+                </li>
+                <li>
+                    <span class="text-small">
+                        {{ t('digital.buyEstimatedFee') }}
+                    </span>
+                    <span>
+                        {{ formatDecimal(calculations.buyEstimatedFee) }}
+                    </span>
+                </li>
+            </ul>
+        </app-block>
+        <app-submitbar>
             <Button :type="formData.BuyOrSell === BuyOrSell.Buy ? 'success' : 'danger'" block @click="formRef?.submit">
-                {{ formData.BuyOrSell === BuyOrSell.Buy ? $t('digital.buy') : $t('digital.sell') }}{{ quote?.goodscode }}
+                {{ formData.BuyOrSell === BuyOrSell.Buy ? $t('digital.buy') : $t('digital.sell') }}
+                {{ quote?.goodscode }}
             </Button>
-            </Col>
-        </Row>
-        <Tabs v-model:active="tabIndex">
+        </app-submitbar>
+        <Tabs class="g-tabs" type="card" v-model:active="tabIndex" shrink>
             <Tab :title="t('digital.position')">
                 <contract-position :params="{ goodsid: goodsId }" v-if="tabIndex === 0" />
             </Tab>
@@ -84,7 +116,7 @@
 
 <script lang="ts" setup>
 import { shallowRef, computed, onMounted, onActivated } from 'vue'
-import { Form, FormInstance, Button, CellGroup, Field, Cell, Tab, Tabs, FieldRule, Col, Row } from 'vant'
+import { Form, FormInstance, Button, CellGroup, Field, Cell, Tab, Tabs, FieldRule } from 'vant'
 import { EValidType, EOrderOperateType, EBuildType } from '@/constants/client'
 import { formatDecimal, handleNoneValue, handleNumberValue, parsePercent } from '@/filters'
 import { useNavigation } from '@mobile/router/navigation'
@@ -94,6 +126,7 @@ import { useOrder } from '@/business/trade'
 import { BuyOrSell, getGoodsCurrencyItemName, PriceMode } from '@/constants/order'
 import AppSelect from '@mobile/components/base/select/index.vue'
 import AppStepper from '@mobile/components/base/stepper/index.vue'
+import AppSubmitbar from '@mobile/components/base/submitbar/index.vue'
 import ContractPosition from '../../components/position/detail/index.vue'
 import ContractOrder from '../../components/order/list/index.vue'
 import ContractAccount from '../../components/account/index.vue'

+ 2 - 1
src/packages/digital/views/contract/goods/list/index.less

@@ -2,6 +2,7 @@
     .table {
         width: 100%;
         text-align: right;
+        padding: 0 var(--van-padding-md);
 
         &-row {
             position: relative;
@@ -34,7 +35,7 @@
         &-cell {
             display: flex;
             flex-direction: column;
-            padding: 10px;
+            padding: 12px 0;
 
             &--media {
                 flex-direction: row;

+ 21 - 27
src/packages/digital/views/contract/goods/list/index.vue

@@ -1,11 +1,6 @@
 <template>
     <app-view class="contract">
-        <template #header>
-            <app-statusbar>
-                <Search shape="round" :placeholder="t('digital.search')" />
-            </app-statusbar>
-        </template>
-        <Tabs v-model:active="currentGroupId" v-if="goodsGroups.length">
+        <Tabs class="g-tabs" type="card" v-model:active="currentGroupId" shrink v-if="goodsGroups.length">
             <template v-for="(item, index) in goodsGroups" :key="index">
                 <Tab :title="goodsGroupeName(item)" :name="item.goodsgroupid">
                     <table class="table" cellspacing="0" cellpadding="0">
@@ -31,7 +26,7 @@
                                     <td>
                                         <div class="table-cell table-cell--media">
                                             <div class="table-cell__image">
-                                                <image-icon :url="getFirstImage(item.thumurls)" />
+                                                <app-image-icon :url="getFirstImage(item.thumurls)" />
                                             </div>
                                             <div class="table-cell__info">
                                                 <span>
@@ -65,7 +60,7 @@
                                     </td>
                                     <td>
                                         <div class="table-cell">
-                                            <span>
+                                            <span :class="item.lastColor">
                                                 {{ parsePercent(item.change) }}
                                             </span>
                                         </div>
@@ -83,12 +78,12 @@
 
 <script lang="ts" setup>
 import { shallowRef, onMounted, computed, onActivated, onUnmounted } from 'vue'
-import { Search, Tab, Tabs, Empty } from 'vant'
+import { Tab, Tabs, Empty } from 'vant'
 import { useNavigation } from '@mobile/router/navigation'
 import { parsePercent, handleNumberValue, getFirstImage } from '@/filters'
 import { i18n, useFuturesStore, useUserStore } from '@/stores'
 import quoteSocket from '@/services/websocket/quote'
-import ImageIcon from '@mobile/components/base/image-icon/index.vue'
+import AppImageIcon from '@mobile/components/base/image-icon/index.vue'
 
 const { router } = useNavigation()
 const userStore = useUserStore()
@@ -96,28 +91,28 @@ const futuresStore = useFuturesStore()
 const currentGroupId = shallowRef(0)
 const { global: { t } } = i18n
 
-const goodsname = (code: string) => {
-    return futuresStore.getI18nGoodsName(code)
-}
-
 const goodsGroups = userStore.userData.goodsgroups.filter((e) => e.marketid === 10101)
 
+const goodsList = computed(() => futuresStore.quotationList.filter((e) => e.goodsgroupid === currentGroupId.value))
+
 const goodsGroupeName = (item: Model.GoodsGroup) => {
     switch (i18n.global.locale) {
-            case 'zh-CN':
-                return item?.goodsgroupname ?? ''
-            case 'en-US':
-                return item?.goodsgroupnameen ?? ''
-            case 'zh-TW':
-                return item?.goodsgroupnametw ?? ''
-            case 'vi':
-                return item?.goodsgroupnamevi ?? ''
-            default:
-                return item?.goodsgroupnameth ?? ''
-        }
+        case 'zh-CN':
+            return item?.goodsgroupname ?? ''
+        case 'en-US':
+            return item?.goodsgroupnameen ?? ''
+        case 'zh-TW':
+            return item?.goodsgroupnametw ?? ''
+        case 'vi':
+            return item?.goodsgroupnamevi ?? ''
+        default:
+            return item?.goodsgroupnameth ?? ''
+    }
 }
 
-const goodsList = computed(() => futuresStore.quotationList.filter((e) => e.goodsgroupid === currentGroupId.value))
+const goodsname = (code: string) => {
+    return futuresStore.getI18nGoodsName(code)
+}
 
 const rowClick = (row: Model.GoodsQuote) => {
     router.push({
@@ -141,7 +136,6 @@ onMounted(() => {
 })
 
 onUnmounted(() => subscribe.stop())
-
 </script>
 
 <style lang="less">

+ 29 - 8
src/packages/digital/views/home/index.less

@@ -1,11 +1,32 @@
 .home {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-
-    &-main {
-        flex: 1;
-        overflow-y: auto;
-        -webkit-overflow-scrolling: touch;
+    &-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        line-height: 1;
+        padding: var(--van-padding-md);
+        padding-bottom: 0;
+
+        &__title {
+            color: #f9bc4a;
+        }
+
+        &__iconbar {
+            .van-image+.van-image {
+                margin-left: 8px;
+            }
+        }
+    }
+
+    .van-tabbar {
+        --van-tabbar-height: 62px;
+        --van-tabbar-background: var(--block-bg-color);
+        --van-tabbar-item-active-background: var(--van-tabbar-background);
+        --van-tabbar-item-text-color: var(--color-info);
+        --van-tabbar-item-active-color: var(--color-primary);
+        --van-tabbar-item-icon-margin-bottom: 8px;
+
+        border-radius: 16px 16px 0 0;
+        overflow: hidden;
     }
 }

+ 42 - 16
src/packages/digital/views/home/index.vue

@@ -1,5 +1,19 @@
 <template>
-  <div class="home">
+  <app-view class="home" flex>
+    <template #header>
+      <app-statusbar>
+        <div class="home-header">
+          <div class="home-header__title">
+            <h2>数字交易</h2>
+          </div>
+          <div class="home-header__iconbar">
+            <app-image-icon url="./img/icons/notice.svg" @click="routerTo('notice-list')" />
+            <app-image-icon url="./img/icons/language.svg" @click="routerTo('setting-luanguage')" />
+          </div>
+        </div>
+        <app-search />
+      </app-statusbar>
+    </template>
     <router-view v-slot="{ Component }">
       <RouterTransition :css="cssTransition">
         <!-- 缓存所有组件 -->
@@ -8,13 +22,18 @@
         </keep-alive>
       </RouterTransition>
     </router-view>
-    <Tabbar v-model="currentTab" active-color="#ff8400" placeholder safe-area-inset-bottom @change="onTabClick">
-      <template v-for="(item, index) in tabbarItems" :key="index">
-        <TabbarItem :icon="item.icon">{{ item.label }}</TabbarItem>
-      </template>
-    </Tabbar>
     <app-updater :ios-update-url="iosUpdateUrl" />
-  </div>
+    <template #footer>
+      <Tabbar v-model="currentTab" :border="false" placeholder @change="onTabClick">
+        <TabbarItem v-for="(item, index) in tabbarItems" :key="index">
+          <template #icon="{ active }">
+            <img :src="active ? item.action : item.icon" />
+          </template>
+          {{ item.label }}
+        </TabbarItem>
+      </Tabbar>
+    </template>
+  </app-view>
 </template>
 
 <script lang="ts" setup>
@@ -23,8 +42,10 @@ import { Tabbar, TabbarItem } from 'vant'
 import { useNavigation } from '@mobile/router/navigation'
 import { useLoginStore } from '@/stores'
 import { i18n } from '@/stores'
+import AppSearch from '@/packages/digital/components/search/index.vue'
 import AppUpdater from '@mobile/components/base/updater/index.vue'
 import RouterTransition from '@mobile/components/base/router-transition/index.vue'
+import AppImageIcon from '@mobile/components/base/image-icon/index.vue'
 
 defineProps({
   iosUpdateUrl: String
@@ -38,33 +59,38 @@ const cssTransition = shallowRef(true) // 是否使用css动画
 const currentTab = shallowRef(0)
 
 // 导航标签列表
-const tabbarItems = [
+const tabbarItems = computed(() => ([
   {
     name: 'home-index',
     label: t('tabbar.home'),
-    icon: 'wap-home',
+    icon: './img/icons/home.svg',
+    action: './img/icons/home-active.svg',
   },
   {
     name: 'home-contract',
     label: t('tabbar.contract'),
-    icon: 'records',
+    icon: './img/icons/contract.svg',
+    action: './img/icons/contract-active.svg',
   },
   {
     name: 'home-listing',
     label: '挂牌',
-    icon: 'records',
+    icon: './img/icons/contract.svg',
+    action: './img/icons/contract-active.svg',
   },
   {
     name: 'home-spot',
     label: t('tabbar.spot'),
-    icon: 'graphic',
+    icon: './img/icons/spot.svg',
+    action: './img/icons/spot-active.svg',
   },
   {
     name: 'home-wallet',
     label: t('tabbar.wallet'),
-    icon: 'cash-back-record',
+    icon: './img/icons/wallet.svg',
+    action: './img/icons/wallet-active.svg',
   }
-]
+]))
 
 const tabIndex = computed(() => {
   const value = window.sessionStorage.getItem('currentTab')
@@ -74,11 +100,11 @@ const tabIndex = computed(() => {
       return parsedValue.index
     }
   }
-  return tabbarItems.findIndex((e) => e.name === route.name)
+  return tabbarItems.value.findIndex((e) => e.name === route.name)
 })
 
 const onTabClick = () => {
-  const { name } = tabbarItems[currentTab.value]
+  const { name } = tabbarItems.value[currentTab.value]
   if (currentTab.value === 0 || loginStore.token) {
     // 缓存当前选中的标签位置,防止 F5 刷新页面后无法定位到当前标签
     window.sessionStorage.setItem('currentTab', JSON.stringify({

+ 35 - 100
src/packages/digital/views/home/main/index.less

@@ -1,119 +1,54 @@
-@import '@mobile/assets/themes/base/mixin.less';
-
 .home-main {
-    &__header {
-        background: linear-gradient(var(--navbar-background), var(--navbar-background) 60%, transparent 60%);
-        padding: 10px;
-    }
-
-    &__iconbar {
-        ul {
-            display: flex;
-            flex-wrap: wrap;
-            padding-top: 12px;
+    &-hot {
+        h4 {
+            font-size: 15px;
+            font-weight: normal;
+            margin-bottom: 12px;
+        }
 
-            &:last-child {
-                padding-bottom: 12px;
-            }
+        section {
+            background-color: var(--block-bg-color);
+            border-radius: 6px;
+            padding-left: 16px;
 
-            li {
+            ul {
                 display: flex;
-                flex-direction: column;
-                align-items: center;
-                width: calc(~'100% / 4');
-                text-align: center;
-
-                .g-icon {
-                    width: 36px;
-                    height: 36px;
-                    font-size: 22px;
-                    color: #fff;
-                    background-color: #000;
-                    border-radius: 50%;
-                    margin-bottom: 4px;
+                padding: 16px;
+                padding-left: 0;
 
-                    &-listing--line {
-                        background-color:#4e9ddb; 
-                    }
-
-                    &-transfer--line {
-                        background-color: #42739b;
-                    }
+                &:not(:first-child) {
+                    border-top: 1px solid rgba(255, 255, 255, 0.04);
+                }
 
-                    &-presale--line {
-                        background-color: #8c8b94;
-                    }
+                &:nth-child(1) {
+                    --van-tag-default-color: #ff2235;
+                }
 
-                    &-auction--line {
-                        background-color: #72c990;
-                    }
+                &:nth-child(2) {
+                    --van-tag-default-color: #fa6720;
+                }
 
-                    &-spot--line {
-                        background-color: #9d6969;
-                    }
+                &:nth-child(3) {
+                    --van-tag-default-color: #fd9605;
+                }
 
-                    &-pricing--line {
-                        background-color: #8272c9;
-                    }
+                li {
+                    flex: 1;
+                    text-align: center;
 
-                    &-swap--line {
-                        background-color: #ebc413;
+                    &:first-child {
+                        flex: 1.5;
+                        text-align: left;
                     }
 
-                    &-quote--line {
-                        background-color: #df4343;
+                    &:last-child {
+                        text-align: right;
                     }
-                }
-            }
-        }
-    }
-
-    &__titlebar {
-        .van-cell__title {
-            font-size: 16px;
-            font-weight: bold;
-        }
-
-        .van-cell__value {
-            flex: none;
-            color: #666;
-        }
-    }
-
-    &__market {
-        .scrollbar {
-            width: 100%;
-            padding: 10px;
-        }
 
-        .van-swipe {
-            height: 32px;
-            background-color: #f6f6f6;
-
-            ul {
-                display: flex;
-                justify-content: space-around;
-                align-items: center;
-                height: inherit;
-                font-size: 13px;
-            }
-        }
-    }
-
-    &__news {
-        .article {
-            &-item {
-                .van-cell__title {
-                    span {
-                        .mixin-text-overflow()
+                    .van-tag {
+                        margin-right: 6px;
                     }
                 }
-
-                .van-cell__value {
-                    flex: initial;
-                    font-size: 12px;
-                    margin-left: 24px;
-                }
             }
         }
     }

+ 65 - 135
src/packages/digital/views/home/main/index.vue

@@ -1,148 +1,78 @@
 <template>
-  <app-view class="home-main">
-    <Banner :data-list="topBanners" />
-    <PullRefresh class="home-main__container" v-model="refreshing" @refresh="onRefresh">
-      <app-block>
-        <Cell :value="$t('common.more')" :to="{ name: 'notice-list' }" icon="volume" is-link>
-          <template #title>
-            <Badge :offset="[10, 8]" :dot="noticeStore.unreadCount > 0">{{ $t('routes.notice') }}</Badge>
-          </template>
-        </Cell>
-      </app-block>
-      <app-block class="home-main__news">
-        <CellGroup class="article">
-          <Cell class="home-main__titlebar" :title="$t('routes.news')" :value="$t('common.more')" icon="fire"
-            :to="{ name: 'news-list' }" is-link />
-          <template v-for="(item, index) in newsList" :key="index">
-            <Cell class="article-item" :title="item.title" :value="formatDate(item.publishdate, 'MM/DD')"
-              :to="{ name: 'news-detail', query: { id: item.id } }" />
-          </template>
-        </CellGroup>
-      </app-block>
-    </PullRefresh>
-  </app-view>
+  <div class="home-main">
+    <app-wallet-total v-if="loginStore.token" />
+    <app-grid :items="gridItems" />
+    <app-block class="home-main-hot">
+      <h4>热门货币</h4>
+      <section>
+        <template v-for="(item, index) in goodsList" :key="index">
+          <ul v-if="index < 3">
+            <li>
+              <Tag>{{ index + 1 }}</Tag>
+              <span>{{ item.goodsid }}/{{ item.goodsname }}</span>
+            </li>
+            <li :class="item.lastColor">{{ parsePercent(item.change) }}</li>
+            <li :class="item.lastColor">{{ item.last.toFixed(item.decimalplace) }}</li>
+          </ul>
+        </template>
+      </section>
+    </app-block>
+    <app-article title="新闻资讯" />
+  </div>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, computed } from 'vue'
-import { Cell, CellGroup, PullRefresh, Badge } from 'vant'
-import { displayname, formatDate } from "@/filters"
-import { queryImageConfigs } from "@/services/api/common"
-import { queryNewTitles } from "@/services/api/news"
-import { useMarketSection } from '@/business/market'
-import { useNoticeStore, useGlobalStore } from '@/stores'
-import Banner from '@mobile/components/base/banner/index.vue'
+import { computed } from 'vue'
+import { Tag } from 'vant'
+import { parsePercent } from '@/filters'
+import { useRequest } from '@/hooks/request'
+import { getHotGoodses } from '@/services/api/goods'
+import { useLoginStore, useFuturesStore, i18n } from '@/stores'
+import { GridItem } from '@/packages/digital/components/grid/types'
+import quoteSocket from '@/services/websocket/quote'
+import AppArticle from '@mobile/components/modules/article/index.vue'
+import AppWalletTotal from '@/packages/digital/components/wallet-total/index.vue'
+import AppGrid from '@/packages/digital/components/grid/index.vue'
 
-// const globalStore = useGlobalStore()
-const noticeStore = useNoticeStore()
-const globalStore = useGlobalStore()
-const refreshing = shallowRef(false) // 是否处于加载中状态
-const topBanners = shallowRef<string[]>([]); // 轮播图列表
-const newsList = shallowRef<Model.NewTitlesRsp[]>([]) // 资讯列表
+const { t } = i18n.global
+const loginStore = useLoginStore()
+const futuresStore = useFuturesStore()
+const subscribe = quoteSocket.createSubscribe()
 
-// 跳转导航页面
-// const switchTab = (routeName: string) => {
-//   if (loginStore.token) {
-//     routerTo(routeName, true)
-//   } else {
-//     routerTo('user-login')
-//   }
-// }
-
-// 获取市场板块
-const { allMarket } = useMarketSection()
-
-const iconbar = computed(() => allMarket.value.reduce<{
-  name: string;
-  label: string;
-  icon: string;
-  markets?: string;
-}[][]>((pre, cur, index) => {
-  if (index > 2) {
-    const item = {
-      name: '',
-      label: displayname(cur),
-      icon: '',
-      markets: cur.marketids
-    }
-    switch (cur.trademode) {
-      case 10:
-        item.name = 'pricing-list'
-        item.icon = 'g-icon-pricing--line'
-        break
-      case 17:
-        item.name = 'spot-list'
-        item.icon = 'g-icon-spot--line'
-        break
-      case 46:
-        item.name = 'swap-list'
-        item.icon = 'g-icon-swap--line'
-        break
-      case 48:
-        item.name = 'presale-list'
-        item.icon = 'g-icon-auction--line'
-        break
-      case 49:
-        item.name = 'transfer-list'
-        item.icon = 'g-icon-transfer--line'
-        break
-      case 16:
-      case 50:
-        item.name = 'goods-list'
-        item.icon = 'g-icon-listing--line'
-        break
-      case 51:
-        item.name = 'ballot-list'
-        item.icon = 'g-icon-presale--line'
-        break
-      case 53:
-          item.name = 'mall-list'
-          item.icon = 'g-icon-quote--line'
-          break
-      case 54:
-        item.name = 'score-list'
-        item.icon = 'g-icon-quote--line'
-        break
-      case 99:
-        item.name = 'market-list'
-        item.icon = 'g-icon-quote--line'
-        break
-    }
-    const i = pre.length - 1
-    if (i < 0 || pre[i].length > 3) {
-      pre.push([item])
-    } else {
-      pre[i].push(item)
-    }
+const gridItems = computed<GridItem[]>(() => ([
+  {
+    text: t('digital.wallet-deposit'),
+    icon: './img/icons/deposit.svg',
+    to: { name: 'wallet-deposit-currency' }
+  },
+  {
+    text: t('digital.wallet-withdraw'),
+    icon: './img/icons/withdraw.svg',
+    to: { name: 'wallet-withdraw-currency' }
+  },
+  {
+    text: t('digital.wallet-transfer'),
+    icon: './img/icons/transfer.svg',
+    to: { name: 'wallet-transfer' }
+  },
+  {
+    text: t('digital.setting'),
+    icon: './img/icons/setting.svg',
+    to: { name: 'setting' }
   }
-  return pre
-}, []))
+]))
 
-// 下拉刷新
-const onRefresh = () => {
-  if (!topBanners.value.length) {
-    queryImageConfigs({
-      data: {
-        imageType: 1,
-      }
-    }).then((res) => {
-      topBanners.value = res.data.map((e) => e.imagepath)
-    })
-  }
-  // 市场资讯
-  queryNewTitles({
-    data: {
-      page: 1,
-      pagesize: 10,
+const { dataList } = useRequest(getHotGoodses, {
+  onSuccess: (res) => {
+    if (!loginStore.token) {
+      futuresStore.goodsList = res.data
+      futuresStore.getQuoteDay()
     }
-  }).then((res) => {
-    newsList.value = res.data
-  }).finally(() => {
-    refreshing.value = false
-  })
-}
+    subscribe.start(...res.data.map((e) => e.goodscode))
+  }
+})
 
-onRefresh()
+const goodsList = computed(() => futuresStore.quotationList.filter((q) => dataList.value.some((e) => e.goodsid === q.goodsid)))
 </script>
 
 <style lang="less">

+ 38 - 0
src/packages/digital/views/notice/list/components/detail/index.less

@@ -0,0 +1,38 @@
+.notice-detail {
+    background-color: #fff;
+
+    [class="van-theme-dark"] & {
+        background-color: #000;
+    }
+
+    .app-view__body {
+        padding-bottom: 60px;
+    }
+
+    &__container {
+        padding: 16px;
+
+        >h1 {
+            font-size: 18px;
+            font-weight: bold;
+        }
+
+        >h4 {
+            display: flex;
+            align-items: center;
+            gap: 6px;
+            font-size: 12px;
+            color: #999;
+            padding: 16px 0;
+        }
+
+        >p {
+            font-size: 14px;
+            line-height: 24px;
+
+            img {
+                max-width: 100%;
+            }
+        }
+    }
+}

+ 54 - 0
src/packages/digital/views/notice/list/components/detail/index.vue

@@ -0,0 +1,54 @@
+<template>
+    <app-modal direction="right-top" height="100%" width="100%" v-model:show="showModal">
+        <app-view class="notice-detail">
+            <template #header>
+                <app-navbar :title="$t('notices.details')" @back="closed" />
+            </template>
+            <section class="notice-detail__container">
+                <h1>{{ selectedRow.title }}</h1>
+                <h4>
+                    <span>{{ formatDate(selectedRow.createtime, 'YYYY-MM-DD HH:mm:ss') }}</span>
+                </h4>
+                <HtmlContainer :context="formatHtmlString(selectedRow.content)" />
+            </section>
+        </app-view>
+    </app-modal>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, PropType, onMounted } from 'vue'
+import { formatDate, formatHtmlString } from '@/filters'
+import { useNoticeStore } from '@/stores'
+import AppModal from '@/components/base/modal/index.vue'
+import HtmlContainer from '@mobile/components/base/html-container/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.NoticeRsp>,
+        required: true
+    }
+})
+
+const noticeStore = useNoticeStore()
+const showModal = shallowRef(true)
+
+// 关闭弹窗
+const closed = () => {
+    showModal.value = false
+}
+
+onMounted(() => {
+    if (!props.selectedRow.readed) {
+        noticeStore.updateNoticeReaded(props.selectedRow.autoid)
+    }
+})
+
+// 暴露组件属性给父组件调用
+defineExpose({
+    closed,
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 62 - 0
src/packages/digital/views/notice/list/index.vue

@@ -0,0 +1,62 @@
+<template>
+    <app-view flex>
+        <template #header>
+            <app-navbar :title="$t('notices.title')" />
+        </template>
+        <Tabs v-model:active="active">
+            <Tab :title="$t('notices.announcement')" :name="1" />
+            <Tab :title="$t('notices.notice')" :name="2" />
+        </Tabs>
+        <app-pull-refresh ref="pullRefreshRef" v-model:loading="noticeStore.loading" @refresh="onRefresh">
+            <CellGroup class="article" style="background-color: transparent;padding-top: 10px;" v-if="dataList.length">
+                <template v-for="(item, index) in dataList" :key="index">
+                    <Cell :value="formatDate(item.createtime, 'YYYY/MM/DD HH:mm:ss')" @click="openDetail(item)">
+                        <template #title>
+                            <TextEllipsis :content="handleNoneValue(item.title)" />
+                        </template>
+                        <template #value>
+                            <span style="font-size: 13px">
+                                {{ formatDate(item.createtime, 'YYYY/MM/DD HH:mm:ss') }}
+                            </span>
+                        </template>
+                        <template #right-icon v-if="!item.readed">
+                            <Badge :offset="[0, 5]" dot />
+                        </template>
+                    </Cell>
+                </template>
+            </CellGroup>
+        </app-pull-refresh>
+        <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)"
+            @closed="closeComponent" v-if="componentId" />
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, defineAsyncComponent, computed } from 'vue'
+import { Tab, Tabs, CellGroup, Cell, TextEllipsis, Badge } from 'vant'
+import { formatDate, handleNoneValue } from '@/filters'
+import { useComponent } from '@/hooks/component'
+import { useNoticeStore } from '@/stores'
+import AppPullRefresh from '@mobile/components/base/pull-refresh/index.vue'
+
+const componentMap = new Map<string, unknown>([
+    ['detail', defineAsyncComponent(() => import('./components/detail/index.vue'))],
+])
+
+const noticeStore = useNoticeStore()
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
+const active = shallowRef(1)
+const selectedRow = shallowRef<Model.NoticeRsp>()
+
+const dataList = computed(() => noticeStore.localizedDataList.filter((e) => e.msgtype === active.value))
+
+const openDetail = (item: Model.NoticeRsp) => {
+    selectedRow.value = item
+    openComponent('detail')
+}
+
+const onRefresh = () => {
+    noticeStore.fetchDataList()
+}
+</script>

+ 3 - 0
src/packages/digital/views/setting/index.less

@@ -1,4 +1,7 @@
 .setting {
+    --van-cell-group-background: transparent;
+    --van-cell-background: transparent;
+
     .app-iconfont {
         .g-icon {
             min-width: 24px;

+ 15 - 6
src/packages/digital/views/setting/index.vue

@@ -3,7 +3,7 @@
         <template #header>
             <app-navbar :title="t('mine.settings')" />
         </template>
-        <CellGroup :title="t('mine.account')" inset>
+        <CellGroup :title="t('mine.account')">
             <Cell is-link :to="{ name: 'account-certification' }"
                 v-if="userStore.userAccount.hasauth === AuthStatus.Uncertified && userStore.userAccount.modifystatus === 1">
                 <template #title>
@@ -26,13 +26,13 @@
                 </template>
             </Cell>
         </CellGroup>
-        <CellGroup :title="t('mine.system')" inset>
-            <Cell is-link v-if="globalStore.getSystemInfo('i18nEnabled')">
+        <CellGroup :title="t('mine.system')">
+            <Cell is-link :to="{ name: 'setting-luanguage' }" v-if="globalStore.getSystemInfo('i18nEnabled')">
                 <template #title>
                     <app-iconfont icon="g-icon-lang">{{ $t('mine.setting.language') }}</app-iconfont>
                 </template>
                 <template #value>
-                    <app-luanguage />
+                    {{ language }}
                 </template>
             </Cell>
             <Cell is-link @click="userLogout">
@@ -45,18 +45,27 @@
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue'
 import { Cell, CellGroup } from 'vant'
 import { dialog } from '@/utils/vant'
 import { AuthStatus } from '@/constants/account'
+import { useRequest } from '@/hooks/request'
+import { getI18nConfigs } from '@/services/api/common'
 import { useUserStore, useGlobalStore, i18n } from '@/stores'
 import eventBus from '@/services/bus'
 import AppIconfont from '@/components/base/iconfont/index.vue'
-import AppLuanguage from '@mobile/components/modules/luanguage/index.vue'
 
+const { t } = i18n.global
 const globalStore = useGlobalStore()
 const userStore = useUserStore()
 
-const { t } = i18n.global
+const { dataList } = useRequest(getI18nConfigs)
+
+// 当前语言
+const language = computed(() => {
+    const locale = dataList.value.find((e) => e.langcode === i18n.global.locale)
+    return locale?.langname ?? i18n.global.locale
+})
 
 const userLogout = () => {
     dialog({

+ 4 - 0
src/packages/digital/views/setting/luanguage/index.less

@@ -0,0 +1,4 @@
+.setting-luanguage{
+    --van-cell-group-background: transparent;
+    --van-cell-background: transparent;
+}

+ 53 - 0
src/packages/digital/views/setting/luanguage/index.vue

@@ -0,0 +1,53 @@
+<template>
+    <app-view class="setting-luanguage">
+        <template #header>
+            <app-navbar title="语言设置" />
+        </template>
+        <CellGroup>
+            <template v-for="(item, index) in dataList" :key="index">
+                <Cell :title="item.langname" clickable @click="onClick(item.langcode as Language)">
+                    <template #right-icon v-if="item.langcode === i18n.global.locale">
+                        <img :src="'./img/icons/success.svg'" />
+                    </template>
+                </Cell>
+            </template>
+        </CellGroup>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { Locale, Cell, CellGroup } from 'vant'
+import { useNavigation } from '@mobile/router/navigation'
+import { Language } from '@/constants/language'
+import { useRequest } from '@/hooks/request'
+import { getI18nConfigs } from '@/services/api/common'
+import { localData } from '@/stores/storage'
+import { i18n } from '@/stores'
+import enUS from 'vant/es/locale/lang/en-US'
+import zhCN from 'vant/es/locale/lang/zh-CN'
+import thTH from 'vant/es/locale/lang/th-TH'
+import zhTW from 'vant/es/locale/lang/zh-TW'
+import viVN from 'vant/es/locale/lang/vi-VN'
+
+const { routerBack } = useNavigation()
+const { dataList } = useRequest(getI18nConfigs)
+
+const vantlocaleMap = {
+    [Language.Simplified]: zhCN,
+    [Language.Traditional]: zhTW,
+    [Language.English]: enUS,
+    [Language.Thai]: thTH,
+    [Language.Vietnamese]: viVN,
+}
+
+const onClick = (value: Language) => {
+    localData.setValue('appLanguage', value)
+    i18n.global.locale = value
+    Locale.use(value.toString(), vantlocaleMap[value])
+    routerBack()
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 4 - 7
src/packages/digital/views/spot/goods/chart/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <app-view class="spot-goods-chart g-layout">
+    <app-view class="spot-goods-chart">
         <template #header>
             <app-navbar :title="quote ? quote.goodscode : '图表'" />
         </template>
@@ -50,14 +50,10 @@
             <goods-chart v-bind="{ theme: 'Dark', goodsCode: quote.goodscode }" />
         </template>
         <template #footer>
-            <Row class="g-layout-block g-layout-block--inset" gutter="10">
-                <Col span="12">
+            <app-submitbar>
                 <Button type="success" block @click="routerBack({ buyOrSell: BuyOrSell.Buy })">买入</Button>
-                </Col>
-                <Col span="12">
                 <Button type="danger" block @click="routerBack({ buyOrSell: BuyOrSell.Sell })">卖出</Button>
-                </Col>
-            </Row>
+            </app-submitbar>
         </template>
     </app-view>
 </template>
@@ -69,6 +65,7 @@ import { handleNumberValue, parsePercent } from '@/filters'
 import { BuyOrSell } from '@/constants/order'
 import { useNavigation } from '@mobile/router/navigation'
 import { useFuturesStore } from '@/stores'
+import AppSubmitbar from '@mobile/components/base/submitbar/index.vue'
 
 const GoodsChart = defineAsyncComponent(() => import('@mobile/components/modules/hqchart/index.vue'))
 

+ 7 - 12
src/packages/digital/views/spot/goods/detail/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <app-view class="sopt-goods-detail g-layout g-form">
+    <app-view class="sopt-goods-detail g-form">
         <template #header>
             <app-navbar :title="quote ? quote.goodscode : '交易'">
                 <template #footer>
@@ -17,16 +17,12 @@
                 </template>
             </app-navbar>
         </template>
-        <Row class="g-layout-block g-layout-block--inset" gutter="10">
-            <Col span="12">
+        <app-submitbar>
             <Button :type="formData.BuyOrSell === BuyOrSell.Buy ? 'success' : 'default'" size="small" block
                 @click="formData.BuyOrSell = BuyOrSell.Buy">买入</Button>
-            </Col>
-            <Col span="12">
             <Button :type="formData.BuyOrSell === BuyOrSell.Sell ? 'danger' : 'default'" size="small" block
                 @click="formData.BuyOrSell = BuyOrSell.Sell">卖出</Button>
-            </Col>
-        </Row>
+        </app-submitbar>
         <Form ref="formRef" class="g-form__container" @submit="onSubmit">
             <CellGroup inset>
                 <Field label="类型" is-link>
@@ -64,14 +60,12 @@
                 <Cell title="可卖数量" :value="formatDecimal(calculations.maxSellQty, baseAccount?.currencydecimalplace)" />
             </CellGroup>
         </Form>
-        <Row class="g-layout-block g-layout-block--inset">
-            <Col span="24">
+        <app-submitbar>
             <Button type="success" block v-if="formData.BuyOrSell === BuyOrSell.Buy"
                 @click="formRef?.submit">买入</Button>
             <Button type="danger" block v-if="formData.BuyOrSell === BuyOrSell.Sell"
                 @click="formRef?.submit">卖出</Button>
-            </Col>
-        </Row>
+        </app-submitbar>
         <Tabs v-model:active="tabIndex" sticky>
             <Tab :title="t('digital.order')">
                 <spot-order :params="{ goodsid: goodsId, orderstatuses: '3,7' }" />
@@ -87,7 +81,7 @@
 import { shallowRef, reactive, computed, onMounted, onUnmounted, onActivated } from 'vue'
 import { Form, Button, CellGroup, Field, Cell, Tab, Tabs, Col, Row, FormInstance, showDialog, FieldRule } from 'vant'
 import { fullloading } from '@/utils/vant'
-import { parsePercent, formatDecimal,handleNumberValue } from '@/filters'
+import { parsePercent, formatDecimal, handleNumberValue } from '@/filters'
 import { EBuildType, EDelistingType, EListingSelectType, EOrderOperateType, EValidType } from '@/constants/client'
 import { BuyOrSell, PriceMode, getPricemode2List } from '@/constants/order'
 import { useNavigation } from '@mobile/router/navigation'
@@ -96,6 +90,7 @@ import { i18n, useFuturesStore, useUserStore } from '@/stores'
 import { useSpotAccountStore } from '../../../wallet/components/spot/composables'
 import quoteSocket from '@/services/websocket/quote'
 import Long from 'long'
+import AppSubmitbar from '@mobile/components/base/submitbar/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 import AppStepper from '@mobile/components/base/stepper/index.vue'
 import SpotOrder from '../../components/order/index.vue'

+ 4 - 9
src/packages/digital/views/spot/goods/list/index.vue

@@ -1,10 +1,5 @@
 <template>
     <app-view class="spot">
-        <template #header>
-            <app-statusbar>
-                <Search shape="round" :placeholder="t('digital.search')" />
-            </app-statusbar>
-        </template>
         <Tabs v-model:active="currentGroupId" v-if="goodsGroups.length">
             <template v-for="(item, index) in goodsGroups" :key="index">
                 <Tab :title="item.goodsgroupname" :name="item.goodsgroupid">
@@ -28,7 +23,7 @@
                                     <td>
                                         <div class="table-cell table-cell--media">
                                             <div class="table-cell__image">
-                                                <image-icon :url="getFirstImage(item.thumurls)" />
+                                                <app-image-icon :url="getFirstImage(item.thumurls)" />
                                             </div>
                                             <div class="table-cell__info">
                                                 <span>
@@ -49,7 +44,7 @@
                                     </td>
                                     <td>
                                         <div class="table-cell">
-                                            <span>
+                                            <span :class="item.lastColor">
                                                 {{ parsePercent(item.change) }}
                                             </span>
                                         </div>
@@ -67,12 +62,12 @@
 
 <script lang="ts" setup>
 import { shallowRef, onMounted, computed } from 'vue'
-import { Search, Tab, Tabs, Empty } from 'vant'
+import { Tab, Tabs, Empty } from 'vant'
 import { useNavigation } from '@mobile/router/navigation'
 import { parsePercent, handleNumberValue, getFirstImage } from '@/filters'
 import { i18n, useFuturesStore, useUserStore } from '@/stores'
 import quoteSocket from '@/services/websocket/quote'
-import ImageIcon from '@mobile/components/base/image-icon/index.vue'
+import AppImageIcon from '@mobile/components/base/image-icon/index.vue'
 
 const { router } = useNavigation()
 const userStore = useUserStore()

+ 4 - 2
src/packages/digital/views/wallet/components/spot/index.less

@@ -2,8 +2,10 @@
     .card {
         display: flex;
         justify-content: space-between;
-        border-bottom: 1px solid #171f2d;
-        padding: 10px 20px;
+        background-color: var(--block-bg-color);
+        border-radius: 6px;
+        padding: 12px;
+        margin-bottom: 12px;
 
         &-section {
             display: flex;

+ 5 - 5
src/packages/digital/views/wallet/components/spot/index.vue

@@ -1,6 +1,6 @@
 <!-- 现货-账户 -->
 <template>
-    <div class="spot-account">
+    <app-block class="spot-account">
         <template v-for="(item, index) in spotAccountStore.dataList" :key="index">
             <div class="card">
                 <div class="card-section">
@@ -9,7 +9,7 @@
                     </div>
                     <div class="card-section__info">
                         <span>
-                            <b>{{ item.currencycode }}</b>
+                            {{ item.currencycode }}
                         </span>
                         <span class="text-small">
                             {{ getDigitalCurrencyName(item.currencyid) }}
@@ -18,10 +18,10 @@
                 </div>
                 <div class="card-section" @click="onClick(item)" v-if="item.digitalaccountid">
                     <div class="card-section__balance">
-                        <span>
+                        <span class="text-small">
                             {{ t('digital.balance') + `(${item.currencycode})` }}
                         </span>
-                        <span class="text-small">
+                        <span>
                             {{ formatDecimal(item.currentbalance, item.currencydecimalplace) }}
                         </span>
                     </div>
@@ -38,7 +38,7 @@
         </template>
         <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)"
             @closed="closeComponent" v-if="componentId" />
-    </div>
+    </app-block>
 </template>
 
 <script lang="ts" setup>

+ 2 - 4
src/packages/digital/views/wallet/currency/index.vue

@@ -3,7 +3,7 @@
         <template #header>
             <app-navbar :title="title">
                 <template #footer>
-                    <Search shape="round" :placeholder="t('digital.search')" />
+                    <app-search />
                 </template>
             </app-navbar>
         </template>
@@ -12,10 +12,9 @@
 </template>
 
 <script lang="ts" setup>
-import { Search } from 'vant'
 import { useNavigation } from '@mobile/router/navigation'
+import AppSearch from '@/packages/digital/components/search/index.vue'
 import SpotAccount from '../components/spot/index.vue'
-import { i18n } from '@/stores'
 
 const props = defineProps({
     title: {
@@ -28,7 +27,6 @@ const props = defineProps({
     }
 })
 
-const { global: { t } } = i18n
 const { router } = useNavigation()
 
 const navigateTo = (currencyId: number) => {

+ 31 - 28
src/packages/digital/views/wallet/deposit/index.vue

@@ -1,40 +1,43 @@
 <!-- 钱包-充值 -->
 <template>
-    <app-view class="wallet-deposit g-layout g-form">
+    <app-view class="wallet-deposit g-form">
         <template #header>
             <app-navbar :title="t('digital.wallet-deposit')" />
         </template>
-        <Form ref="formRef" class="g-form__container g-layout-block" @submit="onSubmit">
+        <Form ref="formRef" class="g-form__container" @submit="onSubmit">
             <CellGroup inset>
-                <app-field-currency name="currency" :label="t('banksign.currency')" label-align="top" :rules="formRules.currency"
-                    v-model="state.currencyId" @change="onCurrencyChange" />
+                <app-field-currency name="currency" :label="t('banksign.currency')" label-align="top"
+                    :rules="formRules.currency" v-model="state.currencyId" @change="onCurrencyChange" />
             </CellGroup>
             <CellGroup inset>
-                <app-field-token name="token" :label="t('digital.depositnetwork')" label-align="top" :rules="formRules.token"
-                    v-model="state.tokenId" :currency="state.currencyName" @change="onTokenChange" />
+                <app-field-token name="token" :label="t('digital.depositnetwork')" label-align="top"
+                    :rules="formRules.token" v-model="state.tokenId" :currency="state.currencyName"
+                    @change="onTokenChange" />
+            </CellGroup>
+            <CellGroup inset v-if="tokenItem">
+                <Cell :title="t('account.balance')" :value="balance" />
+                <app-block class="wallet-deposit__address">
+                    <dl>
+                        <dt>
+                            <h3>{{ t('digital.depositaddres') }}</h3>
+                            <span>{{ `${t('digital.tips11')}` }} {{ state.currencyName }} {{ `${t('digital.tips12')}`
+                                }}</span>
+                        </dt>
+                        <dd v-for="(item, index) in addressList" :key="index">
+                            <span>{{ item.address }}</span>
+                            <Icon name="qr" @click="openQRcode(item.address)" />
+                            <Icon name="link-o" :data-clipboard-text="item.address" v-copy="onCopy" />
+                        </dd>
+                    </dl>
+                    <div>
+                        <Button type="primary" size="small" block @click="formRef?.submit">{{ $t('digital.newaddress')
+                            }}</Button>
+                    </div>
+                </app-block>
             </CellGroup>
         </Form>
-        <CellGroup inset v-if="tokenItem">
-            <Cell :title="t('account.balance')" :value="balance" />
-            <div class="wallet-deposit__address">
-                <dl class="g-layout-block g-layout-block--inset">
-                    <dt>
-                        <h3>{{ t('digital.depositaddres') }}</h3>
-                        <span>{{ `${t('digital.tips11')}` }} {{ currentyItem?.label }} {{ `${t('digital.tips12')}` }}</span>
-                    </dt>
-                    <dd v-for="(item, index) in addressList" :key="index">
-                        <span>{{ item.address }}</span>
-                        <Icon name="qr" @click="openQRcode(item.address)" />
-                        <Icon name="link-o" :data-clipboard-text="item.address" v-copy="onCopy" />
-                    </dd>
-                </dl>
-                <div class="g-layout-block g-layout-block--inset">
-                    <Button type="primary" size="small" block @click="formRef?.submit">{{ $t('digital.newaddress') }}</Button>
-                </div>
-            </div>
-        </CellGroup>
-        <Dialog v-model:show="state.showDialog" :show-confirm-button="false" :cancel-button-text="t('operation.close1')" show-cancel-button
-            destroy-on-close>
+        <Dialog v-model:show="state.showDialog" :show-confirm-button="false" :cancel-button-text="t('operation.close1')"
+            show-cancel-button destroy-on-close>
             <div class="wallet-deposit__qrcode">
                 <app-qrcode :text="state.qrContent" :width="240" />
             </div>
@@ -90,7 +93,7 @@ const balance = computed(() => {
 const formRules: { [key: string]: FieldRule[] } = {
     currenty: [{
         message: t('digital.tips4'),
-        validator: () => !!state.currentyId
+        validator: () => !!state.currencyId
     }],
     token: [{
         message: t('digital.tips3'),

+ 40 - 22
src/packages/digital/views/wallet/index.vue

@@ -1,39 +1,57 @@
 <!-- 钱包 -->
 <template>
     <app-view class="wallet">
-        <template #header>
-            <app-statusbar />
-        </template>
-        <Grid :border="false">
-            <GridItem icon="pending-payment" :text="t('digital.wallet-deposit')" :to="{ name: 'wallet-deposit-currency' }" />
-            <GridItem icon="paid" :text="t('digital.wallet-withdraw')" :to="{ name: 'wallet-withdraw-currency' }" />
-            <GridItem icon="peer-pay" :text="t('digital.wallet-transfer')" :to="{ name: 'wallet-transfer' }" />
-            <GridItem icon="setting-o" :text="t('digital.setting')" :to="{ name: 'setting' }" />
-        </Grid>
-        <Tabs v-model:active="currentTabIndex">
-            <Tab :title="t('tabbar.contract')">
-                <contract-account @click="navigateToContractDetail" />
-            </Tab>
-            <Tab :title="t('tabbar.spot')">
-                <Search shape="round" :placeholder="t('digital.search')" />
-                <spot-account @click="navigateToSpotDetail" />
-            </Tab>
-        </Tabs>
+        <app-wallet-total />
+        <app-grid :items="gridItems" />
+        <app-switch-tab v-model="currentTabIndex" :options="tabs" />
+        <contract-account @click="navigateToContractDetail" v-if="currentTabIndex === 0" />
+        <spot-account @click="navigateToSpotDetail" v-if="currentTabIndex === 1" />
     </app-view>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
-import { Tab, Tabs, Grid, GridItem, Search } from 'vant'
+import { shallowRef, computed } from 'vue'
 import { useNavigation } from '@mobile/router/navigation'
+import { i18n } from '@/stores'
+import { GridItem } from '@/packages/digital/components/grid/types'
+import AppGrid from '@/packages/digital/components/grid/index.vue'
+import AppWalletTotal from '@/packages/digital/components/wallet-total/index.vue'
+import AppSwitchTab from '@/packages/digital/components/switch-tab/index.vue'
 import SpotAccount from './components/spot/index.vue'
 import ContractAccount from './components/contract/index.vue'
-import { i18n } from '@/stores'
 
+const { t } = i18n.global
 const { router } = useNavigation()
-const { global: { t } } = i18n
 const currentTabIndex = shallowRef(0)
 
+const tabs = computed(() => ([
+    { label: t('tabbar.contract') },
+    { label: t('tabbar.spot') }
+]))
+
+const gridItems = computed<GridItem[]>(() => ([
+    {
+        text: t('digital.wallet-deposit'),
+        icon: './img/icons/deposit.svg',
+        to: { name: 'wallet-deposit-currency' }
+    },
+    {
+        text: t('digital.wallet-withdraw'),
+        icon: './img/icons/withdraw.svg',
+        to: { name: 'wallet-withdraw-currency' }
+    },
+    {
+        text: t('digital.wallet-transfer'),
+        icon: './img/icons/transfer.svg',
+        to: { name: 'wallet-transfer' }
+    },
+    {
+        text: t('digital.setting'),
+        icon: './img/icons/setting.svg',
+        to: { name: 'setting' }
+    }
+]))
+
 const navigateToSpotDetail = (currencyId: number) => {
     router.push({
         name: 'spot-detail',

+ 11 - 9
src/packages/digital/views/wallet/withdraw/index.vue

@@ -1,24 +1,25 @@
 <!-- 钱包-提现 -->
 <template>
-    <app-view class="wallet-withdraw g-layout g-form">
+    <app-view class="wallet-withdraw g-form">
         <template #header>
             <app-navbar :title="t('digital.wallet-withdraw')" />
         </template>
-        <Form ref="formRef" class="g-form__container g-layout-block" @submit="onSubmit">
+        <Form ref="formRef" class="g-form__container" @submit="onSubmit">
             <CellGroup inset>
-                <app-field-currency name="currency" :label="t('banksign.currency')" label-align="top" :rules="formRules.currency"
-                    v-model="formData.CurrencyID" @change="onCurrencyChange" />
+                <app-field-currency name="currency" :label="t('banksign.currency')" label-align="top"
+                    :rules="formRules.currency" v-model="formData.CurrencyID" @change="onCurrencyChange" />
             </CellGroup>
             <CellGroup inset>
                 <app-field-token name="token" :label="t('digital.network')" label-align="top" :rules="formRules.token"
                     v-model="state.tokenId" :currency="state.currencyName" @change="onTokenChange" />
             </CellGroup>
             <CellGroup inset>
-                <Field name="Address" v-model="formData.Address" :label="t('quote.pricing.address')" label-align="top" :placeholder="t('common.pleaseenter')"
-                    :rules="formRules.Address" />
+                <Field name="Address" v-model="formData.Address" :label="t('quote.pricing.address')" label-align="top"
+                    :placeholder="t('common.pleaseenter')" :rules="formRules.Address" />
             </CellGroup>
             <CellGroup inset>
-                <Cell :title="t('tss.qty')" :value="`${t('digital.available')} (${formatDecimal(balance, accountItem?.currencydecimalplace)})`" />
+                <Cell :title="t('tss.qty')"
+                    :value="`${t('digital.available')} (${formatDecimal(balance, accountItem?.currencydecimalplace)})`" />
                 <Field name="Amount" label-align="top" :rules="formRules.Amount">
                     <template #input>
                         <app-stepper v-model="formData.Amount" :min="0" :max="balance" />
@@ -26,9 +27,9 @@
                 </Field>
             </CellGroup>
         </Form>
-        <div class="g-form__footer inset">
+        <app-submitbar>
             <Button type="primary" block @click="formRef?.submit">{{ $t('digital.wallet-withdraw') }}</Button>
-        </div>
+        </app-submitbar>
     </app-view>
 </template>
 
@@ -41,6 +42,7 @@ import { digitalAccountWithdrawApply } from '@/services/api/digital'
 import { useNavigation } from '@mobile/router/navigation'
 import { useSpotAccountStore } from '../components/spot/composables'
 import AppStepper from '@mobile/components/base/stepper/index.vue'
+import AppSubmitbar from '@mobile/components/base/submitbar/index.vue'
 import AppFieldCurrency from '@/packages/digital/components/field-currency/index.vue'
 import AppFieldToken from '@/packages/digital/components/field-token/index.vue'
 import { i18n } from '@/stores'

+ 4 - 0
src/packages/mobile/components/base/stepper/index.less

@@ -0,0 +1,4 @@
+.app-stepper {
+    --van-stepper-background: transparent;
+    --van-stepper-button-disabled-color: transparent;
+}

+ 6 - 2
src/packages/mobile/components/base/stepper/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <Stepper :max="max" :button-size="32" />
+    <Stepper class="app-stepper" :max="max" :button-size="32" />
 </template>
 
 <script lang="ts" setup>
@@ -11,4 +11,8 @@ defineProps({
         default: 999999999
     }
 })
-</script>
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 7 - 0
src/packages/mobile/components/base/submitbar/index.less

@@ -0,0 +1,7 @@
+.app-submitbar {
+    .van-space {
+        &-item {
+            flex: 1;
+        }
+    }
+}

+ 16 - 0
src/packages/mobile/components/base/submitbar/index.vue

@@ -0,0 +1,16 @@
+<template>
+    <app-block class="app-submitbar">
+        <Space :size="12" fill>
+            <slot></slot>
+        </Space>
+    </app-block>
+</template>
+
+<script lang="ts" setup>
+import { Space } from 'vant'
+import AppBlock from '../../layouts/block/index.vue'
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 2 - 2
src/packages/mobile/components/base/switch-tab/index.vue

@@ -2,8 +2,8 @@
     <div class="app-switch">
         <div class="app-switch-tabs" :style="styles">
             <template v-for="(item, index) in options" :key="index">
-                <div :class="{ 'app-switch-tabs__item': true, 'active': item[customFieldName.value] === selectedValue }"
-                    @click="onChange(item[customFieldName.value])">
+                <div :class="{ 'app-switch-tabs__item': true, 'active': (item[customFieldName.value] ?? index) === selectedValue }"
+                    @click="onChange(item[customFieldName.value] ?? index)">
                     <slot :option="options[index]" :index="index">
                         <span>{{ item[customFieldName.label] }}</span>
                     </slot>

+ 9 - 19
src/packages/mobile/components/layouts/block/index.less

@@ -1,27 +1,17 @@
 .app-block {
-    padding: 10px;
-    padding-bottom: 0;
+    padding-top: 12px;
+    padding-bottom: 12px;
 
-    &:last-of-type {
-        padding-bottom: 10px;
+    &+& {
+        padding-top: 0;
     }
 
-    &__wrapper {
-        background-color: #fff;
-        border-radius: 8px;
-        overflow: hidden;
-    }
-
-    &.bg &__wrapper {
-        //background: #fff url('../../../assets/images/block-bg.png') no-repeat center bottom;
-        background-size: 100%;
+    &--inset {
+        padding-left: var(--van-padding-md);
+        padding-right: var(--van-padding-md);
     }
 
-    &.flex &__wrapper {
-        display: flex;
-    }
-
-    &.flex--column &__wrapper {
-        flex-direction: column;
+    &__inner {
+        overflow: hidden;
     }
 }

+ 24 - 2
src/packages/mobile/components/layouts/block/index.vue

@@ -1,12 +1,34 @@
 <template>
-    <div class="app-block">
-        <div class="app-block__wrapper">
+    <div :class="className">
+        <div class="app-block__inner" :class="$attrs.class" :style="styles">
             <slot></slot>
         </div>
     </div>
 </template>
 
 <script lang="ts" setup>
+import { CSSProperties } from 'vue'
+
+defineOptions({
+    inheritAttrs: false
+})
+
+const props = defineProps({
+    inset: {
+        type: Boolean,
+        default: true
+    },
+    background: {
+        type: Boolean,
+        default: false
+    }
+})
+
+const className = ['app-block', { 'app-block--inset': props.inset }]
+
+const styles: CSSProperties = {
+    backgroundColor: props.background ? 'var(--block-bg-color)' : 'transparent'
+}
 </script>
 
 <style lang="less">

+ 1 - 1
src/packages/mobile/components/layouts/scroll-view/index.vue

@@ -96,7 +96,7 @@ onMounted(() => {
   }, 0)
 
   const addScrollListener = (el: HTMLDivElement) => {
-    el.addEventListener('scroll', onScroll) // 监听滚动条事件
+    el.addEventListener('scroll', onScroll, { passive: true }) // 监听滚动条事件
   }
 
   // 组件挂载后调用

+ 93 - 0
src/packages/mobile/components/modules/article/index.less

@@ -0,0 +1,93 @@
+.app-article {
+    &__title {
+        font-size: 15px;
+        font-weight: normal;
+        margin-bottom: 12px;
+    }
+
+    &__section {
+        background-color: var(--block-bg-color);
+        border-radius: 6px;
+    }
+
+    .news-list {
+        line-height: 1;
+        padding: 12px;
+
+        &__date {
+            margin-bottom: 10px;
+        }
+
+        &__time {
+            font-size: 12px;
+            color: #777;
+        }
+
+        &__item {
+            position: relative;
+            display: flex;
+            background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 60%, transparent 60%);
+            background-size: 1px 6px; // 1px 宽(虚线粗细),6px 高(间隔周期)
+            background-repeat: repeat-y;
+            background-position: left center;
+            padding-left: 12px;
+
+            &:not(:last-child) {
+                padding-bottom: 28px;
+            }
+
+            &::before {
+                position: absolute;
+                left: -2px;
+                content: '';
+                width: 5px;
+                height: 5px;
+                line-height: 0;
+                background-color: #777;
+                transform: rotate(45deg);
+            }
+
+            &:last-child {
+                &::before {
+                    bottom: 0;
+                }
+            }
+        }
+
+        .news-item {
+            display: flex;
+
+            &__content {
+                flex: 1;
+                line-height: 1.6;
+            }
+
+            &__meta {
+                display: flex;
+                align-items: center;
+                font-size: 12px;
+                color: #777;
+                margin-top: 4px;
+            }
+
+            &__author {
+                display: flex;
+                align-items: center;
+
+                &::before {
+                    display: inline-block;
+                    content: '';
+                    width: 1px;
+                    height: 12px;
+                    background-color: #777;
+                    margin-left: 8px;
+                    margin-right: 8px;
+                }
+            }
+
+            &__image {
+                margin-left: 12px;
+            }
+        }
+    }
+}

+ 71 - 0
src/packages/mobile/components/modules/article/index.vue

@@ -0,0 +1,71 @@
+<template>
+    <app-block class="app-article">
+        <h4 class="app-article__title">{{ title }}</h4>
+        <section class="app-article__section" v-for="(group, index) in newsList" :key="index">
+            <dl class="news-list">
+                <dt class="news-list__date">
+                    <time class="news-list__time">{{ group.time }}</time>
+                </dt>
+                <dd class="news-list__item" v-for="(item, index) in group.list" :key="index">
+                    <div class="news-item">
+                        <div class="news-item__content">
+                            <div class="news-item__title">{{ item.title }}</div>
+                            <div class="news-item__meta">
+                                <span class="news-item__time">{{ formatDate(item.publishdate, 'HH:mm') }}</span>
+                                <span class="news-item__author" v-if="item.author">{{ item.author }}</span>
+                            </div>
+                        </div>
+                        <div class="news-item__image" v-if="item.coverimage">
+                            <Image class="news-item__img" fit="cover" :src="item.coverimage" :width="68" :height="60" :radius="4" />
+                        </div>
+                    </div>
+                </dd>
+            </dl>
+        </section>
+    </app-block>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import { Image } from 'vant'
+import { formatDate } from '@/filters'
+import { useRequest } from '@/hooks/request'
+import { queryNewTitles } from '@/services/api/news'
+
+defineProps({
+    title: {
+        type: String,
+        default: '新闻资讯'
+    }
+})
+
+const { dataList } = useRequest(queryNewTitles, {
+    defaultParams: {
+        pagesize: 20
+    }
+})
+
+const newsList = computed(() => {
+    const list = dataList.value.reduce<{ time: string; list: Model.NewTitlesRsp[] }[]>((res, item) => {
+        const date = item.publishdate.split('T')[0]
+        const group = res.find(group => group.time === date)
+
+        if (group) {
+            group.list.push(item)
+        } else {
+            res.push({
+                time: date,
+                list: [item]
+            })
+        }
+
+        return res
+    }, [])
+
+    return list.sort((a, b) => b.time.localeCompare(a.time))
+})
+</script>
+
+<style lang="less">
+@import "./index.less";
+</style>

+ 7 - 8
src/packages/mobile/views/account/authresult/Index.vue

@@ -1,5 +1,5 @@
 <template>
-    <app-view class="g-layout">
+    <app-view>
         <template #header>
             <app-navbar :title="t('user.authentication.title')" />
         </template>
@@ -30,10 +30,10 @@
                 <Cell :title="t('user.authentication.modifyremark')" :value="userStore.userAccount.modifyremark"
                     v-if="userStore.userAccount.hasauth === AuthStatus.Rejected && userStore.userAccount.modifyremark" />
             </CellGroup>
-            <div class="g-layout-block g-layout-block--inset"
+            <app-submitbar
                 v-if="userStore.userAccount.hasauth !== AuthStatus.Certified && ![2, 3, 4].includes(userStore.userAccount.modifystatus)">
                 <Button type="primary" round block @click="routerTo('account-certification')">重新认证</Button>
-            </div>
+            </app-submitbar>
         </template>
     </app-view>
 </template>
@@ -41,14 +41,13 @@
 <script lang="ts" setup>
 import { shallowRef, onActivated, computed } from 'vue'
 import { Button, CellGroup, Cell, Image, showLoadingToast } from 'vant'
+import { getFileUrl } from '@/filters'
 import { useNavigation } from '@mobile/router/navigation'
-import { AuthStatus, getAuthStatusName, getModifyStatusName } from '@/constants/account'
-import { queryWrDraftUserInfo } from '@/services/api/account'
+import { AuthStatus, getAuthStatusName, getModifyStatusName, getCertificateTypeCodeName } from '@/constants/account'
 import { useRequest } from '@/hooks/request'
-import { getCertificateTypeCodeName } from '@/constants/account'
-import { getFileUrl } from '@/filters'
-import { getWskhOpenAccountConfigs } from '@/services/api/account'
+import { queryWrDraftUserInfo, getWskhOpenAccountConfigs } from '@/services/api/account'
 import { i18n, useUserStore } from '@/stores'
+import AppSubmitbar from '@mobile/components/base/submitbar/index.vue'
 
 const { routerTo } = useNavigation()
 

+ 9 - 0
vue.config.js

@@ -59,6 +59,15 @@ module.exports = defineConfig({
           memoryLimit: 4096,
         },
       }))
+
+    config.plugin('define').tap((definitions) => {
+      Object.assign(definitions[0], {
+        __VUE_OPTIONS_API__: 'true',
+        __VUE_PROD_DEVTOOLS__: 'false',
+        __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
+      })
+      return definitions
+    })
   },
   configureWebpack: (config) => {
     const oem = process.env.VUE_APP_OEM

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.