10.10综合交易所原始源码_移动端
1
7 days ago 4f9044ae2a9f2db03bbb916bc5f6dfd12916361d
1
43 files modified
1 files added
6223 ■■■■ changed files
package-lock.json 230 ●●●●● patch | view | raw | blame | history
src/assets/image/login_logo.png patch | view | raw | blame | history
src/components/Transform/perpetual-open/amountSlider.vue 45 ●●●● patch | view | raw | blame | history
src/components/Transform/perpetual-open/index.vue 324 ●●●● patch | view | raw | blame | history
src/components/Transform/perpetual-position-list/index.vue 65 ●●●● patch | view | raw | blame | history
src/components/Transform/qur-nav/index.vue 20 ●●●● patch | view | raw | blame | history
src/components/fx-footer/index.vue 4 ●●●● patch | view | raw | blame | history
src/components/quotes-title/index.vue 8 ●●●●● patch | view | raw | blame | history
src/components/trade/india-stock-ex-nav/index.vue 8 ●●●● patch | view | raw | blame | history
src/config/index.js 10 ●●●● patch | view | raw | blame | history
src/i18n/modules/CN.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/Italy.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/Japanese.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/Korean.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/de.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/en.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/es.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/fr.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/gr.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/pt.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/ro.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/th.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/tur.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/vi.js 2 ●●●●● patch | view | raw | blame | history
src/i18n/modules/zh-CN.js 2 ●●●●● patch | view | raw | blame | history
src/main.js 2 ●●● patch | view | raw | blame | history
src/views/Record/DepositAndWithdrawal.vue 12 ●●●●● patch | view | raw | blame | history
src/views/cryptos/PerpetualContract/orderDetail.vue 56 ●●●●● patch | view | raw | blame | history
src/views/cryptos/Recharge/rechargePage.vue 4 ●●●● patch | view | raw | blame | history
src/views/foreign/Quotation.vue 6 ●●●●● patch | view | raw | blame | history
src/views/foreign/index.vue 6 ●●●●● patch | view | raw | blame | history
src/views/home/index.vue 129 ●●●●● patch | view | raw | blame | history
src/views/morePage/index.vue 58 ●●●●● patch | view | raw | blame | history
src/views/my/index.vue 1 ●●●● patch | view | raw | blame | history
src/views/optional/index.vue 10 ●●●● patch | view | raw | blame | history
src/views/quotes/List.vue 96 ●●●●● patch | view | raw | blame | history
src/views/quotes/Market.vue 106 ●●●●● patch | view | raw | blame | history
src/views/safety/index.vue 34 ●●●● patch | view | raw | blame | history
src/views/trade/Options.vue 94 ●●●●● patch | view | raw | blame | history
src/views/trade/components/OptionsContract.vue 6 ●●●●● patch | view | raw | blame | history
src/views/trade/index.vue 93 ●●●●● patch | view | raw | blame | history
vite.config.js 4 ●●●● patch | view | raw | blame | history
vite.config.js.timestamp-1775042120961.mjs 88 ●●●●● patch | view | raw | blame | history
yarn.lock 4674 ●●●● patch | view | raw | blame | history
package-lock.json
@@ -39,7 +39,6 @@
        "vuex-persistedstate": "^4.1.0"
      },
      "devDependencies": {
        "@vitalets/google-translate-api": "^9.2.1",
        "@vitejs/plugin-legacy": "^3.0.1",
        "@vitejs/plugin-vue": "^3.2.0",
        "autoprefixer": "^10.4.13",
@@ -743,13 +742,6 @@
      "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
      "dev": true
    },
    "node_modules/@types/http-errors": {
      "version": "1.8.2",
      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.2.tgz",
      "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@types/parse-json": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@@ -771,21 +763,6 @@
      "integrity": "sha512-Zxd7lDz/LliVYEQi3PR9a8CQa/kGCVzF0u9hqDMaTlgXlbG0wHMFPllrcG0ThR6bfs8xrYVuSFM9pJn6HSoUGQ==",
      "peerDependencies": {
        "vue": "^3.0.0"
      }
    },
    "node_modules/@vitalets/google-translate-api": {
      "version": "9.2.1",
      "resolved": "https://registry.npmjs.org/@vitalets/google-translate-api/-/google-translate-api-9.2.1.tgz",
      "integrity": "sha512-zlwQWSjXUZhbZQ6qwtIQ7GdYXFQmJ4wYqzcrYJUxtvzQQwUP+uKUb/SRJaBOQuBntjBjzcdcJoLFrpCKUbIkOg==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/http-errors": "^1.8.2",
        "http-errors": "^2.0.0",
        "node-fetch": "^2.6.7"
      },
      "engines": {
        "node": ">=14"
      }
    },
    "node_modules/@vitejs/plugin-legacy": {
@@ -1769,16 +1746,6 @@
      "version": "3.2.0",
      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
    },
    "node_modules/depd": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/detective": {
      "version": "5.2.1",
@@ -2946,27 +2913,6 @@
        "node": ">=8.0.0"
      }
    },
    "node_modules/http-errors": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "depd": "~2.0.0",
        "inherits": "~2.0.4",
        "setprototypeof": "~1.2.0",
        "statuses": "~2.0.2",
        "toidentifier": "~1.0.1"
      },
      "engines": {
        "node": ">= 0.8"
      },
      "funding": {
        "type": "opencollective",
        "url": "https://opencollective.com/express"
      }
    },
    "node_modules/ignore": {
      "version": "5.2.4",
      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -3527,27 +3473,6 @@
      "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
      "dependencies": {
        "lodash": "^4.17.21"
      }
    },
    "node_modules/node-fetch": {
      "version": "2.7.0",
      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "whatwg-url": "^5.0.0"
      },
      "engines": {
        "node": "4.x || >=6.0.0"
      },
      "peerDependencies": {
        "encoding": "^0.1.0"
      },
      "peerDependenciesMeta": {
        "encoding": {
          "optional": true
        }
      }
    },
    "node_modules/node-releases": {
@@ -4640,13 +4565,6 @@
      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
    },
    "node_modules/setprototypeof": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
      "dev": true,
      "license": "ISC"
    },
    "node_modules/shebang-command": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -4744,16 +4662,6 @@
      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
      "deprecated": "Please use @jridgewell/sourcemap-codec instead"
    },
    "node_modules/statuses": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/string-width": {
      "version": "4.2.3",
@@ -5055,28 +4963,11 @@
        "node": ">=8.0"
      }
    },
    "node_modules/toidentifier": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">=0.6"
      }
    },
    "node_modules/token-stream": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
      "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==",
      "dev": true
    },
    "node_modules/tr46": {
      "version": "0.0.3",
      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/tslib": {
      "version": "2.3.0",
@@ -5657,13 +5548,6 @@
        "vuex": "^3.0 || ^4.0.0-rc"
      }
    },
    "node_modules/webidl-conversions": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
      "dev": true,
      "license": "BSD-2-Clause"
    },
    "node_modules/webpack-sources": {
      "version": "3.2.3",
      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
@@ -5678,17 +5562,6 @@
      "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz",
      "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==",
      "dev": true
    },
    "node_modules/whatwg-url": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "tr46": "~0.0.3",
        "webidl-conversions": "^3.0.0"
      }
    },
    "node_modules/which": {
      "version": "2.0.2",
@@ -6420,12 +6293,6 @@
      "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
      "dev": true
    },
    "@types/http-errors": {
      "version": "1.8.2",
      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.2.tgz",
      "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==",
      "dev": true
    },
    "@types/parse-json": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@@ -6444,18 +6311,8 @@
    "@vant/use": {
      "version": "1.5.1",
      "resolved": "https://registry.npmjs.org/@vant/use/-/use-1.5.1.tgz",
      "integrity": "sha512-Zxd7lDz/LliVYEQi3PR9a8CQa/kGCVzF0u9hqDMaTlgXlbG0wHMFPllrcG0ThR6bfs8xrYVuSFM9pJn6HSoUGQ=="
    },
    "@vitalets/google-translate-api": {
      "version": "9.2.1",
      "resolved": "https://registry.npmjs.org/@vitalets/google-translate-api/-/google-translate-api-9.2.1.tgz",
      "integrity": "sha512-zlwQWSjXUZhbZQ6qwtIQ7GdYXFQmJ4wYqzcrYJUxtvzQQwUP+uKUb/SRJaBOQuBntjBjzcdcJoLFrpCKUbIkOg==",
      "dev": true,
      "requires": {
        "@types/http-errors": "^1.8.2",
        "http-errors": "^2.0.0",
        "node-fetch": "^2.6.7"
      }
      "integrity": "sha512-Zxd7lDz/LliVYEQi3PR9a8CQa/kGCVzF0u9hqDMaTlgXlbG0wHMFPllrcG0ThR6bfs8xrYVuSFM9pJn6HSoUGQ==",
      "requires": {}
    },
    "@vitejs/plugin-legacy": {
      "version": "3.0.2",
@@ -6474,7 +6331,8 @@
      "version": "3.2.0",
      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz",
      "integrity": "sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==",
      "dev": true
      "dev": true,
      "requires": {}
    },
    "@vue-macros/common": {
      "version": "1.3.3",
@@ -6725,7 +6583,8 @@
      "version": "5.3.2",
      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
      "dev": true
      "dev": true,
      "requires": {}
    },
    "acorn-node": {
      "version": "1.8.2",
@@ -7218,12 +7077,6 @@
      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
    },
    "depd": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
      "dev": true
    },
    "detective": {
      "version": "5.2.1",
      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz",
@@ -7609,7 +7462,8 @@
      "version": "8.8.0",
      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz",
      "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==",
      "dev": true
      "dev": true,
      "requires": {}
    },
    "eslint-plugin-prettier": {
      "version": "4.2.1",
@@ -7976,19 +7830,6 @@
      "requires": {
        "css-line-break": "^2.1.0",
        "text-segmentation": "^1.0.3"
      }
    },
    "http-errors": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
      "dev": true,
      "requires": {
        "depd": "~2.0.0",
        "inherits": "~2.0.4",
        "setprototypeof": "~1.2.0",
        "statuses": "~2.0.2",
        "toidentifier": "~1.0.1"
      }
    },
    "ignore": {
@@ -8416,15 +8257,6 @@
        "lodash": "^4.17.21"
      }
    },
    "node-fetch": {
      "version": "2.7.0",
      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
      "dev": true,
      "requires": {
        "whatwg-url": "^5.0.0"
      }
    },
    "node-releases": {
      "version": "2.0.12",
      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
@@ -8612,7 +8444,8 @@
    "pinia-plugin-persistedstate": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.1.0.tgz",
      "integrity": "sha512-8UN+vYMEPBdgNLwceY08mi5olI0wkYaEb8b6hD6xW7SnBRuPydWHlEhZvUWgNb/ibuf4PvufpvtS+dmhYjJQOw=="
      "integrity": "sha512-8UN+vYMEPBdgNLwceY08mi5olI0wkYaEb8b6hD6xW7SnBRuPydWHlEhZvUWgNb/ibuf4PvufpvtS+dmhYjJQOw==",
      "requires": {}
    },
    "pinyin-pro": {
      "version": "3.16.2",
@@ -9206,12 +9039,6 @@
      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
    },
    "setprototypeof": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
      "dev": true
    },
    "shebang-command": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -9293,12 +9120,6 @@
      "version": "1.4.8",
      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
    },
    "statuses": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
      "dev": true
    },
    "string-width": {
      "version": "4.2.3",
@@ -9522,22 +9343,10 @@
        "is-number": "^7.0.0"
      }
    },
    "toidentifier": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
      "dev": true
    },
    "token-stream": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
      "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==",
      "dev": true
    },
    "tr46": {
      "version": "0.0.3",
      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
      "dev": true
    },
    "tslib": {
@@ -9853,7 +9662,8 @@
    "vue-demi": {
      "version": "0.14.5",
      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.5.tgz",
      "integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA=="
      "integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==",
      "requires": {}
    },
    "vue-eslint-parser": {
      "version": "9.3.0",
@@ -9948,12 +9758,6 @@
        "shvl": "^2.0.3"
      }
    },
    "webidl-conversions": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
      "dev": true
    },
    "webpack-sources": {
      "version": "3.2.3",
      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
@@ -9965,16 +9769,6 @@
      "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz",
      "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==",
      "dev": true
    },
    "whatwg-url": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
      "dev": true,
      "requires": {
        "tr46": "~0.0.3",
        "webidl-conversions": "^3.0.0"
      }
    },
    "which": {
      "version": "2.0.2",
src/assets/image/login_logo.png

src/components/Transform/perpetual-open/amountSlider.vue
@@ -24,8 +24,8 @@
    </div>
</template>
<script>
import { mapGetters } from "vuex";
import VueSlider from "vue-slider-component";
import "vue-slider-component/theme/default.css";
export default {
    name: "amountSlider",
    components: {
@@ -34,20 +34,50 @@
    props: {
        maxAmount: {
            type: Number,
            default: 0, //可开或者可平的总数
            default: 0,
        },
        /** 与父级数量输入框同步,限价改价后 max 变化时滑块百分比随之更新 */
        amountValue: {
            type: [String, Number],
            default: "",
        },
    },
    data() {
        return {
            amount: undefined, //用户输入的张数
            sliderAmount: 0, //滑块的数量
            amount: undefined,
            sliderAmount: 0,
            marks: (val) => val % 25 === 0,
        };
    },
    computed: {
        ...mapGetters(["existToken"]),
    watch: {
        maxAmount() {
            this.$nextTick(() => this.syncSliderFromAmount())
        },
        amountValue() {
            this.$nextTick(() => this.syncSliderFromAmount())
        },
    },
    mounted() {
        this.syncSliderFromAmount()
    },
    methods: {
        syncSliderFromAmount() {
            const mx = Number(this.maxAmount)
            const amt = parseFloat(this.amountValue)
            if (!isFinite(mx) || mx <= 0) {
                this.sliderAmount = 0
                return
            }
            if (!isFinite(amt) || amt <= 0) {
                this.sliderAmount = 0
                return
            }
            const pct = Math.min(100, Math.max(0, (amt / mx) * 100))
            const next = Math.round(pct * 100) / 100
            if (Number(this.sliderAmount) !== next) {
                this.sliderAmount = next
            }
        },
        //输入框事件
        inputChange(val) {
            this.$emit("getAmount", val);
@@ -58,7 +88,6 @@
        },
        //滑块事件
        sliderAmountChange(val) {
            console.log("滑块的值", val, this.maxAmount);
            let data;
            if (this.maxAmount) {
                if (val == 0) {
@@ -66,7 +95,7 @@
                } else {
                    const rate = val / 100; //如0.25
                    data = this.maxAmount * rate;
                    this.amount = parseInt(data);
                    this.amount = Math.floor(data * 10000) / 10000;
                }
                this.$emit("getAmount", this.amount);
            }
src/components/Transform/perpetual-open/index.vue
@@ -11,7 +11,7 @@
                                        class="w-5 h-3" />
            <div v-if="showOptions" class="options tabBackground w-36 absolute top-24 left-0 z-10">
              <div class="w-full" @click.stop="handleChoose(item)"
                   :class="{ 'option-active': form.lever_rate === item.lever_rate }" :value="item.lever_rate"
                   :class="{ 'option-active': Number(form.lever_rate) === Number(item.lever_rate) }" :value="item.lever_rate"
                   v-for="item in initData.lever" :key="item.id">
                {{ item.lever_rate }}x
              </div>
@@ -53,7 +53,8 @@
            <div class="h-18 greyBg mb-8 flex pr-5 justify-center rounded-lg textColor items-center"
              v-if="selectIndex == 1">
              <input placeholder="" class="greyBg w-full pl-5 h-16 border-none text-left rounded-lg"
                :disabled="type / 1 === 1" @focus="focus = true" v-model="form.price" />
                :disabled="type / 1 === 1" @focus="focus = true" v-model="form.price"
                @input="onLimitPriceInput" />
              <span class="ml-5">{{ getCurrentUnit() }}</span>
            </div>
@@ -63,9 +64,9 @@
                @click="onReduce">
                <img src="../../../assets/image/public/reduce.png" alt="add" class="w-8 h-2" />
              </div>
              <input v-if="selectIndex == 1" :placeholder="$t('张数')" class="border-none greyBg text-center textColor"
              <input v-if="selectIndex == 1" :placeholder="symbolUpper" class="border-none greyBg text-center textColor"
                :class="['HK-stocks', 'JP-stocks','UK-stocks','DE-stocks','BZ-stocks'].includes(queryType) ? 'full-input' : 'half-input'"
                v-model="form.amount" type="number" @input="onInput" />
                v-model="form.amount" type="number" @input="onInput" @blur="onAmountBlur" />
              <input v-if="selectIndex == 2 && Array.isArray(initFutrue.para) && initFutrue.para.length"
                :placeholder="($t('最少') + (initFutrue && Array.isArray(initFutrue.para) && initFutrue.para[paraIndex] ? $t('最小金额') + initFutrue.para[paraIndex].buy_min : ''))"
                class="border-none greyBg text-center textColor"
@@ -82,49 +83,32 @@
            </div>
            <template v-if="selectIndex == 1">
              <div class="mt-10 mb-8 w-full flex justify-between items-center">
                <span class="text-22 text-grey">{{ $t("可开张数") }}</span>
                <span class="text-22 text-grey">{{ $t("最大数量") }} ({{ symbolUpper }})</span>
                <span class="text-22 textColor">
                  {{ initData.volume }}
                  {{ $t("张") }}
                  {{ maxOpenLotsFormatted }}
                  {{ symbolUpper }}
                </span>
              </div>
              <!-- <vue-slider v-bind="options" v-model="form.amount"></vue-slider> -->
              <!-- <vue-slider class="mainBox" v-bind="options" :marks="marks" v-model="form.volume" :hide-label="true"  width="90%"
              :railStyle="{ background: '#404040', height: '4px' }"
              :processStyle="{ background: '#266BFF', height: '4px' }">
              <template v-slot:step="{ active }">
                <div :class="['custom-step', { active }]"></div>
              </template>
            </vue-slider>
            <div style="color: #868D9A" class="mt-36 text-24 w-full flex justify-between items-center">
              <span class="flex-1 text-left">0%</span>
              <span class="flex-1 text-left">25%</span>
              <span class="flex-1 text-center">50%</span>
              <span class="flex-1 text-right">75%</span>
              <span class="flex-1 text-right">100%</span>
            </div> -->
              <!-- 张数输入 -->
              <amount-slider ref="sliderRef" :maxAmount="getVolumnByLever()" @getAmount="getAmount"></amount-slider>
              <!-- 仅保留一个数量滑块;上限随市价/限价委托价对应的 maxOpenLots 变化 -->
              <amount-slider ref="sliderRef" :maxAmount="maxOpenLots" :amountValue="form.amount"
                @getAmount="getAmount"></amount-slider>
            </template>
            <template v-if="selectIndex == 1 && userInfo && userInfo.token">
              <div class="flex justify-between mt-8">
                <div class="text-grey">{{ $t('合约金额') }}</div>
                <div class="textColor">{{ initData.amount * (form.amount / 1) * form.lever_rate }}
                <div class="textColor">{{ formatNum4(perpNotionalUsdt) }}
                  <span>{{  getCurrentUnit() }}</span>
                </div>
              </div>
              <div class="flex justify-between mt-8">
                <div class="text-grey">{{ $t("保证金") }}</div>
                <div class="textColor">
                  {{ (initData.amount * (form.amount / 1)) }} {{ getCurrentUnit() }}
                  {{ formatNum4(perpMarginUsdt) }} {{ getCurrentUnit() }}
                </div>
              </div>
              <div class="flex justify-between mt-8">
                <div class="text-grey">{{ $t("建仓手续费") }}</div>
                <div class="textColor">{{
                  userInfo.perpetual_contracts_status === '1' ? initData.fee *
                (form.amount / 1) : initData.fee * (form.amount / 1) * form.lever_rate
                }} {{ getCurrentUnit() }}
                <div class="textColor">{{ formatNum4(perpOpenFeeUsdt) }} {{ getCurrentUnit() }}
                </div>
              </div>
            </template>
@@ -316,16 +300,13 @@
<script>
import config from "@/config";
import { Popup, showToast } from 'vant';
import { closeDialog, Popup, showToast } from 'vant';
import { mapGetters } from 'vuex'
import VueSlider from 'vue-slider-component/dist-css/vue-slider-component.umd.js'
import 'vue-slider-component/theme/default.css'
import { _orderOpen, _futrueOrder, _futrueOrderDetail, _getBalance, _futrueOrderInit } from '@/service/trade.api'
import ContractFutrue from '@/components/Transform/contract-futrue/index.vue'
import PopupDelivery from "@/components/Transform/popup-delivery/index.vue";
import { fixDate, throttle } from "@/utils/index.js";
import AmountSlider from "./amountSlider.vue";
import "vue-slider-component/theme/default.css";
import { _getHomeList } from "@/service/home.api";
import { themeStore } from '@/store/theme';
import { currentUnit } from '@/utils/coinUnit.js'
@@ -336,7 +317,6 @@
export default {
  name: "perpetualPosition",
  components: {
    VueSlider,
    ContractFutrue,
    PopupDelivery,
    AmountSlider,
@@ -403,7 +383,24 @@
      if (JSON.stringify(this.initFutrue.para) != '[]') {
        this.form.para_id = this.initFutrue.para && this.initFutrue.para[this.paraIndex].para_id // 不优雅,不可靠
      }
      if (this.selectIndex / 1 === 1) {
        this.handleInitSliderOption(true)
      }
    },
    'form.price'() {
      if (this.selectIndex / 1 === 1) {
        this.handleInitSliderOption(true)
      }
    },
    'form.lever_rate'() {
      if (this.selectIndex / 1 === 1) {
        this.handleInitSliderOption(true)
      }
    },
    type() {
      if (this.selectIndex / 1 === 1) {
        this.handleInitSliderOption(true)
      }
    },
    initOpen: { // 处理滚动条初始值
      deep: true,
@@ -495,8 +492,164 @@
      }
      return this.initFutrue
    },
    coudBuyVolume() { // 可买数量
      return Math.floor((this.initOpen.volume / 1) / this.form.lever_rate)
    /**
     * 永续下单可用 USDT:与行情页「账户余额」一致,优先 userInfo.balance(钱包 USDT);
     * openview 返回的 volume 在部分接口里并非可用保证金,仅用 initOpen.volume 会导致可开数量极小。
     */
    contractAvailableUsdt() {
      const fromUser = parseFloat(this.userInfo && this.userInfo.balance)
      if (fromUser > 0 && isFinite(fromUser)) return fromUser
      const fromOpen = parseFloat(this.initOpen.volume)
      if (fromOpen > 0 && isFinite(fromOpen)) return fromOpen
      return 0
    },
    /**
     * 最大可开数量(币本位):
     * - 市价:按最新成交价/行情价折算;
     * - 限价:按输入委托价折算。
     * 公式:maxQty = balance * lever / (price * (1 + feeRate))
     * 其中 feeRate 来自开仓初始化接口(initOpen.fee),不再写死 0.01。
     */
    maxOpenLots() {
      if (this.selectIndex / 1 !== 1) {
        return Math.max(0, Number(this.initOpen.volume) || 0)
      }
      if (!this.userInfo || !this.userInfo.token) return 0
      const balance = this.contractAvailableUsdt
      if (!(balance > 0) || !isFinite(balance)) return 0
      const lever = Math.max(1, Number(this.form.lever_rate) || 1)
      if (!(lever > 0) || !isFinite(lever)) return 0
      const markPrice = parseFloat(this.price)
      const limitP = parseFloat(this.form.price)
      const isLimit = String(this.type) === '2' || Number(this.type) === 2
      let priceForMax
      if (isLimit) {
        if (limitP > 0 && isFinite(limitP)) {
          priceForMax = limitP
        } else {
          return 0
        }
      } else if (markPrice > 0 && isFinite(markPrice)) {
        priceForMax = markPrice
      } else if (limitP > 0 && isFinite(limitP)) {
        priceForMax = limitP
      } else {
        return 0
      }
      // 与 maxOpenableBaseQuantity 对齐:
      // costPerUnit = marginPerUnit + feePerUnit
      // marginPerUnit = price / L
      const marginPerUnit = priceForMax / lever
      const feeNum = Number(this.initOpen && this.initOpen.fee)
      let feePerUnit = 0
      if (Number.isFinite(feeNum) && feeNum > 0) {
        // 小于 0.05 按费率(名义 × 费率 × 杠杆);否则按每单位绝对费用 × 杠杆
        feePerUnit = feeNum < 0.05 ? priceForMax * feeNum * lever : feeNum * lever
      }
      const costPerUnit = marginPerUnit + feePerUnit
      if (!Number.isFinite(costPerUnit) || costPerUnit <= 0) return 0
      let maxQty = balance / costPerUnit
      const serverVol = Number(this.initOpen && this.initOpen.volume)
      // fee 为小数费率时,沿用快捷式与旧逻辑一致
      if (
        Number.isFinite(serverVol) &&
        serverVol > 0 &&
        Number.isFinite(priceForMax) &&
        priceForMax > 0 &&
        Number.isFinite(feeNum) &&
        feeNum > 0 &&
        feeNum < 0.05
      ) {
        maxQty = ((balance * lever) / (1 + feeNum * lever)) / priceForMax
      }
      if (!Number.isFinite(maxQty) || maxQty <= 0) return 0
      if (maxQty <= 1e-10) return 0
      const minLot = Number(
        this.initOpen && (
          this.initOpen.buy_min ??
          this.initOpen.min_qty ??
          this.initOpen.min_volume ??
          this.initOpen.min
        )
      )
      if (Number.isFinite(minLot) && minLot > 0 && maxQty < minLot) return 0
      // 保留计算精度,不做四舍五入、不做截断
      return maxQty
    },
    /** 最大可开数量展示为四位小数 */
    maxOpenLotsFormatted() {
      const n = this.maxOpenLots
      if (!(n >= 0) || !isFinite(n)) return '0'
      // 展示原始精度(不四舍五入、不截断)
      return String(n)
    },
    coudBuyVolume() {
      return this.maxOpenLots
    },
    /** 当前交易品种大写,作数量单位展示 */
    symbolUpper() {
      const s = this.symbol || (this.$route && this.$route.params && this.$route.params.symbol) || ''
      return String(s).trim().toUpperCase() || '--'
    },
    /** 永续展示用有效价:限价用委托价;市价用行情价(与 maxOpenLots 口径一致) */
    perpCalcPrice() {
      if (this.selectIndex / 1 !== 1) return 0
      const isLimit = String(this.type) === '2' || Number(this.type) === 2
      const fp = parseFloat(this.form.price)
      const mp = parseFloat(this.price)
      if (isLimit) {
        if (fp > 0 && isFinite(fp)) return fp
        return (mp > 0 && isFinite(mp)) ? mp : 0
      }
      if (mp > 0 && isFinite(mp)) return mp
      return (fp > 0 && isFinite(fp)) ? fp : 0
    },
    perpCalcQty() {
      const q = parseFloat(this.form.amount)
      return (q > 0 && isFinite(q)) ? q : 0
    },
    perpCalcLever() {
      const L = parseFloat(this.form.lever_rate)
      return (L > 0 && isFinite(L)) ? L : 1
    },
    /** 扣除建仓手续费后的有效数量(按当前价格折算) */
    perpNetQtyAfterFee() {
      const qty = this.perpCalcQty
      const p = this.perpCalcPrice
      if (!(qty > 0) || !(p > 0) || !isFinite(qty) || !isFinite(p)) return 0
      const feeInQty = this.perpOpenFeeUsdt / p
      const netQty = qty - feeInQty
      return netQty > 0 && isFinite(netQty) ? netQty : 0
    },
    /** 合约名义价值 ≈ 价格 × 扣费后数量(USDT) */
    perpNotionalUsdt() {
      return this.perpCalcPrice * this.perpNetQtyAfterFee
    },
    /** 保证金 ≈ 名义价值 ÷ 杠杆,切换杠杆会立刻变 */
    perpMarginUsdt() {
      const L = this.perpCalcLever
      return L > 0 ? (this.perpNotionalUsdt / L) : 0
    },
    /** 建仓手续费:与接口约定一致,依赖数量与杠杆 */
    perpOpenFeeUsdt() {
      const qty = this.perpCalcQty
      const fee = parseFloat(this.initOpen.fee) || 0
      const lever = this.perpCalcLever
      const price = this.perpCalcPrice
      if (!(qty > 0) || !isFinite(qty)) return 0
      if (String(this.userInfo.perpetual_contracts_status) === '1') {
        return fee * qty
      }
      return fee * qty * price / lever
    },
  },
  data() {
@@ -572,6 +725,13 @@
    this.clearTimeout()
  },
  methods: {
    /** 限价委托价变化:刷新滑块上限与 options.max(maxOpenLots 为计算属性会随 form.price 自动变) */
    onLimitPriceInput() {
      this.$nextTick(() => {
        if (this.selectIndex / 1 !== 1) return
        this.handleInitSliderOption(true)
      })
    },
    //获取张数
    getAmount(val) {
      if (!val) {
@@ -582,6 +742,13 @@
    getCurrentUnit() {
      return currentUnit(this.queryType)
    },
    formatNum4(n) {
      const v = parseFloat(n)
      if (!isFinite(v)) return '0.0000'
      const factor = 10000
      const truncated = Math.trunc(v * factor) / factor
      return truncated.toFixed(4)
    },
    onSelect(item) {
      this.actions.map((item) => {
        item.className = ''
@@ -590,11 +757,8 @@
      this.showType = item.value;
      this.$emit("changeValueBack", this.showType);
    },
    // 获取张数,数据转换
    getVolumnByLever() {
      let vol;
      vol = this.initOpen.volume / 1;
      return Math.floor(vol);
      return this.maxOpenLots
    },
    selectItemArry(item) {
      this.dataArrValue = item.value;
@@ -705,38 +869,52 @@
    },
    handleInitSliderOption(amount) {
      if (!amount) {
        // 金额是否需要变动
        this.form.amount = "";
      }
      console.log(this.initOpen.volume, this.form.lever_rate);
      let vol;
      vol = this.initOpen.volume / 1;
      this.options.max = Math.floor(vol);
      console.log("this.options.max", this.options.max);
      if (this.options.max > 0) {
        this.options.disabled = false;
      let vol
      if (this.selectIndex / 1 === 1) {
        vol = this.maxOpenLots
      } else {
        this.options.disabled = true;
        vol = Math.floor(Number(this.initOpen.volume) || 0)
      }
      this.options.max = vol
      if (this.options.max > 0) {
        this.options.disabled = false
      } else {
        this.options.disabled = true
      }
      if (this.selectIndex / 1 === 1) {
        const amt = parseFloat(this.form.amount)
        const mx = parseFloat(this.options.max)
        if (isFinite(amt) && isFinite(mx) && amt > mx + 1e-10) {
          this.form.amount = mx
        }
      }
    },
    handleChoose(item) {
      this.showOptions = !this.showOptions
      this.form.lever_rate = item.lever_rate
      console.log('handleChoose')
      this.handleInitSliderOption()
      // 选中后关闭下拉;杠杆统一为数字,避免 "100" 与 100 比较不一致
      this.showOptions = false
      this.form.lever_rate = Number(item.lever_rate)
      this.getAmount()
      // 勿调用 handleInitSliderOption() 无参:会清空 form.amount,导致合约金额/与价格相关展示错乱
      // 杠杆变化由 watch form.lever_rate 触发 handleInitSliderOption(true),保留已填数量并刷新上限
    },
    onAdd() { // +
      if (this.options.max === 0) {
        return
      }
      if (this.form.amount === this.options.max) {
        return;
      }
      this.form.amount++
      const cur = parseFloat(this.form.amount) || 0
      const mx = parseFloat(this.options.max) || 0
      if (cur >= mx - 1e-8) return
      const next = Math.min(Math.floor((cur + 1) * 10000) / 10000, mx)
      this.form.amount = next
    },
    onReduce() { // -
      if (this.form.amount > 1) {
        this.form.amount--
      const cur = parseFloat(this.form.amount) || 0
      if (cur > 1) {
        this.form.amount = Math.floor((cur - 1) * 10000) / 10000
      } else if (cur > 0) {
        this.form.amount = 0
      }
    },
    onParaId(evt) { // 交割日期
@@ -749,9 +927,27 @@
      // console.log(1111111, this.form.amount, this.selectIndex)
      if (this.selectIndex == 1 && this.options.max == 0) {
        this.form.amount = this.form.amount / 1
      } else if (this.selectIndex == 1 && this.form.amount / 1 > this.options.max / 1) {
        this.form.amount = this.options.max / 1
      } else if (this.selectIndex == 1) {
        const amt = parseFloat(this.form.amount)
        const mx = parseFloat(this.options.max)
        if (isFinite(amt) && isFinite(mx) && amt > mx) {
          this.form.amount = mx
        }
      }
    },
    /** 数量展示:截断四位小数(市价/限价均按当前 perpCalcPrice 算下方合约金额) */
    onAmountBlur() {
      if (this.selectIndex / 1 !== 1) return
      const raw = this.form.amount
      if (raw === '' || raw === null || raw === undefined) return
      const v = parseFloat(raw)
      if (!isFinite(v)) {
        this.form.amount = ''
        return
      }
      const factor = 10000
      const truncated = Math.trunc(v * factor) / factor
      this.form.amount = truncated === 0 ? '' : truncated
    },
    //价格类型下拉框切换
    selectBtn() {
@@ -809,7 +1005,7 @@
      }
      if (!this.form.amount) {
        if (this.selectIndex == 1) {
          showToast(this.$t('请输入合约张数'))
          showToast(`${this.$t('请输入').trim()} ${this.symbolUpper}`)
        } else {
          showToast(this.$t('请输入金额'))
        }
src/components/Transform/perpetual-position-list/index.vue
@@ -1,14 +1,9 @@
<template>
  <!-- 持有仓位列表 -->
  <div id="cryptos">
    <!--        <div class="flex justify-between" v-if="listData.length">-->
    <!--            <div class="flex" @click.stop="changeIcon">-->
    <!--                <img v-show="!iconShow" src="../../assets/image/public/grey-rounded.png" alt="" class="w-38 h-38"/>-->
    <!--                <img v-show="iconShow" src="../../assets/image/public/blue-rounded.png" alt="" class="w-38 h-38"/>-->
    <!--                <div class="ml-37xw">{{ $t('隐藏其它合约') }}</div>-->
    <!--            </div>-->
    <!--            <button class="border-none all-cancel-btn pl-34 pr-34 pt-10 pb-10 text-26" @click="onSellAll">{{ $t('一键平仓') }}</button>-->
    <!--        </div>-->
    <div class="close-all-row" v-if="listData.length">
      <button type="button" class="close-all-btn" @click="onSellAll">{{ $t('一键平仓') }}</button>
    </div>
    <div class="border-b-color" v-for="item in listData" :key="item.order_no">
      <div class="flex justify-between pt-11 pb-11">
        <div class="flex flex-col">
@@ -43,22 +38,27 @@
      <div class="flex pt-11 pb-8 text-28">
        <div class="flex-1">
          <div class="text-grey">{{ $t('持仓数量') }}</div>
          <div class="mt-5 textColor">{{ item.volume / (item.lever_rate ? item.lever_rate : 1) }}*{{
            item.lever_rate ? item.lever_rate : 1 }}x</div>
          <div class="mt-5 textColor">{{ formatNum4(item.volume) }}
            <!-- *{{item.lever_rate ? item.lever_rate : 1 }}x -->
          </div>
        </div>
        <div class="flex-1 text-center  text-28">
          <div class="text-grey">{{ $t('保证金') }} ( {{ routeType == 'cryptos' ? 'USDT' : 'USD' }})</div>
          <div class="mt-5 textColor">{{ item.deposit }}</div>
          <div class="mt-5 textColor">{{ formatNum4(item.deposit) }}</div>
        </div>
        <div class="flex-1 flex flex-col items-end  text-28">
          <div class="text-grey">{{ $t('开仓价格') }}</div>
          <div class="mt-5 textColor">{{ item.trade_avg_price }}</div>
          <div class="mt-5 textColor">{{ formatNum4(item.trade_avg_price) }}</div>
        </div>
      </div>
      <div class="flex pb-8 text-28">
        <div class="flex-1">
          <div class="text-grey">{{ $t('标记价格') }}</div>
          <div class="mt-5 textColor">{{ item.mark_price }}</div>
        </div>
        <div class="flex-1">
          <div class="text-grey">{{ $t('强平价') }}</div>
          <div class="mt-5 textColor">{{ item.force_close_price }}</div>
        </div>
        <div class="flex-1 flex flex-col items-center justify-end">
          <button class="text-30 detail-btn border-light-blue greyBg colorMain w-32 h-16" @click="goDetail(item)">{{
@@ -82,7 +82,6 @@
  name: "perpetualPositionList",
  data() {
    return {
      iconShow: false,
      routeType: 'cryptos'
    }
  },
@@ -102,8 +101,13 @@
    this.routeType = this.$route.query.type
  },
  methods: {
    changeIcon() {
      this.iconShow = !this.iconShow;
    /** 截断保留四位小数(不舍入) */
    formatNum4(val) {
      const v = parseFloat(val)
      if (!isFinite(v)) return '0.0000'
      const factor = 10000
      const truncated = Math.trunc(v * factor) / factor
      return truncated.toFixed(4)
    },
    goDetail(item) {
      this.$router.push({ path: "/cryptos/orderDetail", query: { order_no: item.order_no } });
@@ -123,7 +127,7 @@
    },
    onSellAll() { // 平仓全部
      _orderSellBatch().then(() => {
        this.showToast(this.$t('平仓成功'))
        showToast(this.$t('平仓成功'))
        this.$emit('sell')
      })
    }
@@ -142,5 +146,34 @@
    background: $US_tabActice_background;
    color: $color_main;
  }
  .close-all-row {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 0.8rem 0 1.2rem;
  }
  .close-all-btn {
    border: none;
    outline: none;
    cursor: pointer;
    font-size: 1.3rem;
    font-weight: 600;
    line-height: 1.2;
    padding: 0.65rem 1.6rem;
    border-radius: 0.5rem;
    /* 与全局主题一致:--btn_main / --quotes-btn-text */
    background: var(--btn_main);
    color: var(--quotes-btn-text);
    box-shadow:
      0 0.35rem 1rem rgba(0, 0, 0, 0.14),
      inset 0 1px 0 rgba(255, 255, 255, 0.35);
    &:active {
      opacity: 0.92;
      transform: scale(0.98);
    }
  }
}
</style>
src/components/Transform/qur-nav/index.vue
@@ -47,12 +47,7 @@
        {
          name: this.$t('合约'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/indexNav/3271.png`, import.meta.url),
          path: '/cryptos/perpetualContract/AAPL?type=US-stocks&selectIndex=1'
        },
        {
          name: this.$t('理财'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/indexNav/3262.png`, import.meta.url),
          path: '/cryptos/fund'
          path: '/trade/options?symbol=btc&activeTab=cryptos'
        },
        {
          name: this.$t('客服'),
@@ -62,20 +57,9 @@
      ],
      navList1: [
        {
          name: this.$t('股票'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/indexNav/3258.png`, import.meta.url),
          path: '/quotes/openTrade?tabActive=0&symbol=AAPL&type=US-stocks',
          isLogin: false
        },
        {
          name: this.$t('质押'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/indexNav/3257.png`, import.meta.url),
          path: '/cryptos/pledgeLoan'
        },
        {
          name: this.$t('账变记录'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/indexNav/3268.png`, import.meta.url),
          path: '/cryptos/accountChange?type=US-stocks'
          path: '/cryptos/accountChange?type=cryptos'
        },
        {
          name: this.$t('分享'),
src/components/fx-footer/index.vue
@@ -19,11 +19,11 @@
        </template>
      </van-tabbar-item>
      <van-tabbar-item name="fund" to="/cryptos/fund">
      <!-- <van-tabbar-item name="fund" to="/cryptos/fund">
        <template #icon>
          <img :src="active == 'fund' ? icon.fund.active : icon.fund.inactive" alt="fund" />
        </template>
      </van-tabbar-item>
      </van-tabbar-item> -->
      <van-tabbar-item name="personal" to="/personal">
        <template #icon>
src/components/quotes-title/index.vue
@@ -35,16 +35,8 @@
const router = useRouter()
const tabList = ref([
  {
    name: t("外汇"),
    value: "forex",
    id: "",
  }, {
    name: t("大宗商品"),
    value: "commodities",
    id: "",
  }, {
    name: t("指数"),
    value: "indices",
    id: "",
  }, {
    name: t("加密货币"),
src/components/trade/india-stock-ex-nav/index.vue
@@ -58,12 +58,6 @@
      ],
      navList1: [
        {
          name: this.$t('助力贷'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/nav/asset.png`, import.meta.url),
          path: '/cryptos/loan',
          isLogin: true
        },
        {
          name: this.$t('新股认购'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/etfNav/icon5-5.png`, import.meta.url),
          path: '/ipo/index?tabActive=0&stock=US-stocks&stockActive=3'
@@ -71,7 +65,7 @@
        {
          name: this.$t('期货交易'),
          icon: new URL(`../../../assets/theme/${thStore.theme}/image/etfNav/icon3-3.png`, import.meta.url),
          path: '/cryptos/perpetualContract/AAPL?type=US-stocks&selectIndex=2'
          path: '/cryptos/perpetualContract/btc?type=cryptos&selectIndex=2'
        },
        {
          name: this.$t('客服'),
src/config/index.js
@@ -42,13 +42,13 @@
//const ENV_DEV = '9d9d8.com' // 填写前端域名网址
// const ENV_DEV = '127.0.0.1' // dev
// const ENV_DEV = '127.0.0.1:8086' // dev
const ENV_DEV = 'api.eledrink.com' // dev
// const ENV_DEV = '192.168.10.11:8848' // dev
const ENV_DEV = 'api.dpcex.com' // dev
// const ENV_PRO = window.location.hostname // 接口域名跟随 H5
// const ENV_PRO = "127.0.0.1"
// const ENV_PRO = "127.0.0.1:8086"
const ENV_PRO = "api.eledrink.com"
// const ENV_PRO = "192.168.10.11:8848"
const ENV_PRO = "api.dpcex.com"
// 避免打包出错务必把 app域名的注释要放在在本地ENV_PRO的下面
// const ENV_PRO = 'foilwm.com' //  app域名
@@ -66,7 +66,7 @@
}
export const BASE_URL = base_url
export const WS_URL = ws_url
export const IMG_PATH = 'https://img.eledrink.com'
export const IMG_PATH = 'https://img.dpcex.com'
export const HOST_URL = host_url
export default {
src/i18n/modules/CN.js
@@ -2961,4 +2961,6 @@
    "home.heroFeatures1": "強大平台,超低點差,快速執行,專屬支持",
    "home.heroTitle2": "頂尖交易技術",
    "home.heroDesc2": "通過我們的先進交易系統,您可以精準把握市場動態,高效執行每一筆交易!",
    "资金费": "資金費",
    "距下次结算": "距下次結算(UTC 每4小時)",
}
src/i18n/modules/Italy.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Piattaforma potente, spread ultra-bassi, esecuzione rapida, supporto esclusivo",
    "home.heroTitle2": "Tecnologia di trading all'avanguardia",
    "home.heroDesc2": "Attraverso il nostro sistema di trading avanzato, puoi cogliere con precisione le dinamiche di mercato ed eseguire ogni transazione in modo efficiente!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/Japanese.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "強力なプラットフォーム、超低スプレッド、高速実行、専属サポート",
    "home.heroTitle2": "最先端の取引技術",
    "home.heroDesc2": "当社の高度な取引システムを通じて、市場の動きを正確に把握し、すべての取引を効率的に実行できます!",
    "资金费": "Funding Fee",
    "距下次结算": "次回決済(UTC・4時間)",
}
src/i18n/modules/Korean.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "강력한 플랫폼, 초저 스프레드, 빠른 실행, 독점 지원",
    "home.heroTitle2": "최첨단 거래 기술",
    "home.heroDesc2": "당사의 고급 거래 시스템을 통해 시장 역학을 정확하게 파악하고 모든 거래를 효율적으로 실행할 수 있습니다!",
    "资金费": "Funding Fee",
    "距下次结算": "다음 정산 (UTC / 4시간)",
}
src/i18n/modules/de.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Leistungsstarke Plattform, ultra-niedrige Spreads, schnelle Ausführung, exklusiver Support",
    "home.heroTitle2": "Hochmoderne Handelstechnologie",
    "home.heroDesc2": "Durch unser fortschrittliches Handelssystem können Sie Marktdynamiken genau erfassen und jede Transaktion effizient ausführen!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/en.js
@@ -3012,4 +3012,6 @@
    'home.heroFeatures1': "Powerful platform, ultra-low spreads, fast execution, exclusive support",
    'home.heroTitle2': "Cutting-edge Trading Technology",
    'home.heroDesc2': "Through our advanced trading system, you can accurately grasp market dynamics and efficiently execute every transaction!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/es.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Plataforma potente, spreads ultra bajos, ejecución rápida, soporte exclusivo",
    "home.heroTitle2": "Tecnología de trading de vanguardia",
    "home.heroDesc2": "¡A través de nuestro sistema de trading avanzado, puede captar con precisión la dinámica del mercado y ejecutar cada transacción de manera eficiente!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/fr.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Plateforme puissante, spreads ultra-faibles, exécution rapide, support exclusif",
    "home.heroTitle2": "Technologie de trading de pointe",
    "home.heroDesc2": "Grâce à notre système de trading avancé, vous pouvez saisir avec précision la dynamique du marché et exécuter chaque transaction efficacement!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/gr.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Ισχυρή πλατφόρμα, εξαιρετικά χαμηλά spreads, γρήγορη εκτέλεση, αποκλειστική υποστήριξη",
    "home.heroTitle2": "Σύγχρονη τεχνολογία συναλλαγών",
    "home.heroDesc2": "Μέσω του προηγμένου συστήματος συναλλαγών μας, μπορείτε να κατανοήσετε με ακρίβεια τη δυναμική της αγοράς και να εκτελέσετε αποτελεσματικά κάθε συναλλαγή!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/pt.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Plataforma poderosa, spreads ultra baixos, execução rápida, suporte exclusivo",
    "home.heroTitle2": "Tecnologia de negociação de ponta",
    "home.heroDesc2": "Através do nosso sistema de negociação avançado, você pode captar com precisão a dinâmica do mercado e executar cada transação com eficiência!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/ro.js
@@ -2953,4 +2953,6 @@
    "home.heroFeatures1": "Platformă puternică, spreaduri ultra scăzute, execuție rapidă, suport exclusiv",
    "home.heroTitle2": "Tehnologie de tranzacționare de ultimă generație",
    "home.heroDesc2": "Prin sistemul nostru avansat de tranzacționare, puteți înțelege cu precizie dinamica pieței și executa eficient fiecare tranzacție!",
    "资金费": "Funding Fee",
    "距下次结算": "Next settlement (UTC / 4h)",
}
src/i18n/modules/th.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "แพลตฟอร์มที่ทรงพลัง สเปรดต่ำมาก การดำเนินการที่รวดเร็ว การสนับสนุนพิเศษ",
    "home.heroTitle2": "เทคโนโลยีการซื้อขายที่ล้ำสมัย",
    "home.heroDesc2": "ผ่านระบบการซื้อขายขั้นสูงของเรา คุณสามารถเข้าใจพลวัตของตลาดได้อย่างแม่นยำและดำเนินการทุกธุรกรรมอย่างมีประสิทธิภาพ!",
    "资金费": "Funding Fee",
    "距下次结算": "รอบชำระถัดไป (UTC / 4 ชม.)",
}
src/i18n/modules/tur.js
@@ -2953,4 +2953,6 @@
    "home.heroFeatures1": "Güçlü platform, ultra düşük spreadler, hızlı yürütme, özel destek",
    "home.heroTitle2": "En son teknoloji işlem teknolojisi",
    "home.heroDesc2": "Gelişmiş işlem sistemimiz aracılığıyla, piyasa dinamiklerini doğru bir şekilde kavrayabilir ve her işlemi verimli bir şekilde gerçekleştirebilirsiniz!",
    "资金费": "Funding Fee",
    "距下次结算": "Sonraki mahsup (UTC / 4 saat)",
}
src/i18n/modules/vi.js
@@ -2959,4 +2959,6 @@
    "home.heroFeatures1": "Nền tảng mạnh mẽ, spread siêu thấp, thực thi nhanh, hỗ trợ độc quyền",
    "home.heroTitle2": "Công nghệ giao dịch tiên tiến",
    "home.heroDesc2": "Thông qua hệ thống giao dịch tiên tiến của chúng tôi, bạn có thể nắm bắt chính xác động thái thị trường và thực hiện mọi giao dịch một cách hiệu quả!",
    "资金费": "Funding Fee",
    "距下次结算": "Lần quyết toán tiếp (UTC / 4h)",
}
src/i18n/modules/zh-CN.js
@@ -2963,4 +2963,6 @@
    "home.heroFeatures1": "强大平台,超低点差,快速执行,专属支持",
    "home.heroTitle2": "顶尖交易技术",
    "home.heroDesc2": "通过我们的先进交易系统,您可以精准把握市场动态,高效执行每一笔交易!",
    "资金费": "资金费",
    "距下次结算": "距下次结算(UTC 每4小时)",
}
src/main.js
@@ -15,7 +15,7 @@
pinia.use(piniaPluginPersistedstate)
// 生产环境且为 PC 端时跳转到 PC 站
const PC_SITE_URL = 'https://wealthinfrapc.eledrink.com'
const PC_SITE_URL = 'https://pc.dpcex.com'
function isPc() {
  if (typeof navigator === 'undefined') return false
  const ua = navigator.userAgent || ''
src/views/Record/DepositAndWithdrawal.vue
@@ -11,8 +11,8 @@
        </div>
        <div class="list-wrap px-8 pt-8">
            <div class="tab-wrap flex px-4 mt-5">
                <div class="tab-item mr-4" :class="[selectIndexActive === index ? 'active' : '']"
                    v-for="(item, index) in tabListTwo" :key="index" @click="changeCoin(index)">{{ item }}</div>
                <!-- <div class="tab-item mr-4" :class="[selectIndexActive === index ? 'active' : '']"
                    v-for="(item, index) in tabListTwo" :key="index" @click="changeCoin(index)">{{ item }}</div> -->
            </div>
            <van-pull-refresh v-model="refreshing" @refresh="onRefresh" :pulling-text="t('下拉即可刷新')"
                :loosing-text="t('释放即可刷新')" :loading-text="t('加载中...')">
@@ -82,7 +82,7 @@
const router = useRouter()
const route = useRoute()
let active = ref(0)
let selectIndexActive = ref(0)
let selectIndexActive = ref(1)
const list = ref([]);
const loading = ref(false);
const refreshing = ref(false)
@@ -119,13 +119,15 @@
            selectIndexActive.value = 0
        }
    }
    changeCoin(1)
})
let currentTab = ref({
    name: t('recharge'),
    direction: 'recharge'
})
// const tabListTwo = [t('foreignCurrency'), t('digitalCurrency')]
const tabListTwo = [t('foreignCurrency')]
const tabListTwo = ['', t('digitalCurrency')]
// const tabListTwo = [ t('digitalCurrency')]
// const tabListTwo = [t('foreignCurrency')]
const page = ref(1)
const onClickTab = () => {
    currentTab.value = tabList.value[active.value]
src/views/cryptos/PerpetualContract/orderDetail.vue
@@ -27,6 +27,16 @@
          <div class="text-grey">{{ $t('可平金额') }}</div>
          <div class="textColor">{{ detail.amount }}</div>
        </div>
        <div class="flex justify-between cell-item cell-item--funding">
          <div class="text-grey">
            <div>{{ $t('资金费') }}</div>
            <div class="text-22 mt-2 funding-countdown-hint">{{ $t('距下次结算') }}</div>
          </div>
          <div class="textColor text-right">
            <div>{{ detail.funding_fee }}</div>
            <div class="text-26 mt-2 funding-countdown">{{ fundingCountdownDisplay }}</div>
          </div>
        </div>
        <div class="flex justify-between cell-item">
          <div class="text-grey">{{ $t('保证金') }}</div>
          <div class="textColor">{{ detail.deposit }}</div>
@@ -77,7 +87,11 @@
      detail: {
      },
      timer: null
      timer: null,
      /** 资金费周期倒计时刷新(与订单轮询分离) */
      fundingCountdownTimer: null,
      /** 到下一 4 小时整点(UTC 日内第 2–6 阶段起点)剩余时间 HH:mm:ss */
      fundingCountdownDisplay: '04:00:00',
    }
  },
  components: {
@@ -89,9 +103,34 @@
    this.timer = setInterval(() => {
      this.fetchDetail(order_no);
    }, 1000);
    this.tickFundingCountdown();
    this.fundingCountdownTimer = setInterval(() => this.tickFundingCountdown(), 1000);
  },
  methods: {
    /**
     * 一天按 UTC 分为 6 个阶段(每阶段 4h),每到整点阶段边界倒计时重新从 4:00:00 计起。
     */
    tickFundingCountdown() {
      const PHASE_MS = 4 * 60 * 60 * 1000;
      const now = Date.now();
      const d = new Date(now);
      const utcMidnight = Date.UTC(
        d.getUTCFullYear(),
        d.getUTCMonth(),
        d.getUTCDate(),
        0, 0, 0, 0
      );
      const elapsed = now - utcMidnight;
      const remainder = PHASE_MS - (elapsed % PHASE_MS);
      const totalSec = Math.floor(remainder / 1000);
      const h = Math.floor(totalSec / 3600);
      const m = Math.floor((totalSec % 3600) / 60);
      const s = totalSec % 60;
      this.fundingCountdownDisplay = [h, m, s]
        .map((n) => String(n).padStart(2, '0'))
        .join(':');
    },
    dayjs,
    handleText(state) {
      let str = '';
@@ -139,7 +178,11 @@
    },
  },
  beforeUnmount() {
    this.clearTimer()
    this.clearTimer();
    if (this.fundingCountdownTimer) {
      clearInterval(this.fundingCountdownTimer);
      this.fundingCountdownTimer = null;
    }
  }
}
</script>
@@ -160,6 +203,15 @@
    padding: 0 30px 30px 30px;
  }
  .funding-countdown {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
  }
  .funding-countdown-hint {
    opacity: 0.85;
  }
  .mt-30 {
    margin-top: 30px;
  }
src/views/cryptos/Recharge/rechargePage.vue
@@ -20,7 +20,7 @@
                    <div @click="copy(address)" class="text-26 border-solid-grey text-center code-btn rounded-6 textColor">
                        {{ $t('复制地址') }}</div>
                </div>
                <div>
                <!-- <div>
                    <div class="textColor text-28">{{ $t('转出地址(选填)') }}</div>
                    <div style="position: relative;" class="mt-6 mb-5 text-28">
                        <input style="padding-right: 80px;" v-model="enterAddress"
@@ -59,7 +59,7 @@
                    <div class="text-30 mb-7 textColor">{{ $t('重要提示') }}</div>
                    <div class="text-28 text-grey" v-html="tip"></div>
                    <button class="btnMain text-white next-btn text-30 rounded-lg" @click="nextBtn">{{ $t('下一步') }}</button>
                </div>
                </div> -->
            </div>
        </div>
    </div>
src/views/foreign/Quotation.vue
@@ -80,12 +80,10 @@
const tabIndex = ref(0)
const listData = ref([])
const interval = ref(null)
const type = ref('forex') //默认查询外汇
const category = ref('forex') //默认查询外汇
const type = ref('forex')
const category = ref('commodities')
const tabList = ref([
  { title: t('外汇'), category: "forex", type: "forex" },
  { title: t('大宗商品'), category: "commodities", type: "forex" },
  { title: t('指数'), category: "indices", type: "forex" },
  { title: t('加密货币'), category: "cryptos", type: "cryptos" },
])
const isLoading = ref(false)
src/views/foreign/index.vue
@@ -158,13 +158,11 @@
const { t } = useI18n()
const tabIndex = ref(1)
const tabIndex = ref(0)
const defaultListData = ref([])
const symbolType = ref('commodities') //默认查询外汇
const symbolType = ref('commodities')
const tabList = ref([
  { title: t('外汇'), value: "forex" },
  { title: t('大宗商品'), value: "commodities" },
  { title: t('指数'), value: "indices" },
  { title: t('加密货币'), value: "cryptos" },
])
const hotListDataNameArr = ref([])
src/views/home/index.vue
@@ -4,7 +4,7 @@
        <div class="header">
            <div class="header-left">
                <img src="@/assets/image/login_logo.png" alt="Wealthinfra" class="logo" />
                <span class="logo-text">Wealthinfra</span>
                <span class="logo-text">1m</span>
            </div>
            <div class="header-right">
                <div class="lang-selector" @click="$router.push('/language')">
@@ -93,12 +93,6 @@
                </div>
                <span>{{ $t('withdraw') }}</span>
            </div>
            <div class="quick-item" @click="$router.push('/cryptos/loan')">
                <div class="quick-icon">
                    <img src="@/assets/image/dk.png" alt="loan" />
                </div>
                <span>{{ $t('home.loan') }}</span>
            </div>
            <div class="quick-item" @click="$router.push('/customerService')">
                <div class="quick-icon">
                    <img src="@/assets/image/lxkf.png" alt="service" />
@@ -114,15 +108,7 @@
                    @click="goToOptions(pair.symbol, pair.type)">
                    <div class="currency-info">
                        <div class="currency-pair-row">
                            <template v-if="activeTab === 'forex' && getPairIconUrl(pair)">
                                <div class="currency-card-icon-wrap">
                                    <img :src="getPairIconUrl(pair)" alt=""
                                        class="currency-card-icon currency-card-icon--large" />
                                    <img v-if="getPairIconUrlSm(pair)" :src="getPairIconUrlSm(pair)" alt=""
                                        class="currency-card-icon currency-card-icon--sm" />
                                </div>
                            </template>
                            <img v-else-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt=""
                            <img v-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt=""
                                class="currency-card-icon" />
                            <div class="currency-pair">{{ pair.symboltxt.toUpperCase() }}</div>
                        </div>
@@ -137,18 +123,9 @@
        <!-- Trading Instruments -->
        <div class="trading-section">
            <div class="trading-tabs">
                <div class="tab-item" :class="{ active: activeTab === 'forex' }" @click="activeTab = 'forex'">
                    {{ $t('外汇') }}
                </div>
                <div class="tab-item" :class="{ active: activeTab === 'crypto' }" @click="activeTab = 'crypto'">
            <div class="trading-tabs trading-tabs--single">
                <div class="tab-item active">
                    {{ $t('加密货币') }}
                </div>
                <div class="tab-item" :class="{ active: activeTab === 'stock' }" @click="activeTab = 'stock'">
                    {{ $t('股票') }}
                </div>
                <div class="tab-item" :class="{ active: activeTab === 'etf' }" @click="activeTab = 'etf'">
                    ETF
                </div>
            </div>
            <div class="trading-pairs">
@@ -156,15 +133,7 @@
                    @click="goToOptions(pair.symbol, pair.type)">
                    <div class="pair-header">
                        <div class="pair-symbol">
                            <template v-if="activeTab === 'forex' && getPairIconUrl(pair)">
                                <div class="pair-symbol-icon-wrap">
                                    <img :src="getPairIconUrl(pair)" alt=""
                                        class="pair-symbol-icon pair-symbol-icon--large" />
                                    <img v-if="getPairIconUrlSm(pair)" :src="getPairIconUrlSm(pair)" alt=""
                                        class="pair-symbol-icon pair-symbol-icon--sm" />
                                </div>
                            </template>
                            <img v-else-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt=""
                            <img v-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt=""
                                class="pair-symbol-icon" />
                            {{ pair.symboltxt.toUpperCase() }}
                        </div>
@@ -260,7 +229,7 @@
                        <li>{{ $t('home.teamIntro') }}</li>
                        <li>{{ $t('帮助中心') }}</li>
                        <li>{{ $t('home.emailSupport') }}</li>
                        <li>support@wealthinfra.com</li>
                        <li>support@1m.com</li>
                    </ul>
                </div>
                <div class="info-section">
@@ -286,7 +255,7 @@
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { IMG_PATH } from '@/config'
@@ -294,14 +263,6 @@
import { _getRealtimeByType } from '@/service/quotes.api'
import MiniKlineChart from '@/components/MiniKlineChart/index.vue'
// 外汇货币代码 -> 国旗图国家/地区代码(用于 flagcdn.com)
const CURRENCY_TO_FLAG = {
    eur: 'eu', usd: 'us', gbp: 'gb', jpy: 'jp', chf: 'ch', aud: 'au', cad: 'ca', nzd: 'nz',
    cny: 'cn', cnh: 'cn', hkd: 'hk', sgd: 'sg', nok: 'no', sek: 'se', dkk: 'dk', mxn: 'mx',
    zar: 'za', try: 'tr', pln: 'pl', inr: 'in', krw: 'kr', thb: 'th', myr: 'my', idr: 'id',
    php: 'ph', brl: 'br', rub: 'ru', czk: 'cz', huf: 'hu', ron: 'ro', bgn: 'bg', hrk: 'hr'
}
const FLAG_CDN = 'https://flagcdn.com/w40'
import { _getUsHeadNews } from '@/service/user.api'
// import newsPlaceholder from '@/assets/image/news-placeholder.png'
@@ -351,8 +312,6 @@
    return langMap[locale.value] || '简体'
})
const activeTab = ref('forex')
const tradingPairs = ref([])
// 新闻列表,用于横向滚动轮播(与 news/index.vue 同源:_getUsHeadNews)
const newsList = ref([])
@@ -360,88 +319,25 @@
// Market Overview 使用 Trading Instruments 的前3条数据(保留完整 pair 用于展示图标)
const currencyPairs = computed(() => tradingPairs.value.slice(0, 3))
// 从外汇对取基础货币代码,如 EUR/USD -> eur,EURUSD -> eur(无斜杠取前3位)
function getForexBaseCurrency(symbol) {
    if (!symbol || typeof symbol !== 'string') return ''
    const s = symbol.trim()
    if (s.includes('/')) return s.split('/')[0].trim().toLowerCase()
    return s.slice(0, 3).toLowerCase()
}
// 从外汇对取计价货币代码,如 EUR/USD -> usd(右下角小图用)
function getForexQuoteCurrency(symbol) {
    if (!symbol || typeof symbol !== 'string') return ''
    const s = symbol.trim()
    if (s.includes('/')) return s.split('/')[1]?.trim().toLowerCase() || ''
    return s.length > 3 ? s.slice(3, 6).toLowerCase() : ''
}
// 列表项图标地址:外汇用国旗,加密货币用 symbol 图;股票、ETF 不展示图标
function getPairIconUrl(pair) {
    if (!pair) return ''
    if (activeTab.value === 'stock' || activeTab.value === 'etf') return ''
    if (activeTab.value === 'forex') {
        const code = CURRENCY_TO_FLAG[getForexBaseCurrency(pair.symbol)]
        return code ? `${FLAG_CDN}/${code}.png` : ''
    }
    return pair.iconImg ? `${IMG_PATH}/symbol/${pair.iconImg}.png` : ''
}
// 小图用名字后面3位(计价货币)的国旗,仅外汇有效
function getPairIconUrlSm(pair) {
    if (!pair || activeTab.value !== 'forex') return ''
    const quote = getForexQuoteCurrency(pair.symbol)
    if (!quote) return ''
    const code = CURRENCY_TO_FLAG[quote]
    return code ? `${FLAG_CDN}/${code}.png` : ''
}
// 获取交易数据
// 获取交易数据(仅数字货币)
const fetchTradingData = async () => {
    let type = ''
    let category = null
    switch (activeTab.value) {
        case 'crypto':
            type = 'cryptos'
            break
        case 'etf':
            type = 'indices'
            break
        case 'stock':
            type = 'US-stocks'
            break
        case 'forex':
            type = 'forex'
            category = 'forex'
            break
        default:
            type = 'forex'
            category = 'forex'
    }
    const type = 'cryptos'
    try {
        const params = {
            type: type,
            pageNo: 1
        }
        if (category) {
            params.category = category
        }
        const data = await _getRealtimeByType(params)
        if (data && Array.isArray(data)) {
            // 外汇 tab 只展示指定 symbol:EURUSD GBPUSD AUDUSD XAUUSD NZDUSD
            const forexAllowedSymbols = ['EURUSD', 'GBPUSD', 'AUDUSD', 'XAUUSD', 'NZDUSD']
            let list = data
            if (activeTab.value === 'forex') {
                list = data.filter(item => {
                    const raw = (item.symbol || item.enName || '').toString().trim()
                    const normalized = raw.replace(/\//g, '').toUpperCase()
                    return forexAllowedSymbols.includes(normalized)
                })
            }
            const list = data
            // 只取前5条数据,并转换为需要的格式
            tradingPairs.value = list.slice(0, 5).map(item => {
                const basePrice = parseFloat(item.close || item.lastPrice || 0)
@@ -477,11 +373,6 @@
        tradingPairs.value = []
    }
}
// 监听 tab 切换
watch(activeTab, () => {
    fetchTradingData()
})
// 根据当前价与涨跌幅生成小型 K 线数据,每根 [open, close, low, high]
function generateMiniKlineData(basePrice, changeRatio) {
src/views/morePage/index.vue
@@ -35,30 +35,6 @@
                        </van-grid-item>
                    </van-grid>
                </div> -->
                <div class="line-div"></div>
                <div class="nav-list ">
                    <div class="title px-5">{{ t('ETF') }}</div>
                    <van-grid class="van-grid-main" :column-num="4" :border="false">
                        <van-grid-item v-for="(item, index) in navList.etf" @click="openUrl(item)" :key="index"
                            :text="item.name">
                            <template #icon>
                                <img class="grid-item-img" :src="item.url" />
                            </template>
                        </van-grid-item>
                    </van-grid>
                </div>
                <div class="line-div"></div>
                <div class="nav-list ">
                    <div class="title px-5">{{ t('外汇') }}</div>
                    <van-grid class="van-grid-main" :column-num="4" :border="false">
                        <van-grid-item v-for="(item, index) in navList.foreign" @click="openUrl(item)" :key="index"
                            :text="item.name">
                            <template #icon>
                                <img class="grid-item-img" :src="item.url" />
                            </template>
                        </van-grid-item>
                    </van-grid>
                </div>
            </div>
            <div class="search" v-else>
                <div class="nav-list ">
@@ -96,51 +72,19 @@
        { name: t('充值'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/recharge.png`, import.meta.url), path: '/cryptos/recharge/rechargeList', isLogin: true },
        { name: t('提现'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/withdraw.png`, import.meta.url), path: '/cryptos/withdraw/withdrawPage', isLogin: true },
        { name: t('账变记录'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/record.png`, import.meta.url), path: '/cryptos/accountChange?type=cryptos', isLogin: true },
        { name: t('质押借币'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/PledgeLoan.png`, import.meta.url), path: '/cryptos/pledgeLoan', isLogin: false },
        { name: t('助力贷'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/Helpoan.png`, import.meta.url), path: '/cryptos/loan', isLogin: true },
        { name: t('news'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/Information.png`, import.meta.url), path: '/news', isLogin: false },
        { name: t('理财'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/financialmanagement.png`, import.meta.url), path: '/cryptos/fm-home', isLogin: false },
        { name: t('闪兑'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/exchange.png`, import.meta.url), path: '/cryptos/exchangePage', isLogin: true },
        { name: t('账变记录'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/record.png`, import.meta.url), path: '/cryptos/accountChange?type=cryptos', isLogin: true },
        { name: t('SpotTrading'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/coin.png`, import.meta.url), path: '/cryptos/trade/btc', isLogin: false },
        { name: t('永续合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/trading.png`, import.meta.url), path: '/cryptos/perpetualContract/btc?type=cryptos', isLogin: false },
        { name: t('交割合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/delivery.png`, import.meta.url), path: '/cryptos/perpetualContract/btc?selectIndex=2&type=cryptos', isLogin: false },
        { name: t('资金'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/asset.png`, import.meta.url), path: '/cryptos/funds?type=cryptos', isLogin: true },
        { name: t('基金理财'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/financialmanagement.png`, import.meta.url), path: '/cryptos/fund', isLogin: false },
        { name: t('智能矿池'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/SmartKuangchi.png`, import.meta.url), path: '/cryptos/machine', isLogin: false },
        { name: t('C2C'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/c2c.png`, import.meta.url), path: '/cryptos/wantBuy', isLogin: false },
    ],
    stock: [
        { name: t('美股指数'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/trade.png`, import.meta.url), path: '/quotes/openTrade?tabActive=0&symbol=AAPL&type=US-stocks', isLogin: false },
        { name: t('美股合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/contract.png`, import.meta.url), path: '/cryptos/perpetualContract/AAPL?type=US-stocks', isLogin: false },
        { name: t('美股交割'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/delivery1.png`, import.meta.url), path: '/cryptos/perpetualContract/AAPL?type=US-stocks&selectIndex=2', isLogin: false },
        { name: t('港股指数'), url: new URL(`../../assets/theme/${thStore.theme}/image/hkNav/icon01.png`, import.meta.url), path: '/quotes/openTrade?tabActive=0&symbol=00139&type=HK-stocks', isLogin: false },
        { name: t('港股合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/hkNav/icon02.png`, import.meta.url), path: '/cryptos/perpetualContract/00139?type=HK-stocks', isLogin: false },
        { name: t('港股交割'), url: new URL(`../../assets/theme/${thStore.theme}/image/hkNav/icon03.png`, import.meta.url), path: '/cryptos/perpetualContract/00139?type=HK-stocks&selectIndex=2', isLogin: false },
        { name: t('台股指数'), url: new URL(`../../assets/theme/${thStore.theme}/image/twNav/icon01.png`, import.meta.url), path: '/quotes/openTrade?tabActive=0&symbol=2002&type=TW-stocks', isLogin: false },
        { name: t('台股合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/twNav/icon02.png`, import.meta.url), path: '/cryptos/perpetualContract/2002?type=TW-stocks', isLogin: false },
        { name: t('台股交割'), url: new URL(`../../assets/theme/${thStore.theme}/image/twNav/icon03.png`, import.meta.url), path: '/cryptos/perpetualContract/2002?type=TW-stocks&selectIndex=2', isLogin: false },
        // { name: t('A股指数'), url: new URL(`../../assets/theme/${thStore.theme}/image/aNav/icon01.png`, import.meta.url), path: '/quotes/openTrade?tabActive=0&symbol=SH688981&type=A-stocks', isLogin: false },
        // { name: t('A股合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/aNav/icon02.png`, import.meta.url), path: '/cryptos/perpetualContract/SH688981?type=A-stocks', isLogin: false },
        // { name: t('A股交割'), url: new URL(`../../assets/theme/${thStore.theme}/image/aNav/icon03.png`, import.meta.url), path: '/cryptos/perpetualContract/SH688981?type=A-stocks&selectIndex=2', isLogin: false },
    ],
    etf: [
        { name: t('ETF交易'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/trade.png`, import.meta.url), path: '/quotes/openTrade?tabActive=0&symbol=GlobalETF500&type=indices', isLogin: false },
        { name: t('ETF合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/contract.png`, import.meta.url), path: '/cryptos/perpetualContract/GlobalETF500?type=indices', isLogin: false },
        { name: t('ETF交割'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/delivery1.png`, import.meta.url), path: '/cryptos/perpetualContract/GlobalETF500?type=indices&selectIndex=2', isLogin: false },
    ],
    foreign: [
        // { name: t('外汇交易'), url: new URL('@/assets/theme/dark/image/nav/trade.png', import.meta.url), path: '/foreign/coinChart?symbol=USDSGD', isLogin: false },
        { name: t('外汇合约'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/contract.png`, import.meta.url), path: '/foreign/coinChart?symbol=USDSGD', isLogin: false },
        { name: t('外汇交割'), url: new URL(`../../assets/theme/${thStore.theme}/image/nav/delivery1.png`, import.meta.url), path: '/foreign/deliveryContract/USDSGD', isLogin: false },
    ]
})
const searchList = ref([])
const allList = ref([]) //全部数据
onMounted(async () => {
    allList.value = [].concat(navList.value.recommend, navList.value.stock, navList.value.etf, navList.value.foreign)
    allList.value = navList.value.recommend.slice()
})
const onClickButton = () => {
    if (searchVal.value) {
src/views/my/index.vue
@@ -139,7 +139,6 @@
    { title: t('authVerify'), path: '/certificationCenter', icon: userIcon(1), rightText: status.value != null ? statusText(status.value) : undefined },
    { title: t('账变记录'), path: '/cryptos/accountChange', icon: userIcon(2) },
    { title: t('信用分'), path: '/my/creditScore', icon: userIcon(4) },
    { title: t('助力贷'), path: '/cryptos/loan', icon: userIcon(3) },
    { title: t('邀请推广'), path: '/promote', icon: userIcon(5) },
    { title: t('安全'), path: '/safety', icon: userIcon(6) },
    { title: t('language'), path: '/language', icon: userIcon(7), rightText: languageLabel.value },
src/views/optional/index.vue
@@ -9,7 +9,7 @@
          <span class="title">{{ optionalType == 1 ? activeInfo.listName : optionalName }}</span>
        </div>
        <div class="icon-group">
          <div class="icon search" @click="onRoute('/optional/search?symbolType=indices')">
          <div class="icon search" @click="onRoute('/optional/search?symbolType=cryptos')">
            <img :src="handleImage(searchIcon)" alt="">
          </div>
          <!-- <div class="icon setting">
@@ -69,7 +69,7 @@
            <div class="add-icon icon">
              <img src="@/assets/image/optional/add.png" alt="">
            </div>
            <span @click="onRoute('/optional/search?symbolType=indices')">{{ t('add') }}</span>
            <span @click="onRoute('/optional/search?symbolType=cryptos')">{{ t('add') }}</span>
          </div>
        </section>
      </div>
@@ -86,10 +86,10 @@
          <p>{{ t('myPortfolio') }}</p>
          <div class="sidebar">
            <van-sidebar v-model="activeSideBarIndex">
              <van-sidebar-item @click="openType('all', 'ETF')" :title="t('all')" />
              <van-sidebar-item @click="openType('all', t('加密货币'))" :title="t('all')" />
              <van-sidebar-item :title="`${item.listName}(${item.symbolCount})`" @click="openId(item)"
                v-for="(item, index) in optionalList" :key="index" />
              <van-sidebar-item @click="openType('indices', 'ETF')" :title="`ETF(${fixedData.indices || 0})`" />
              <!-- <van-sidebar-item @click="openType('indices', 'ETF')" :title="`ETF(${fixedData.indices || 0})`" /> -->
              <!-- <van-sidebar-item @click="openType('US-stocks', t('UsStocks'))"
                :title="`${t('UsStocks')}(${fixedData['US-stocks'] || 0})`" />
              <van-sidebar-item @click="openType('HK-stocks', t('HkStocks'))"
@@ -100,7 +100,7 @@
                :title="`${t('A股')}(${fixedData['A-stocks'] || 0})`" /> -->
              <van-sidebar-item @click="openType('cryptos', t('digitalCurrency'))"
                :title="`${t('digitalCurrency')}(${fixedData.cryptos || 0})`" />
              <van-sidebar-item @click="openType('forex', t('外汇'))" :title="`${t('外汇')}(${fixedData.forex || 0})`" />
              <!-- <van-sidebar-item @click="openType('forex', t('外汇'))" :title="`${t('外汇')}(${fixedData.forex || 0})`" /> -->
              <!-- <van-sidebar-item @click="openType('INDIA-stocks', t('印度股'))"
                                :title="`${t('印度股')}(${fixedData['INDIA-stocks'] || 0})`" /> -->
            </van-sidebar>
src/views/quotes/List.vue
@@ -130,97 +130,6 @@
    symbolType: 'cryptos',
    tabIndex: 1
  },
  {
    title: 'ETF',
    type: 'Etf',
    urlMatch: 'etf',
    symbolType: 'indices',
    tabIndex: 8
  },
  // {
  //   title: t('印度股'),
  //   type: 'INDIAStock',
  //   urlMatch: 'INDIA-stocks',
  //   symbolType: 'INDIA-stocks',
  //   tabIndex: 0
  // },
  {
    title: t('UsStocks'),
    type: 'UsStock',
    urlMatch: 'stock',
    symbolType: 'US-stocks',
    tabIndex: 3
  },
  // {
  //   title: t('港股'),
  //   type: 'HkStock',
  //   urlMatch: 'HK-stocks',
  //   symbolType: 'HK-stocks',
  //   tabIndex: 4
  // },
  // {
  //   title: t('台股'),
  //   type: 'TWStock',
  //   urlMatch: 'TW-stocks',
  //   symbolType: 'TW-stocks',
  //   tabIndex: 5
  // },
  // {
  //   title: t('日股'),
  //   type: 'JPStock',
  //   urlMatch: 'JP-stocks',
  //   symbolType: 'JP-stocks',
  //   tabIndex: 6
  // },
  // {
  //   title: t('A股'),
  //   type: 'AStock',
  //   urlMatch: 'A-stocks',
  //   symbolType: 'A-stocks',
  //   tabIndex: 7
  // },
  // {
  //   title: t('马股'),
  //   type: 'MYStock',
  //   urlMatch: 'MY-stocks',
  //   symbolType: 'MY-stocks',
  //   tabIndex: 8
  // },
  {
    title: t('外汇'),
    type: 'Foreign',
    urlMatch: 'for',
    symbolType: 'forex',
    tabIndex: 2
  },
  // {
  //   title: t('印度股'),
  //   type: 'INDIAStock',
  //   urlMatch: 'INDIA-stocks',
  //   symbolType: 'INDIA-stocks',
  //   tabIndex: 0
  // },
  // {
  //   title: t('英股'),
  //   type: 'UKStock',
  //   urlMatch: 'UK-stocks',
  //   symbolType: 'UK-stocks',
  //   tabIndex: 9
  // },
  // {
  //   title: t('德股'),
  //   type: 'DEStock',
  //   urlMatch: 'DE-stocks',
  //   symbolType: 'DE-stocks',
  //   tabIndex: 10
  // },
  // {
  //   title: t('巴股'),
  //   type: 'BZStock',
  //   urlMatch: 'BZ-stocks',
  //   symbolType: 'BZ-stocks',
  //   tabIndex: 11
  // },
])
@@ -236,7 +145,10 @@
    let obj = listTab.value.find(item => {
      return item.tabIndex == tabActive.value
    })
    symbolType.value = obj?.symbolType
    symbolType.value = obj?.symbolType || 'cryptos'
    if (!obj) {
      tabActive.value = 1
    }
    return
  }
  let urlPath = GetUrlRelativePath()
src/views/quotes/Market.vue
@@ -1,21 +1,9 @@
<template>
    <div class="quotes-market-page">
        <!-- Top Tabs -->
        <div class="market-tabs">
            <!-- <div class="tab-item" :class="{ active: activeTab === 'optional' }" @click="activeTab = 'optional'">
                {{ $t('自选') }}
            </div> -->
            <div class="tab-item" :class="{ active: activeTab === 'forex' }" @click="activeTab = 'forex'">
                {{ $t('外汇') }}
            </div>
            <div class="tab-item" :class="{ active: activeTab === 'crypto' }" @click="activeTab = 'crypto'">
        <div class="market-tabs market-tabs--single">
            <div class="tab-item active">
                {{ $t('加密货币') }}
            </div>
            <div class="tab-item" :class="{ active: activeTab === 'stock' }" @click="activeTab = 'stock'">
                {{ $t('股票') }}
            </div>
            <div class="tab-item" :class="{ active: activeTab === 'etf' }" @click="activeTab = 'etf'">
                ETF
            </div>
        </div>
@@ -24,18 +12,10 @@
            <van-list v-model:loading="marketLoading" :finished="marketFinished" :immediate-check="false"
                :scroll-target="marketListRef" :finished-text="$t('没有更多了') || '没有更多了'" @load="loadMoreMarket">
                <div class="pair-item" v-for="pair in tradingPairs" :key="pair.symbol"
                    @click="goToOptions(pair.symbol, pair.type)">
                    @click="goToOptions(pair.symbol)">
                    <div class="pair-header">
                        <div class="pair-symbol">
                            <template v-if="activeTab === 'forex' && getPairIconUrl(pair)">
                                <div class="pair-symbol-icon-wrap">
                                    <img :src="getPairIconUrl(pair)" alt=""
                                        class="pair-symbol-icon pair-symbol-icon--large" />
                                    <img v-if="getPairIconUrlSm(pair)" :src="getPairIconUrlSm(pair)" alt=""
                                        class="pair-symbol-icon pair-symbol-icon--sm" />
                                </div>
                            </template>
                            <img v-else-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt=""
                            <img v-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt=""
                                class="pair-symbol-icon" />
                            {{ pair.symboltxt.toUpperCase() }}
                        </div>
@@ -63,7 +43,7 @@
</template>
<script setup>
import { ref, onMounted, watch, onBeforeUnmount, nextTick } from 'vue'
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { List as VanList } from 'vant'
@@ -74,15 +54,6 @@
import { OPCIONA_LIST } from '@/store/types.store'
import { IMG_PATH } from '@/config'
import MiniKlineChart from '@/components/MiniKlineChart/index.vue'
// 外汇货币代码 -> 国旗图国家/地区代码(与首页一致)
const CURRENCY_TO_FLAG = {
    eur: 'eu', usd: 'us', gbp: 'gb', jpy: 'jp', chf: 'ch', aud: 'au', cad: 'ca', nzd: 'nz',
    cny: 'cn', cnh: 'cn', hkd: 'hk', sgd: 'sg', nok: 'no', sek: 'se', dkk: 'dk', mxn: 'mx',
    zar: 'za', try: 'tr', pln: 'pl', inr: 'in', krw: 'kr', thb: 'th', myr: 'my', idr: 'id',
    php: 'ph', brl: 'br', rub: 'ru', czk: 'cz', huf: 'hu', ron: 'ro', bgn: 'bg', hrk: 'hr'
}
const FLAG_CDN = 'https://flagcdn.com/w40'
const { t } = useI18n()
const router = useRouter()
@@ -99,42 +70,11 @@
const marketInitialLoading = ref(false) // tab 切换时首屏请求中,避免 @load 重复请求 pageNo=1
const MARKET_PAGE_SIZE = 10
// 从外汇对取基础货币代码,与首页一致
function getForexBaseCurrency(symbol) {
    if (!symbol || typeof symbol !== 'string') return ''
    const s = symbol.trim()
    if (s.includes('/')) return s.split('/')[0].trim().toLowerCase()
    return s.slice(0, 3).toLowerCase()
}
// 从外汇对取计价货币代码,如 EUR/USD -> usd(右下角小图用)
function getForexQuoteCurrency(symbol) {
    if (!symbol || typeof symbol !== 'string') return ''
    const s = symbol.trim()
    if (s.includes('/')) return s.split('/')[1]?.trim().toLowerCase() || ''
    return s.length > 3 ? s.slice(3, 6).toLowerCase() : ''
}
// 列表项图标地址:外汇用国旗,加密货币用 symbol 图;股票、ETF 不展示图标;自选按 type 判断
function getPairIconUrl(pair) {
    if (!pair) return ''
    const tab = activeTab.value
    if (tab === 'stock' || tab === 'etf') return ''
    if (tab === 'optional' && (pair.type === 'US-stocks' || pair.type === 'indices')) return ''
    if (tab === 'forex') {
        const code = CURRENCY_TO_FLAG[getForexBaseCurrency(pair.symbol)]
        return code ? `${FLAG_CDN}/${code}.png` : ''
    }
    return pair.iconImg ? `${IMG_PATH}/symbol/${pair.iconImg}.png` : ''
}
// 小图用名字后面3位(计价货币)的国旗,仅外汇有效
function getPairIconUrlSm(pair) {
    if (!pair || activeTab.value !== 'forex') return ''
    const quote = getForexQuoteCurrency(pair.symbol)
    if (!quote) return ''
    const code = CURRENCY_TO_FLAG[quote]
    return code ? `${FLAG_CDN}/${code}.png` : ''
}
// 根据当前价与涨跌幅生成小型 K 线数据,与首页一致
@@ -261,36 +201,13 @@
// 获取交易数据;pageNo 页码,append 是否追加
const fetchTradingData = async (pageNo = 1, append = false) => {
    let type = ''
    let category = null
    switch (activeTab.value) {
        case 'crypto':
            type = 'cryptos'
            break
        case 'etf':
            type = 'indices'
            break
        case 'stock':
            type = 'US-stocks'
            break
        case 'forex':
            type = 'forex'
            category = 'forex'
            break
        default:
            type = 'forex'
            category = 'forex'
    }
    const type = 'cryptos'
    try {
        const params = {
            type: type,
            pageNo: pageNo,
            pageSize: MARKET_PAGE_SIZE
        }
        if (category) {
            params.category = category
        }
        const data = await _getRealtimeByType(params)
@@ -367,13 +284,11 @@
}
// 跳转到交易页 Options,与首页一致:/trade/options?symbol=xxx&activeTab=xxx
function goToOptions(symbol, type) {
function goToOptions(symbol) {
    if (!symbol) return
    const tabMap = { crypto: 'cryptos', etf: 'indices', stock: 'US-stocks', forex: 'forex', optional: 'optional' }
    const activeTabValue = type || tabMap[activeTab.value] || 'cryptos'
    router.push({
        path: '/trade/options',
        query: { symbol, activeTab: activeTabValue }
        query: { symbol, activeTab: 'cryptos' }
    })
}
@@ -403,11 +318,6 @@
        marketInitialLoading.value = false
    }
}
// 监听 tab 切换
watch(activeTab, () => {
    fetchData()
})
// 处理列表项点击
const handleItemClick = (pair) => {
src/views/safety/index.vue
@@ -44,28 +44,28 @@
const { t } = useI18n()
const verifyList = ref([
  // {
  //   title: t('googleAuthenticator'),
  //   name: "google",
  //   icon: {
  //     verifyno: new URL('@/assets/image/userCenter/gooleVerifyno.png', import.meta.url),
  //     verify: new URL('@/assets/image/userCenter/gooleVerify.png', import.meta.url)
  //   },
  //   isVerify: false,
  //   url: '/bindVerify?type=3',
  //   type: 3
  // },
  {
    title: t('phoneVerify'),
    name: "phone",
    title: t('googleAuthenticator'),
    name: "google",
    icon: {
      verifyno: new URL('@/assets/image/userCenter/phoneVerifyno.png', import.meta.url),
      verify: new URL('@/assets/image/userCenter/phoneVerify.png', import.meta.url)
      verifyno: new URL('@/assets/image/userCenter/gooleVerifyno.png', import.meta.url),
      verify: new URL('@/assets/image/userCenter/gooleVerify.png', import.meta.url)
    },
    isVerify: false,
    url: '/bindVerify?type=1',
    type: 1
    url: '/bindVerify?type=3',
    type: 3
  },
  // {
  //   title: t('phoneVerify'),
  //   name: "phone",
  //   icon: {
  //     verifyno: new URL('@/assets/image/userCenter/phoneVerifyno.png', import.meta.url),
  //     verify: new URL('@/assets/image/userCenter/phoneVerify.png', import.meta.url)
  //   },
  //   isVerify: false,
  //   url: '/bindVerify?type=1',
  //   type: 1
  // },
  {
    title: t('emailVerify'),
    name: "email",
src/views/trade/Options.vue
@@ -33,7 +33,7 @@
            close-icon-position="top-right">
            <div class="symbol-modal">
                <!-- Modal Tabs:同一 activeTab 的 label 与 value 统一配置 -->
                <div class="modal-tabs">
                <div v-if="modalTabs.length > 1" class="modal-tabs">
                    <div class="tab-item" v-for="tab in modalTabs" :key="tab.value"
                        :class="{ active: activeTab === tab.value }" @click="activeTab = tab.value">
                        {{ $t(tab.label) }}
@@ -77,17 +77,13 @@
import OptionsContract from './components/OptionsContract.vue'
import { _getHomeList } from '@/service/home.api'
import { _getRealtimeByType, _isItemHasAddGlobal } from '@/service/quotes.api'
import { _listItemsById, _itemUserOptionalList, _getQuotes } from '@/service/quotes.api'
import { _collect, _deleteCollect } from '@/service/cryptos.api'
import { useUserStore } from '@/store/user'
import { useQuotesStore } from '@/store/quotes.store'
import { OPCIONA_LIST } from '@/store/types.store'
import { HOST_URL } from '@/config'
const route = useRoute()
const { t } = useI18n()
const useStore = useUserStore()
const quotesStore = useQuotesStore()
const tradeType = ref('options') // 'options' 期权 / 'contract' 合约,默认期权交易
const currentSymbol = ref('btc')
@@ -103,30 +99,19 @@
const symbolInitialLoading = ref(false) // tab 切换时首屏请求中,避免 @load 重复请求 pageNo=1
const SYMBOL_PAGE_SIZE = 10
// 允许的 activeTab 值(与 modalTabs.value 一致)
const VALID_ACTIVE_TABS = ['optional', 'forex', 'cryptos', 'US-stocks', 'indices']
// 从路由 path(params) 或 query 同步 symbol 与 activeTab
// 从路由 path(params) 或 query 同步 symbol(品种固定为数字货币)
function applyFromRoute() {
    const p = route.params || {}
    const q = route.query || {}
    const symbol = q.symbol ?? p.symbol
    const tab = q.activeTab ?? q.tab ?? p.activeTab ?? p.tab
    if (symbol != null && String(symbol).trim()) {
        currentSymbol.value = String(symbol).trim()
    }
    if (tab != null && VALID_ACTIVE_TABS.includes(String(tab))) {
        activeTab.value = tab
    }
    activeTab.value = 'cryptos'
}
// 弹窗 Tab:label 为多语言 key,value 与接口类型统一(optional 仅自选列表用)
const modalTabs = [
    // { label: '自选', value: 'optional' },
    { label: '外汇', value: 'forex' },
    { label: '加密货币', value: 'cryptos' },
    { label: '股票', value: 'US-stocks' },
    { label: 'ETF', value: 'indices' }
]
// 头部显示:关联选择项的 name,无则用 symbol
@@ -140,9 +125,7 @@
// 嵌入合约:1=永续(合约交易),2=交割(期权交易)
const embedSelectIndex = computed(() => tradeType.value === 'contract' ? 1 : 2)
// 嵌入合约品种类型(value 已与接口类型统一,仅 optional 需映射)
const embedType = computed(() => {
    return activeTab.value === 'optional' ? 'forex' : activeTab.value
})
const embedType = computed(() => 'cryptos')
// 图标路径
const starIcon = new URL('@/assets/image/icon-star.png', import.meta.url).href
@@ -155,59 +138,13 @@
    return `${HOST_URL}/symbol/${baseSymbol.toLowerCase()}.png`
}
// 获取自选数据
const fetchOptionalData = async () => {
    try {
        if (!useStore.userInfo.token) {
            symbolList.value = []
            return
        }
        const listData = await _itemUserOptionalList()
        if (listData && listData.list && listData.list.length > 0) {
            const firstList = listData.list[0]
            const itemsData = await _listItemsById({ id: firstList.listId })
            if (itemsData && Array.isArray(itemsData)) {
                const symbols = itemsData.map(item => ({ symbol: item.symbol }))
                quotesStore[OPCIONA_LIST](symbols)
                if (symbols.length > 0) {
                    const symbolStr = symbols.map(s => s.symbol).join(',')
                    const data = await _getHomeList(symbolStr)
                    if (data && Array.isArray(data)) {
                        symbolList.value = data
                    } else {
                        symbolList.value = []
                    }
                } else {
                    symbolList.value = []
                }
            } else {
                symbolList.value = []
            }
        } else {
            symbolList.value = []
        }
    } catch (error) {
        console.error('获取自选数据失败:', error)
        symbolList.value = []
    }
}
// 获取交易数据(activeTab.value 已与接口 type 统一);pageNo 页码,append 是否追加
// 获取交易数据;pageNo 页码,append 是否追加
const fetchTradingData = async (pageNo = 1, append = false) => {
    const type = activeTab.value === 'optional' ? 'forex' : activeTab.value
    const category = type === 'forex' ? 'forex' : null
    try {
        const params = {
            type: type,
            type: 'cryptos',
            pageNo: pageNo,
            pageSize: SYMBOL_PAGE_SIZE
        }
        if (category) {
            params.category = category
        }
        const data = await _getRealtimeByType(params)
@@ -244,11 +181,6 @@
// 上拉触底加载更多(仅非自选 tab 分页,pageSize=10)
const loadMoreSymbols = async () => {
    if (activeTab.value === 'optional') {
        symbolFinished.value = true
        symbolLoading.value = false
        return
    }
    if (symbolInitialLoading.value) {
        symbolLoading.value = false
        return
@@ -269,13 +201,8 @@
    symbolFinished.value = false
    symbolInitialLoading.value = true
    try {
        if (activeTab.value === 'optional') {
            await fetchOptionalData()
            symbolFinished.value = true
        } else {
            await fetchTradingData(1, false)
            symbolPage.value = 2
        }
        await fetchTradingData(1, false)
        symbolPage.value = 2
        // tab 切换后列表滚动回顶部
        await nextTick()
        if (symbolListRef.value) symbolListRef.value.scrollTop = 0
@@ -337,11 +264,6 @@
        showToast(e?.msg || t('操作失败'))
    }
}
// 监听 tab 切换
watch(activeTab, () => {
    fetchData()
})
// 当前交易对变化时刷新收藏状态
watch(currentSymbol, () => {
src/views/trade/components/OptionsContract.vue
@@ -456,6 +456,7 @@
    },
    fetchOrderListCur(symbol) {
      if (!this.userInfo || !this.userInfo.token) return
      symbol = ''
      const params = { symbol, type: 'orders', page_no: 1 }
      _contractApplyOrderList(params).then(data => { this.orderCur = data || [] })
      this.clearTimer()
@@ -465,6 +466,8 @@
    },
    fetchOrderListHold(symbol) {
      if (!this.userInfo || !this.userInfo.token) return
      symbol = ''
      const symbolType = this.type || 'cryptos'
      const obj = { symbol, type: 'orders', page_no: 1, symbolType }
      contractOrder(obj).then(data => {
@@ -480,11 +483,13 @@
    fetchFutrueHoldList(symbol) {
      if (!this.userInfo || !this.userInfo.token) return
      const symbolType = this.type || 'cryptos'
      symbol = ''
      _futrueOrderList(symbol, 'orders', 1, symbolType).then(data => {
        this.futrueHold = (data || []).sort(this.sortData)
      })
      this.clearTimer()
      this.timer = setInterval(() => {
        symbol = ''
        _futrueOrderList(symbol, 'orders', 1, symbolType).then(data => {
          this.futrueHold = (data || []).sort(this.sortData)
        })
@@ -492,6 +497,7 @@
    },
    fetchFutrueHistory(symbol) {
      const symbolType = this.type || 'cryptos'
      symbol = ''
      _futrueOrderList(symbol, 'hisorders', 1, symbolType).then(data => {
        this.futrueHistroy = data || []
      })
src/views/trade/index.vue
@@ -550,7 +550,7 @@
const tabActive = ref(defaultTabActive);
const navActive = ref(0);
const userStore = useUserStore();
const symbolType = ref("indices"); //默认etf
const symbolType = ref("cryptos");
const etfList = ref([]);
const symbolList = ref([]);
const currency = ref({});
@@ -599,97 +599,6 @@
    symbolType: 'cryptos',
    tabIndex: 1
  },
  {
    title: 'ETF',
    type: 'Etf',
    urlMatch: 'etf',
    symbolType: 'indices',
    tabIndex: 0
  },
  {
    title: t('外汇'),
    type: 'Foreign',
    urlMatch: 'for',
    symbolType: 'forex',
    tabIndex: 2
  },
  // {
  //   title: t('马股'),
  //   type: 'MyStock',
  //   urlMatch: 'stock',
  //   symbolType: 'MY-stocks',
  //   tabIndex: 12
  // },
  // {
  //   title: t('印度股'),
  //   type: 'INDIAStock',
  //   urlMatch: 'INDIA-stocks',
  //   symbolType: 'INDIA-stocks',
  //   tabIndex: 8
  // },
  // {
  //   title: t('UsStocks'),
  //   type: 'UsStock',
  //   urlMatch: 'stock',
  //   symbolType: 'US-stocks',
  //   tabIndex: 3
  // },
  // {
  //   title: t('港股'),
  //   type: 'HkStock',
  //   urlMatch: 'HK-stocks',
  //   symbolType: 'HK-stocks',
  //   tabIndex: 4
  // },
  // {
  //   title: t('台股'),
  //   type: 'TWStock',
  //   urlMatch: 'TW-stocks',
  //   symbolType: 'TW-stocks',
  //   tabIndex: 5
  // },
  // {
  //   title: t('日股'),
  //   type: 'JPStock',
  //   urlMatch: 'JP-stocks',
  //   symbolType: 'JP-stocks',
  //   tabIndex: 6
  // },
  // {
  //   title: t('A股'),
  //   type: 'AStock',
  //   urlMatch: 'A-stocks',
  //   symbolType: 'A-stocks',
  //   tabIndex: 7
  // },
  // {
  //   title: t('印度股'),
  //   type: 'INDIAStock',
  //   urlMatch: 'INDIA-stocks',
  //   symbolType: 'INDIA-stocks',
  //   tabIndex: 8
  // },
  // {
  //   title: t('英股'),
  //   type: 'UKStock',
  //   urlMatch: 'UK-stocks',
  //   symbolType: 'UK-stocks',
  //   tabIndex: 9
  // },
  // {
  //   title: t('德股'),
  //   type: 'DEStock',
  //   urlMatch: 'DE-stocks',
  //   symbolType: 'DE-stocks',
  //   tabIndex: 10
  // },
  // {
  //   title: t('巴股'),
  //   type: 'BZStock',
  //   urlMatch: 'BZ-stocks',
  //   symbolType: 'BZ-stocks',
  //   tabIndex: 11
  // },
])
const navTabList = computed(() => {
vite.config.js
@@ -61,8 +61,8 @@
            // 关键:用正则表达式匹配所有含/api的路径(忽略大小写)
            '^/api.*': { // 匹配以/api开头的任何路径(如/api、/api/xxx、/api?xx等)
                // target: 'http://154.23.189.28:8086',
                target:'https://api.eledrink.com',
                // target:'https://api.eledrink.com',
                target:'https://api.dpcex.com',
                // target:'https://api.dpcex.com',
                // target: 'https://by2.cccxxx.cc',
                changeOrigin: true,
                secure: false,
vite.config.js.timestamp-1775042120961.mjs
New file
@@ -0,0 +1,88 @@
// vite.config.js
import {
  defineConfig
} from "file:///D:/xzpro/26-3-31/wap/node_modules/vite/dist/node/index.js";
import vue from "file:///D:/xzpro/26-3-31/wap/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import path from "path";
import Components from "file:///D:/xzpro/26-3-31/wap/node_modules/unplugin-vue-components/dist/vite.mjs";
import {
  VantResolver
} from "file:///D:/xzpro/26-3-31/wap/node_modules/unplugin-vue-components/dist/resolvers.mjs";
import DefineOptions from "file:///D:/xzpro/26-3-31/wap/node_modules/unplugin-vue-define-options/dist/vite.mjs";
import {
  visualizer
} from "file:///D:/xzpro/26-3-31/wap/node_modules/rollup-plugin-visualizer/dist/plugin/index.js";
import legacy from "file:///D:/xzpro/26-3-31/wap/node_modules/@vitejs/plugin-legacy/dist/index.mjs";
var __vite_injected_original_dirname = "D:\\xzpro\\26-3-31\\wap";
var isVisualizer = process.env.VISUALIZER === "show";
var vite_config_default = defineConfig({
  base: "./",
  plugins: [
    vue(),
    Components({
      resolvers: [VantResolver()]
    }),
    DefineOptions(),
    isVisualizer && visualizer({
      gzipSize: true
    })
  ],
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/assets/css/variable.scss";`
      }
    }
  },
  server: {
    open: true,
    port: 333,
    host: "0.0.0.0",
    proxy: {
      "^/api.*": {
        target: "https://api.dpcex.com",
        changeOrigin: true,
        secure: false,
        rewrite: (path2) => path2,
        configure: (proxy) => {
          proxy.on("proxyReq", (proxyReq, req) => {
            console.log(`[\u4EE3\u7406\u6210\u529F] \u6355\u83B7\u8BF7\u6C42: ${req.method} ${req.url}`);
          });
          proxy.on("error", (err) => {
            console.log(`[\u4EE3\u7406\u9519\u8BEF] ${err.message}`);
          });
        }
      }
    }
  },
  resolve: {
    dedupe: [
      "vue"
    ],
    alias: {
      "vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js",
      "~": path.resolve(__vite_injected_original_dirname, "./"),
      "@": path.resolve(__vite_injected_original_dirname, "src")
    }
  },
  build: {
    assetsDir: "static",
    chunkSizeWarningLimit: 5e3,
    target: "esnext",
    rollupOptions: {
      input: {
        index: path.resolve(__vite_injected_original_dirname, "index.html")
      },
      output: {
        chunkFileNames: "js/[name]-[hash].js",
        entryFileNames: "js/[name]-[hash].js",
        assetFileNames: "[ext]/[name]-[hash].[ext]"
      }
    }
  },
  externals: ["vue"]
});
export {
  vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFx4enByb1xcXFwyNi0zLTMxXFxcXHdhcFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRDpcXFxceHpwcm9cXFxcMjYtMy0zMVxcXFx3YXBcXFxcdml0ZS5jb25maWcuanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0Q6L3h6cHJvLzI2LTMtMzEvd2FwL3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHtcclxuXHRkZWZpbmVDb25maWdcclxufSBmcm9tICd2aXRlJ1xyXG5pbXBvcnQgdnVlIGZyb20gJ0B2aXRlanMvcGx1Z2luLXZ1ZSdcclxuaW1wb3J0IHBhdGggZnJvbSAncGF0aCdcclxuaW1wb3J0IENvbXBvbmVudHMgZnJvbSAndW5wbHVnaW4tdnVlLWNvbXBvbmVudHMvdml0ZSc7XHJcbmltcG9ydCB7XHJcblx0VmFudFJlc29sdmVyXHJcbn0gZnJvbSAndW5wbHVnaW4tdnVlLWNvbXBvbmVudHMvcmVzb2x2ZXJzJztcclxuaW1wb3J0IERlZmluZU9wdGlvbnMgZnJvbSAndW5wbHVnaW4tdnVlLWRlZmluZS1vcHRpb25zL3ZpdGUnO1xyXG5pbXBvcnQge1xyXG5cdHZpc3VhbGl6ZXJcclxufSBmcm9tICdyb2xsdXAtcGx1Z2luLXZpc3VhbGl6ZXInXHJcbi8vIHl5aGhcclxuaW1wb3J0IGxlZ2FjeSBmcm9tICdAdml0ZWpzL3BsdWdpbi1sZWdhY3knO1xyXG5cclxuXHJcbmNvbnN0IGlzVmlzdWFsaXplciA9IHByb2Nlc3MuZW52LlZJU1VBTElaRVIgPT09ICdzaG93J1xyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xyXG5cdGJhc2U6ICcuLycsXHJcblx0cGx1Z2luczogW1xyXG5cdFx0dnVlKCksXHJcblx0XHRDb21wb25lbnRzKHtcclxuXHRcdFx0cmVzb2x2ZXJzOiBbVmFudFJlc29sdmVyKCldLFxyXG5cdFx0fSksXHJcblx0XHREZWZpbmVPcHRpb25zKCksXHJcblx0XHRpc1Zpc3VhbGl6ZXIgJiYgdmlzdWFsaXplcih7XHJcblx0XHRcdGd6aXBTaXplOiB0cnVlXHJcblx0XHR9KSxcclxuXHRcdC8vIHl5aGhcclxuXHRcdC8vIGxlZ2FjeSh7XHJcblx0XHQvLyBcdHRhcmdldHM6IFsnZGVmYXVsdHMnLCAnbm90IElFIDExJ10sXHJcblx0XHQvLyB9KSxcclxuXHJcblx0XSxcclxuXHRjc3M6IHtcclxuXHRcdHByZXByb2Nlc3Nvck9wdGlvbnM6IHtcclxuXHRcdFx0c2Nzczoge1xyXG5cdFx0XHRcdGFkZGl0aW9uYWxEYXRhOiBgQGltcG9ydCBcIkAvYXNzZXRzL2Nzcy92YXJpYWJsZS5zY3NzXCI7YFxyXG5cdFx0XHR9LFxyXG5cdFx0fVxyXG5cdH0sXHJcblx0Ly8gc2VydmVyOiB7XHJcblx0Ly8gICBvcGVuOiB0cnVlLFxyXG5cdC8vICAgcG9ydDogMzMzLFxyXG5cdC8vICAgaG1yOiB0cnVlLFxyXG5cdC8vICAgaG9zdDogJzAuMC4wLjAnLFxyXG5cdC8vICAgcHJveHk6IHtcclxuXHQvLyAgICAgXCIvYXBpXCI6IHtcclxuXHQvLyAgICAgICB0YXJnZXQ6IFwiaHR0cDovL2FwaS5zd2Z0ZWsuY29tXCIsICAgLy9cdTU4NkJcdTUxOTlcdTUzQ0RcdTU0MTFcdTRFRTNcdTc0MDY4MDg1XHU1NDBFXHU3Njg0XHU1N0RGXHU1NDBEXHJcblx0Ly8gICAgICAgY2hhbmdlT3JpZ2luOiB0cnVlLFxyXG5cdC8vICAgICAgIHNlY3VyZTogZmFsc2VcclxuXHQvLyAgICAgfSxcclxuXHQvLyAgIH0sXHJcblx0Ly8gfSxcclxuXHRzZXJ2ZXI6IHtcclxuXHRcdG9wZW46IHRydWUsXHJcblx0XHRwb3J0OiAzMzMsXHJcblx0XHRob3N0OiAnMC4wLjAuMCcsXHJcblx0XHRwcm94eToge1xyXG5cdFx0XHQvLyBcdTUxNzNcdTk1MkVcdUZGMUFcdTc1MjhcdTZCNjNcdTUyMTlcdTg4NjhcdThGQkVcdTVGMEZcdTUzMzlcdTkxNERcdTYyNDBcdTY3MDlcdTU0MkIvYXBpXHU3Njg0XHU4REVGXHU1Rjg0XHVGRjA4XHU1RkZEXHU3NTY1XHU1OTI3XHU1QzBGXHU1MTk5XHVGRjA5XHJcblx0XHRcdCdeL2FwaS4qJzogeyAvLyBcdTUzMzlcdTkxNERcdTRFRTUvYXBpXHU1RjAwXHU1OTM0XHU3Njg0XHU0RUZCXHU0RjU1XHU4REVGXHU1Rjg0XHVGRjA4XHU1OTgyL2FwaVx1MzAwMS9hcGkveHh4XHUzMDAxL2FwaT94eFx1N0I0OVx1RkYwOVxyXG5cdFx0XHRcdC8vIHRhcmdldDogJ2h0dHA6Ly8xNTQuMjMuMTg5LjI4OjgwODYnLFxyXG5cdFx0XHRcdHRhcmdldDonaHR0cHM6Ly9hcGkuMW1jcnlwdG8uY29tJyxcclxuXHRcdFx0XHQvLyB0YXJnZXQ6J2h0dHBzOi8vYXBpLjFtY3J5cHRvLmNvbScsXHJcblx0XHRcdFx0Ly8gdGFyZ2V0OiAnaHR0cHM6Ly9ieTIuY2NjeHh4LmNjJyxcclxuXHRcdFx0XHRjaGFuZ2VPcmlnaW46IHRydWUsXHJcblx0XHRcdFx0c2VjdXJlOiBmYWxzZSxcclxuXHRcdFx0XHQvLyBsb2dMZXZlbDogJ2RlYnVnJywgIC8vIFx1NjI1M1x1NTM3MFx1OEJFNlx1N0VDNlx1NEVFM1x1NzQwNlx1NjVFNVx1NUZEN1xyXG5cdFx0XHRcdHJld3JpdGU6IChwYXRoKSA9PiBwYXRoLCAvLyBcdTRFMERcdTRGRUVcdTY1MzlcdThERUZcdTVGODRcdUZGMDhcdTU5ODJcdTY3OUNcdTc2RUVcdTY4MDdBUElcdTY3MkNcdThFQUJcdTVDMzFcdTY3MDkvYXBpXHU1MjREXHU3RjAwXHVGRjA5XHJcblx0XHRcdFx0Y29uZmlndXJlOiAocHJveHkpID0+IHtcclxuXHRcdFx0XHRcdHByb3h5Lm9uKCdwcm94eVJlcScsIChwcm94eVJlcSwgcmVxKSA9PiB7XHJcblx0XHRcdFx0XHRcdGNvbnNvbGUubG9nKGBbXHU0RUUzXHU3NDA2XHU2MjEwXHU1MjlGXSBcdTYzNTVcdTgzQjdcdThCRjdcdTZDNDI6ICR7cmVxLm1ldGhvZH0gJHtyZXEudXJsfWApO1xyXG5cdFx0XHRcdFx0fSk7XHJcblx0XHRcdFx0XHRwcm94eS5vbignZXJyb3InLCAoZXJyKSA9PiB7XHJcblx0XHRcdFx0XHRcdGNvbnNvbGUubG9nKGBbXHU0RUUzXHU3NDA2XHU5NTE5XHU4QkVGXSAke2Vyci5tZXNzYWdlfWApO1xyXG5cdFx0XHRcdFx0fSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHRyZXNvbHZlOiB7XHJcblx0XHRkZWR1cGU6IFtcclxuXHRcdFx0J3Z1ZSdcclxuXHRcdF0sXHJcblx0XHRhbGlhczoge1xyXG5cdFx0XHQndnVlLWkxOG4nOiAndnVlLWkxOG4vZGlzdC92dWUtaTE4bi5janMuanMnLFxyXG5cdFx0XHRcIn5cIjogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4vJyksXHJcblx0XHRcdFwiQFwiOiBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAnc3JjJyksXHJcblx0XHR9XHJcblx0fSxcclxuXHRidWlsZDoge1xyXG5cdFx0YXNzZXRzRGlyOiBcInN0YXRpY1wiLFxyXG5cdFx0Y2h1bmtTaXplV2FybmluZ0xpbWl0OiA1MDAwLCAvLyBcdTY1MzlcdTRFM0ExMDAwS2lCXHJcblx0XHQvLyB5eWhoXHJcblx0XHR0YXJnZXQ6ICdlc25leHQnLCAvLyBcdTYyMTZcdTgwMDUgJ2VzMjAxOScgLyAnZXMyMDIwJ1xyXG5cdFx0Ly8gXHU2NjBFXHU3ODZFXHU4QkJFXHU3RjZFXHU2NTJGXHU2MzAxXHU1RjAyXHU2QjY1XHU3NTFGXHU2MjEwXHU1NjY4XHU3Njg0XHU3NkVFXHU2ODA3XHU3M0FGXHU1ODgzXHJcblx0XHRyb2xsdXBPcHRpb25zOiB7XHJcblx0XHRcdGlucHV0OiB7XHJcblx0XHRcdFx0aW5kZXg6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsIFwiaW5kZXguaHRtbFwiKSxcclxuXHRcdFx0fSxcclxuXHRcdFx0b3V0cHV0OiB7XHJcblx0XHRcdFx0Y2h1bmtGaWxlTmFtZXM6ICdqcy9bbmFtZV0tW2hhc2hdLmpzJyxcclxuXHRcdFx0XHRlbnRyeUZpbGVOYW1lczogXCJqcy9bbmFtZV0tW2hhc2hdLmpzXCIsXHJcblx0XHRcdFx0YXNzZXRGaWxlTmFtZXM6IFwiW2V4dF0vW25hbWVdLVtoYXNoXS5bZXh0XVwiXHJcblx0XHRcdH0sXHJcblx0XHR9LFxyXG5cdH0sXHJcblx0ZXh0ZXJuYWxzOiBbJ3Z1ZSddXHJcbn0pIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFzUDtBQUFBLEVBQ3JQO0FBQUEsT0FDTTtBQUNQLE9BQU8sU0FBUztBQUNoQixPQUFPLFVBQVU7QUFDakIsT0FBTyxnQkFBZ0I7QUFDdkI7QUFBQSxFQUNDO0FBQUEsT0FDTTtBQUNQLE9BQU8sbUJBQW1CO0FBQzFCO0FBQUEsRUFDQztBQUFBLE9BQ007QUFFUCxPQUFPLFlBQVk7QUFkbkIsSUFBTSxtQ0FBbUM7QUFpQnpDLElBQU0sZUFBZSxRQUFRLElBQUksZUFBZTtBQUNoRCxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMzQixNQUFNO0FBQUEsRUFDTixTQUFTO0FBQUEsSUFDUixJQUFJO0FBQUEsSUFDSixXQUFXO0FBQUEsTUFDVixXQUFXLENBQUMsYUFBYSxDQUFDO0FBQUEsSUFDM0IsQ0FBQztBQUFBLElBQ0QsY0FBYztBQUFBLElBQ2QsZ0JBQWdCLFdBQVc7QUFBQSxNQUMxQixVQUFVO0FBQUEsSUFDWCxDQUFDO0FBQUEsRUFNRjtBQUFBLEVBQ0EsS0FBSztBQUFBLElBQ0oscUJBQXFCO0FBQUEsTUFDcEIsTUFBTTtBQUFBLFFBQ0wsZ0JBQWdCO0FBQUEsTUFDakI7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUFBLEVBY0EsUUFBUTtBQUFBLElBQ1AsTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLElBQ04sT0FBTztBQUFBLE1BRU4sV0FBVztBQUFBLFFBRVYsUUFBTztBQUFBLFFBR1AsY0FBYztBQUFBLFFBQ2QsUUFBUTtBQUFBLFFBRVIsU0FBUyxDQUFDQSxVQUFTQTtBQUFBLFFBQ25CLFdBQVcsQ0FBQyxVQUFVO0FBQ3JCLGdCQUFNLEdBQUcsWUFBWSxDQUFDLFVBQVUsUUFBUTtBQUN2QyxvQkFBUSxJQUFJLHdEQUFnQixJQUFJLFVBQVUsSUFBSSxLQUFLO0FBQUEsVUFDcEQsQ0FBQztBQUNELGdCQUFNLEdBQUcsU0FBUyxDQUFDLFFBQVE7QUFDMUIsb0JBQVEsSUFBSSw4QkFBVSxJQUFJLFNBQVM7QUFBQSxVQUNwQyxDQUFDO0FBQUEsUUFDRjtBQUFBLE1BQ0Q7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1IsUUFBUTtBQUFBLE1BQ1A7QUFBQSxJQUNEO0FBQUEsSUFDQSxPQUFPO0FBQUEsTUFDTixZQUFZO0FBQUEsTUFDWixLQUFLLEtBQUssUUFBUSxrQ0FBVyxJQUFJO0FBQUEsTUFDakMsS0FBSyxLQUFLLFFBQVEsa0NBQVcsS0FBSztBQUFBLElBQ25DO0FBQUEsRUFDRDtBQUFBLEVBQ0EsT0FBTztBQUFBLElBQ04sV0FBVztBQUFBLElBQ1gsdUJBQXVCO0FBQUEsSUFFdkIsUUFBUTtBQUFBLElBRVIsZUFBZTtBQUFBLE1BQ2QsT0FBTztBQUFBLFFBQ04sT0FBTyxLQUFLLFFBQVEsa0NBQVcsWUFBWTtBQUFBLE1BQzVDO0FBQUEsTUFDQSxRQUFRO0FBQUEsUUFDUCxnQkFBZ0I7QUFBQSxRQUNoQixnQkFBZ0I7QUFBQSxRQUNoQixnQkFBZ0I7QUFBQSxNQUNqQjtBQUFBLElBQ0Q7QUFBQSxFQUNEO0FBQUEsRUFDQSxXQUFXLENBQUMsS0FBSztBQUNsQixDQUFDOyIsCiAgIm5hbWVzIjogWyJwYXRoIl0KfQo=
yarn.lock
Diff too large