国际化

This commit is contained in:
pengxiaolong
2025-12-02 21:23:21 +08:00
parent 84bab8b8c8
commit 812fade238
15 changed files with 1589 additions and 484 deletions

Binary file not shown.

93
package-lock.json generated
View File

@@ -15,6 +15,7 @@
"pinia": "^3.0.3", "pinia": "^3.0.3",
"qwebchannel": "^6.2.0", "qwebchannel": "^6.2.0",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-i18n": "^9.14.5",
"vue-router": "^4.0.3", "vue-router": "^4.0.3",
"vuex": "^4.0.0" "vuex": "^4.0.0"
}, },
@@ -91,6 +92,7 @@
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.2.0", "@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2", "@babel/code-frame": "^7.26.2",
@@ -1767,6 +1769,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@@ -1790,6 +1793,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1874,6 +1878,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@@ -2221,6 +2226,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@@ -2871,6 +2877,50 @@
"@hapi/hoek": "^9.0.0" "@hapi/hoek": "^9.0.0"
} }
}, },
"node_modules/@intlify/core-base": {
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
"license": "MIT",
"dependencies": {
"@intlify/message-compiler": "9.14.5",
"@intlify/shared": "9.14.5"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
"license": "MIT",
"dependencies": {
"@intlify/shared": "9.14.5",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8", "version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
@@ -3222,6 +3272,7 @@
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/lodash": "*" "@types/lodash": "*"
} }
@@ -3648,6 +3699,7 @@
"integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==", "integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/helper-compilation-targets": "^7.12.16", "@babel/helper-compilation-targets": "^7.12.16",
"@soda/friendly-errors-webpack-plugin": "^1.8.0", "@soda/friendly-errors-webpack-plugin": "^1.8.0",
@@ -4386,6 +4438,7 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
@@ -4912,6 +4965,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001688", "caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73", "electron-to-chromium": "^1.5.73",
@@ -5718,6 +5772,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@@ -5732,6 +5787,7 @@
"integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"icss-utils": "^5.1.0", "icss-utils": "^5.1.0",
"postcss": "^8.4.33", "postcss": "^8.4.33",
@@ -5820,6 +5876,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@@ -8467,13 +8524,15 @@
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/lodash-unified": { "node_modules/lodash-unified": {
"version": "1.0.3", "version": "1.0.3",
@@ -8910,6 +8969,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@@ -9784,6 +9844,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.8", "nanoid": "^3.3.8",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@@ -10712,6 +10773,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@@ -12880,6 +12942,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@@ -13269,6 +13332,7 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
"integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.13", "@vue/compiler-dom": "3.5.13",
"@vue/compiler-sfc": "3.5.13", "@vue/compiler-sfc": "3.5.13",
@@ -13292,6 +13356,26 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/vue-i18n": {
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
"license": "MIT",
"dependencies": {
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-loader": { "node_modules/vue-loader": {
"version": "17.4.2", "version": "17.4.2",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz", "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz",
@@ -13431,6 +13515,7 @@
"integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/eslint-scope": "^3.7.7", "@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6", "@types/estree": "^1.0.6",
@@ -13567,6 +13652,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@@ -13684,6 +13770,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@@ -13778,6 +13865,7 @@
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=10.13.0" "node": ">=10.13.0"
} }
@@ -13795,6 +13883,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",

View File

@@ -14,6 +14,7 @@
"pinia": "^3.0.3", "pinia": "^3.0.3",
"qwebchannel": "^6.2.0", "qwebchannel": "^6.2.0",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-i18n": "^9.14.5",
"vue-router": "^4.0.3", "vue-router": "^4.0.3",
"vuex": "^4.0.0" "vuex": "^4.0.0"
}, },

View File

@@ -18,14 +18,16 @@ export function tkaccountuseinfo(accountName) {
return getAxios({ url: `/api/common/accountCount?accountName=${accountName}` }) return getAxios({ url: `/api/common/accountCount?accountName=${accountName}` })
} }
export function tokenVerification() {
return getAxios({ url: `/api/common/health` })
}
export function tkhostdata(data) { export function tkhostdata(data) {
return postAxios({ url: '/api/big-brother/page', data }) return postAxios({ url: '/api/big-brother/page', data })
} }
export function apiGetCart() { export function apiGetCart() {
return getAxios({ url: '/cgi-bin/cart/latest' }) return getAxios({ url: '/cgi-bin/cart/latest' })
} }

BIN
src/assets/wifi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

26
src/i18n/index.js Normal file
View File

@@ -0,0 +1,26 @@
import { createI18n } from 'vue-i18n'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import en from 'element-plus/dist/locale/en.mjs'
// 导入语言文件
import zh from './lang/zh-CN'
import enLang from './lang/en-US'
// 创建 i18n 实例
const i18n = createI18n({
legacy: false, // 使用 Composition API
locale: localStorage.getItem('language') || 'zh-CN', // 默认语言
fallbackLocale: 'zh-CN', // 备用语言
messages: {
'zh-CN': zh,
'en-US': enLang
}
})
// Element Plus 语言配置
export const elementPlusLocale = {
'zh-CN': zhCn,
'en-US': en
}
export default i18n

354
src/i18n/lang/en-US.js Normal file
View File

@@ -0,0 +1,354 @@
export default {
common: {
networkSettings: 'Network Settings',
language: 'Language',
simplifiedChinese: 'Simplified Chinese',
english: 'English',
accountLogin: 'Account Login',
tenantName: 'Tenant Name',
account: 'Account',
password: 'Password',
login: 'Login',
version: 'Version',
expirationtime: 'Expiration Time',
},
hostsList: {
// 顶部筛选
filterPrivateUsers: 'Filter Private Users',
minCoins: 'Min Coins',
maxCoins: 'Max Coins',
minLevel: 'Min Level',
maxLevel: 'Max Level',
specifiedRooms: 'Specified Rooms',
specifyRooms: 'Specify Rooms',
total: 'Total',
valid: 'Valid',
reset: 'Reset',
start: 'Start',
end: 'End',
// 第二行筛选
selectCountry: 'Select Country',
bigBrotherId: 'Big Brother ID',
search: 'Search',
exportExcel: 'Export Excel Data',
moreFilters: 'More Filters',
openTikTok: 'Open TikTok Login',
currentNetwork: 'Current Network',
runningTime: 'Running Time',
// 表格列
id: 'Id',
hostId: 'Host ID',
userId: 'User ID',
level: 'Level',
fansLevel: 'Fan Club Level',
coins: 'Coins',
totalGiftCoins: 'Total Gift Coins',
region: 'Region',
followerCount: 'Followers',
followingCount: 'Following',
createTime: 'Created Time',
// 更多筛选弹窗
time: 'Time',
startTime: 'Start Time',
endTime: 'End Time',
selectTime: 'Select Query Time',
minValue: 'Min Value',
maxValue: 'Max Value',
enterMinValue: 'Enter Min Value',
enterMaxValue: 'Enter Max Value',
sort: 'Sort',
sortType: 'Sort Type',
sortOrder: 'Ascending/Descending',
pleaseSelect: 'Please Select',
ascending: 'Ascending',
descending: 'Descending',
confirm: 'Confirm',
cancel: 'Cancel',
// 指定直播间弹窗
cancelSpecify: 'Cancel Specify Rooms',
specifyReset: 'Reset',
specifyConfirm: 'Confirm',
specifyStart: 'Start',
enterRoomIds: 'Enter room IDs, separate multiple IDs with Enter key',
enterRoomId: 'Please enter room ID',
// 网络问题弹窗
networkFailed:
'Network connection failed, unable to access the network. Please check network settings.',
// 复制提示
noContentToCopy: 'No content to copy',
copySuccess: 'Copied successfully',
copyFailed: 'Copy failed',
// 任务状态 loading
stopping: 'Stopping...',
starting: 'Starting...',
// 获取国家失败弹窗
pleaseEnterCountryName: 'Please enter the country name in Chinese',
getCountryFailed: 'Failed to get country',
},
// ==== 新增:国家名称国际化 ====
countries: {
AD: "Andorra",
AE: "United Arab Emirates",
AF: "Afghanistan",
AG: "Antigua and Barbuda",
AI: "Anguilla",
AL: "Albania",
AM: "Armenia",
AO: "Angola",
AQ: "Antarctica",
AR: "Argentina",
AS: "American Samoa",
AT: "Austria",
AU: "Australia",
AU1: "Australia",
AW: "Aruba",
AX: "Åland Islands",
AZ: "Azerbaijan",
BA: "Bosnia and Herzegovina",
BB: "Barbados",
BD: "Bangladesh",
BE: "Belgium",
BF: "Burkina Faso",
BG: "Bulgaria",
BH: "Bahrain",
BI: "Burundi",
BJ: "Benin",
BL: "Saint Barthélemy",
BM: "Bermuda",
BN: "Brunei Darussalam",
BO: "Bolivia",
BQ: "Bonaire, Sint Eustatius and Saba",
BR: "Brazil",
BS: "Bahamas",
BT: "Bhutan",
BV: "Bouvet Island",
BW: "Botswana",
BY: "Belarus",
BZ: "Belize",
CA: "Canada",
CA1: "Canada",
CC: "Cocos (Keeling) Islands",
CD: "Democratic Republic of the Congo",
CF: "Central African Republic",
CG: "Republic of the Congo",
CH: "Switzerland",
CI: "Côte d'Ivoire",
CK: "Cook Islands",
CL: "Chile",
CM: "Cameroon",
CN: "China",
CO: "Colombia",
CR: "Costa Rica",
CU: "Cuba",
CV: "Cape Verde",
CW: "Curaçao",
CX: "Christmas Island",
CY: "Cyprus",
CZ: "Czech Republic",
DE: "Germany",
DG: "Diego Garcia",
DJ: "Djibouti",
DK: "Denmark",
DM: "Dominica",
DO: "Dominican Republic",
DZ: "Algeria",
EC: "Ecuador",
EE: "Estonia",
EG: "Egypt",
EH: "Western Sahara",
ER: "Eritrea",
ES: "Spain",
ET: "Ethiopia",
FI: "Finland",
FJ: "Fiji",
FK: "Falkland Islands",
FM: "Micronesia",
FO: "Faroe Islands",
FR: "France",
GA: "Gabon",
GB: "United Kingdom",
GD: "Grenada",
GE: "Georgia",
GF: "French Guiana",
GG: "Guernsey",
GH: "Ghana",
GI: "Gibraltar",
GL: "Greenland",
GM: "Gambia",
GN: "Guinea",
GP: "Guadeloupe",
GQ: "Equatorial Guinea",
GR: "Greece",
GS: "South Georgia and the South Sandwich Islands",
GT: "Guatemala",
GU: "Guam",
GW: "Guinea-Bissau",
GY: "Guyana",
HK: "Hong Kong SAR China",
HM: "Heard Island and McDonald Islands",
HN: "Honduras",
HR: "Croatia",
HT: "Haiti",
HU: "Hungary",
ID: "Indonesia",
IE: "Ireland",
IL: "Israel",
IM: "Isle of Man",
IN: "India",
IO: "British Indian Ocean Territory",
IQ: "Iraq",
IR: "Iran",
IS: "Iceland",
IT: "Italy",
JE: "Jersey",
JM: "Jamaica",
JO: "Jordan",
JP: "Japan",
JP1: "Japan",
KE: "Kenya",
KG: "Kyrgyzstan",
KH: "Cambodia",
KI: "Kiribati",
KM: "Comoros",
KN: "Saint Kitts and Nevis",
KP: "North Korea",
KR: "South Korea",
KR1: "South Korea",
KR1_UXWAUDIT: "South Korea",
KW: "Kuwait",
KY: "Cayman Islands",
KZ: "Kazakhstan",
LA: "Laos",
LB: "Lebanon",
LC: "Saint Lucia",
LI: "Liechtenstein",
LK: "Sri Lanka",
LR: "Liberia",
LS: "Lesotho",
LT: "Lithuania",
LU: "Luxembourg",
LV: "Latvia",
LY: "Libya",
MA: "Morocco",
MC: "Monaco",
MD: "Moldova",
ME: "Montenegro",
MF: "Saint Martin",
MG: "Madagascar",
MH: "Marshall Islands",
MK: "North Macedonia",
ML: "Mali",
MM: "Myanmar",
MN: "Mongolia",
MO: "Macao SAR China",
MP: "Northern Mariana Islands",
MQ: "Martinique",
MR: "Mauritania",
MS: "Montserrat",
MT: "Malta",
MU: "Mauritius",
MV: "Maldives",
MW: "Malawi",
MX: "Mexico",
MY: "Malaysia",
MZ: "Mozambique",
NA: "Namibia",
NC: "New Caledonia",
NE: "Niger",
NF: "Norfolk Island",
NG: "Nigeria",
NI: "Nicaragua",
NL: "Netherlands",
NO: "Norway",
NP: "Nepal",
NR: "Nauru",
NU: "Niue",
NZ: "New Zealand",
OM: "Oman",
PA: "Panama",
PE: "Peru",
PF: "French Polynesia",
PG: "Papua New Guinea",
PH: "Philippines",
PK: "Pakistan",
PL: "Poland",
PM: "Saint Pierre and Miquelon",
PN: "Pitcairn Islands",
PR: "Puerto Rico",
PS: "Palestine",
PT: "Portugal",
PW: "Palau",
PY: "Paraguay",
QA: "Qatar",
RE: "Réunion",
RO: "Romania",
RS: "Serbia",
RU: "Russia",
RW: "Rwanda",
SA: "Saudi Arabia",
SB: "Solomon Islands",
SC: "Seychelles",
SD: "Sudan",
SE: "Sweden",
SG: "Singapore",
SI: "Slovenia",
SJ: "Svalbard and Jan Mayen",
SK: "Slovakia",
SL: "Sierra Leone",
SM: "San Marino",
SN: "Senegal",
SO: "Somalia",
SR: "Suriname",
SS: "South Sudan",
ST: "Sao Tome and Principe",
SV: "El Salvador",
SX: "Sint Maarten",
SY: "Syria",
SZ: "Eswatini",
TC: "Turks and Caicos Islands",
TD: "Chad",
TF: "French Southern Territories",
TG: "Togo",
TH: "Thailand",
TJ: "Tajikistan",
TK: "Tokelau",
TL: "Timor-Leste",
TM: "Turkmenistan",
TN: "Tunisia",
TO: "Tonga",
TR: "Turkey",
TT: "Trinidad and Tobago",
TV: "Tuvalu",
TW: "Taiwan",
TZ: "Tanzania",
UA: "Ukraine",
UG: "Uganda",
UM: "United States Minor Outlying Islands",
US: "United States",
UY: "Uruguay",
UZ: "Uzbekistan",
VA: "Vatican City",
VC: "Saint Vincent and the Grenadines",
VE: "Venezuela",
VG: "British Virgin Islands",
VI: "U.S. Virgin Islands",
VN: "Vietnam",
VN1: "Vietnam",
VU: "Vanuatu",
WS: "Samoa",
YE: "Yemen",
YT: "Mayotte",
ZA: "South Africa",
ZM: "Zambia",
ZW: "Zimbabwe"
}
}

347
src/i18n/lang/zh-CN.js Normal file
View File

@@ -0,0 +1,347 @@
export default {
common: {
networkSettings: '网络设置',
language: '语言',
simplifiedChinese: '简体中文',
english: 'English',
accountLogin: '账号登录',
tenantName: '租户名称',
account: '账号',
password: '密码',
login: '登录',
version: '版本号',
expirationtime: '过期时间',
},
hostsList: {
filterPrivateUsers: '过滤隐私用户',
minCoins: '最小金币',
maxCoins: '最大金币',
minLevel: '最小等级',
maxLevel: '最大等级',
specifiedRooms: '已指定直播间',
specifyRooms: '指定直播间',
total: '总数',
valid: '有效数',
reset: '重置',
start: '开始',
end: '结束',
selectCountry: '选择国家',
bigBrotherId: '大哥id',
search: '查询',
exportExcel: '导出Excel数据',
moreFilters: '更多筛选',
openTikTok: '打开 TikTok 登录',
currentNetwork: '当前网络',
runningTime: '运行时间',
id: 'Id',
hostId: '所在直播间主播id',
time: '时间',
startTime: '开始时间',
endTime: '结束时间',
selectTime: '选择查询时间',
minValue: '最小值',
maxValue: '最大值',
enterMinValue: '请输入最小值',
enterMaxValue: '请输入最大值',
sort: '排序',
sortType: '排序类型',
sortOrder: '升序/降序',
pleaseSelect: '请选择',
ascending: '升序',
descending: '降序',
confirm: '确认',
cancel: '取消',
cancelSpecify: '取消指定直播间',
specifyReset: '重置',
specifyConfirm: '确认',
specifyStart: '开始',
networkFailed: '网络连接失败,无法访问网络,请查看网络设置。',
enterRoomIds: '请输入直播间id多个id用回车键隔开',
// ==== 新增:表格列、排序使用 ====
userId: '用户id',
level: '等级',
fansLevel: '粉丝团等级',
coins: '打赏的金币',
totalGiftCoins: '打赏金币总和',
region: '地区',
followerCount: '粉丝数',
followingCount: '关注数',
createTime: '创建时间',
// ==== 新增:复制提示 ====
noContentToCopy: '无内容可复制',
copySuccess: '复制成功',
copyFailed: '复制失败',
// ==== 新增:获取国家失败弹窗 ====
pleaseEnterCountryName: '请输入要获取的国家',
getCountryFailed: '获取国家失败',
// ==== 新增loading 提示 ====
stopping: '正在停止...',
starting: '正在启动...',
// ==== 新增单个直播间id校验提示 ====
enterRoomId: '请输入直播间id',
},
// ==== 新增:国家名称国际化 ====
countries: {
AD: "安道尔",
AE: "阿拉伯联合酋长国",
AF: "阿富汗",
AG: "安提瓜和巴布达",
AI: "安圭拉",
AL: "阿尔巴尼亚",
AM: "亚美尼亚",
AO: "安哥拉",
AQ: "南极洲",
AR: "阿根廷",
AS: "美属萨摩亚",
AT: "奥地利",
AU: "澳大利亚",
AU1: "澳大利亚",
AW: "阿鲁巴",
AX: "奥兰群岛",
AZ: "阿塞拜疆",
BA: "波斯尼亚和黑塞哥维那",
BB: "巴巴多斯",
BD: "孟加拉国",
BE: "比利时",
BF: "布基纳法索",
BG: "保加利亚",
BH: "巴林",
BI: "布隆迪",
BJ: "贝宁",
BL: "圣巴泰勒米",
BM: "百慕大群岛",
BN: "文莱达鲁萨兰国",
BO: "玻利维亚",
BQ: "博奈尔、圣尤斯特歇斯和萨巴",
BR: "巴西",
BS: "巴哈马",
BT: "不丹",
BV: "布韦岛",
BW: "博茨瓦纳",
BY: "白俄罗斯",
BZ: "伯利兹",
CA: "加拿大",
CA1: "加拿大",
CC: "科科斯(基林)群岛",
CD: "刚果民主共和国",
CF: "中非共和国",
CG: "刚果共和国",
CH: "瑞士",
CI: "科特迪瓦",
CK: "库克群岛",
CL: "智利",
CM: "喀麦隆",
CN: "中国",
CO: "哥伦比亚",
CR: "哥斯达黎加",
CU: "古巴",
CV: "佛得角",
CW: "库拉索",
CX: "圣诞岛",
CY: "塞浦路斯",
CZ: "捷克共和国",
DE: "德国",
DG: "迪戈加西亚岛",
DJ: "吉布提",
DK: "丹麦",
DM: "多米尼克",
DO: "多米尼加共和国",
DZ: "阿尔及利亚",
EC: "厄瓜多尔",
EE: "爱沙尼亚",
EG: "埃及",
EH: "西撒哈拉",
ER: "厄立特里亚",
ES: "西班牙",
ET: "埃塞俄比亚",
FI: "芬兰",
FJ: "斐济",
FK: "福克兰群岛",
FM: "密克罗尼西亚",
FO: "法罗群岛",
FR: "法国",
GA: "加蓬",
GB: "英国",
GD: "格林纳达",
GE: "格鲁吉亚",
GF: "法属圭亚那",
GG: "根西岛",
GH: "加纳",
GI: "直布罗陀",
GL: "格陵兰",
GM: "冈比亚",
GN: "几内亚",
GP: "瓜德罗普",
GQ: "赤道几内亚",
GR: "希腊",
GS: "南乔治亚和南桑德威奇群岛",
GT: "危地马拉",
GU: "关岛",
GW: "几内亚比绍",
GY: "圭亚那",
HK: "中国香港特别行政区",
HM: "赫德岛和麦克唐纳群岛",
HN: "洪都拉斯",
HR: "克罗地亚",
HT: "海地",
HU: "匈牙利",
ID: "印度尼西亚",
IE: "爱尔兰",
IL: "以色列",
IM: "马恩岛",
IN: "印度",
IO: "英属印度洋领地",
IQ: "伊拉克",
IR: "伊朗",
IS: "冰岛",
IT: "意大利",
JE: "泽西岛",
JM: "牙买加",
JO: "约旦",
JP: "日本",
JP1: "日本",
KE: "肯尼亚",
KG: "吉尔吉斯斯坦",
KH: "柬埔寨",
KI: "基里巴斯",
KM: "科摩罗",
KN: "圣基茨和尼维斯",
KP: "朝鲜",
KR: "韩国",
KR1: "韩国",
KR1_UXWAUDIT: "韩国",
KW: "科威特",
KY: "开曼群岛",
KZ: "哈萨克斯坦",
LA: "老挝",
LB: "黎巴嫩",
LC: "圣卢西亚",
LI: "列支敦士登",
LK: "斯里兰卡",
LR: "利比里亚",
LS: "莱索托",
LT: "立陶宛",
LU: "卢森堡",
LV: "拉脱维亚",
LY: "利比亚",
MA: "摩洛哥",
MC: "摩纳哥",
MD: "摩尔多瓦",
ME: "黑山",
MF: "圣马丁",
MG: "马达加斯加",
MH: "马绍尔群岛",
MK: "北马其顿",
ML: "马里",
MM: "缅甸",
MN: "蒙古",
MO: "中国澳门特别行政区",
MP: "北马里亚纳群岛",
MQ: "马提尼克",
MR: "毛里塔尼亚",
MS: "蒙特塞拉特",
MT: "马耳他",
MU: "毛里求斯",
MV: "马尔代夫",
MW: "马拉维",
MX: "墨西哥",
MY: "马来西亚",
MZ: "莫桑比克",
NA: "纳米比亚",
NC: "新喀里多尼亚",
NE: "尼日尔",
NF: "诺福克岛",
NG: "尼日利亚",
NI: "尼加拉瓜",
NL: "荷兰",
NO: "挪威",
NP: "尼泊尔",
NR: "瑙鲁",
NU: "纽埃",
NZ: "新西兰",
OM: "阿曼",
PA: "巴拿马",
PE: "秘鲁",
PF: "法属玻利尼西亚",
PG: "巴布亚新几内亚",
PH: "菲律宾",
PK: "巴基斯坦",
PL: "波兰",
PM: "圣皮埃尔和密克隆群岛",
PN: "皮特凯恩群岛",
PR: "波多黎各",
PS: "巴勒斯坦",
PT: "葡萄牙",
PW: "帕劳",
PY: "巴拉圭",
QA: "卡塔尔",
RE: "留尼汪",
RO: "罗马尼亚",
RS: "塞尔维亚",
RU: "俄罗斯",
RW: "卢旺达",
SA: "沙特阿拉伯",
SB: "索罗门群岛",
SC: "塞舌尔",
SD: "苏丹",
SE: "瑞典",
SG: "新加坡",
SI: "斯洛文尼亚",
SJ: "斯瓦尔巴和扬马延",
SK: "斯洛伐克",
SL: "塞拉利昂",
SM: "圣马利诺",
SN: "塞内加尔",
SO: "索马里",
SR: "苏里南",
SS: "南苏丹",
ST: "圣多美和普林西比",
SV: "萨尔瓦多",
SX: "荷属圣马丁",
SY: "叙利亚",
SZ: "斯威士兰",
TC: "特克斯和凯科斯群岛",
TD: "乍得",
TF: "法属南部领地",
TG: "多哥",
TH: "泰国",
TJ: "塔吉克斯坦",
TK: "托克劳群岛",
TL: "东帝汶",
TM: "土库曼斯坦",
TN: "突尼斯",
TO: "汤加",
TR: "土耳其",
TT: "特立尼达和多巴哥",
TV: "图瓦卢",
TW: "台湾",
TZ: "坦桑尼亚",
UA: "乌克兰",
UG: "乌干达",
UM: "美国本土外小岛屿",
US: "美国",
UY: "乌拉圭",
UZ: "乌兹别克斯坦",
VA: "梵蒂冈",
VC: "圣文森特",
VE: "委内瑞拉",
VG: "英属维尔京群岛",
VI: "美属维尔京群岛",
VN: "越南",
VN1: "越南",
VU: "瓦努阿图",
WS: "萨摩亚",
YE: "也门",
YT: "马约特岛",
ZA: "南非",
ZM: "赞比亚",
ZW: "津巴布韦"
}
}

View File

@@ -5,14 +5,18 @@ import store from './store'
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import ElementPlus from 'element-plus' import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' import 'element-plus/dist/index.css'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'; // 引入中文语言包 import i18n, { elementPlusLocale } from './i18n'
const app = createApp(App); const app = createApp(App);
// 获取当前语言
const currentLocale = localStorage.getItem('language') || 'zh-CN'
app.use(ElementPlus, { app.use(ElementPlus, {
locale: zhCn, // 配置中文 locale: elementPlusLocale[currentLocale] // 动态配置Element Plus语言
}); });
app.use(ElementPlus) // 注册 ElementPlus app.use(i18n); // 注册 i18n
app.use(createPinia()); // 注册 Pinia app.use(createPinia()); // 注册 Pinia
app.use(store); // 注册 store app.use(store); // 注册 store
app.use(router); // 注册 router app.use(router); // 注册 router

View File

@@ -22,16 +22,16 @@ const { stopScript } = usePythonBridge();
let baseURL = '' let baseURL = ''
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
// 生产环境 // 生产环境
// baseURL = "https://api.tkpage.yolozs.com" baseURL = "https://crawlclient.api.yolozs.com"
baseURL = "http://47.79.98.113:8101" // baseURL = "http://47.79.98.113:8101"
// baseURL = "http://192.168.0.103:8085/" // baseURL = "http://192.168.0.103:8085/"
// baseURL = "http://192.168.1.144:8101" // baseURL = "http://192.168.1.144:8101"
} else { } else {
// 测试环境 // 测试环境
// baseURL = "http://120.26.251.180:8085/" // baseURL = "http://120.26.251.180:8085/"
// 开发环境 // 开发环境
// baseURL = "https://api.tkpage.yolozs.com" baseURL = "https://crawlclient.api.yolozs.com"
baseURL = "http://47.79.98.113:8101" // baseURL = "http://47.79.98.113:8101"
// baseURL = "http://192.168.1.144:8101" // baseURL = "http://192.168.1.144:8101"
// baseURL = "http://api.tkpage.vvtiktok.cn" // baseURL = "http://api.tkpage.vvtiktok.cn"
} }

View File

@@ -1,261 +1,270 @@
// country-utils.js // country-utils.js
import i18n from '../i18n'
// 国家代码枚举(只包含代码,不包含名称)
export const CountryCode = { export const CountryCode = {
AD: "安道尔", AD: "AD",
AE: "阿拉伯联合酋长国", AE: "AE",
AF: "阿富汗", AF: "AF",
AG: "安提瓜和巴布达", AG: "AG",
AI: "安圭拉", AI: "AI",
AL: "阿尔巴尼亚", AL: "AL",
AM: "亚美尼亚", AM: "AM",
AO: "安哥拉", AO: "AO",
AQ: "南极洲", AQ: "AQ",
AR: "阿根廷", AR: "AR",
AS: "美属萨摩亚", AS: "AS",
AT: "奥地利", AT: "AT",
AU: "澳大利亚", AU: "AU",
AU1: "澳大利亚", AU1: "AU1",
AW: "阿鲁巴", AW: "AW",
AX: "奥兰群岛", AX: "AX",
AZ: "阿塞拜疆", AZ: "AZ",
BA: "波斯尼亚和黑塞哥维那", BA: "BA",
BB: "巴巴多斯", BB: "BB",
BD: "孟加拉国", BD: "BD",
BE: "比利时", BE: "BE",
BF: "布基纳法索", BF: "BF",
BG: "保加利亚", BG: "BG",
BH: "巴林", BH: "BH",
BI: "布隆迪", BI: "BI",
BJ: "贝宁", BJ: "BJ",
BL: "圣巴泰勒米", BL: "BL",
BM: "百慕大群岛", BM: "BM",
BN: "文莱达鲁萨兰国", BN: "BN",
BO: "玻利维亚", BO: "BO",
BQ: "博奈尔、圣尤斯特歇斯和萨巴", BQ: "BQ",
BR: "巴西", BR: "BR",
BS: "巴哈马", BS: "BS",
BT: "不丹", BT: "BT",
BV: "布韦岛", BV: "BV",
BW: "博茨瓦纳", BW: "BW",
BY: "白俄罗斯", BY: "BY",
BZ: "伯利兹", BZ: "BZ",
CA: "加拿大", CA: "CA",
CA1: "加拿大", CA1: "CA1",
CC: "科科斯(基林)群岛", CC: "CC",
CD: "刚果民主共和国", CD: "CD",
CF: "中非共和国", CF: "CF",
CG: "刚果共和国", CG: "CG",
CH: "瑞士", CH: "CH",
CI: "科特迪瓦", CI: "CI",
CK: "库克群岛", CK: "CK",
CL: "智利", CL: "CL",
CM: "喀麦隆", CM: "CM",
CN: "中国", CN: "CN",
CO: "哥伦比亚", CO: "CO",
CR: "哥斯达黎加", CR: "CR",
CU: "古巴", CU: "CU",
CV: "佛得角", CV: "CV",
CW: "库拉索", CW: "CW",
CX: "圣诞岛", CX: "CX",
CY: "塞浦路斯", CY: "CY",
CZ: "捷克共和国", CZ: "CZ",
DE: "德国", DE: "DE",
DG: "迪戈加西亚岛", DG: "DG",
DJ: "吉布提", DJ: "DJ",
DK: "丹麦", DK: "DK",
DM: "多米尼克", DM: "DM",
DO: "多米尼加共和国", DO: "DO",
DZ: "阿尔及利亚", DZ: "DZ",
EC: "厄瓜多尔", EC: "EC",
EE: "爱沙尼亚", EE: "EE",
EG: "埃及", EG: "EG",
EH: "西撒哈拉", EH: "EH",
ER: "厄立特里亚", ER: "ER",
ES: "西班牙", ES: "ES",
ET: "埃塞俄比亚", ET: "ET",
FI: "芬兰", FI: "FI",
FJ: "斐济", FJ: "FJ",
FK: "福克兰群岛", FK: "FK",
FM: "密克罗尼西亚", FM: "FM",
FO: "法罗群岛", FO: "FO",
FR: "法国", FR: "FR",
GA: "加蓬", GA: "GA",
GB: "英国", GB: "GB",
GD: "格林纳达", GD: "GD",
GE: "格鲁吉亚", GE: "GE",
GF: "法属圭亚那", GF: "GF",
GG: "根西岛", GG: "GG",
GH: "加纳", GH: "GH",
GI: "直布罗陀", GI: "GI",
GL: "格陵兰", GL: "GL",
GM: "冈比亚", GM: "GM",
GN: "几内亚", GN: "GN",
GP: "瓜德罗普", GP: "GP",
GQ: "赤道几内亚", GQ: "GQ",
GR: "希腊", GR: "GR",
GS: "南乔治亚和南桑德威奇群岛", GS: "GS",
GT: "危地马拉", GT: "GT",
GU: "关岛", GU: "GU",
GW: "几内亚比绍", GW: "GW",
GY: "圭亚那", GY: "GY",
HK: "中国香港特别行政区", HK: "HK",
HM: "赫德岛和麦克唐纳群岛", HM: "HM",
HN: "洪都拉斯", HN: "HN",
HR: "克罗地亚", HR: "HR",
HT: "海地", HT: "HT",
HU: "匈牙利", HU: "HU",
ID: "印度尼西亚", ID: "ID",
IE: "爱尔兰", IE: "IE",
IL: "以色列", IL: "IL",
IM: "马恩岛", IM: "IM",
IN: "印度", IN: "IN",
IO: "英属印度洋领地", IO: "IO",
IQ: "伊拉克", IQ: "IQ",
IR: "伊朗", IR: "IR",
IS: "冰岛", IS: "IS",
IT: "意大利", IT: "IT",
JE: "泽西岛", JE: "JE",
JM: "牙买加", JM: "JM",
JO: "约旦", JO: "JO",
JP: "日本", JP: "JP",
JP1: "日本", JP1: "JP1",
KE: "肯尼亚", KE: "KE",
KG: "吉尔吉斯斯坦", KG: "KG",
KH: "柬埔寨", KH: "KH",
KI: "基里巴斯", KI: "KI",
KM: "科摩罗", KM: "KM",
KN: "圣基茨和尼维斯", KN: "KN",
KP: "朝鲜", KP: "KP",
KR: "韩国", KR: "KR",
KR1: "韩国", KR1: "KR1",
KR1_UXWAUDIT: "韩国", KR1_UXWAUDIT: "KR1_UXWAUDIT",
KW: "科威特", KW: "KW",
KY: "开曼群岛", KY: "KY",
KZ: "哈萨克斯坦", KZ: "KZ",
LA: "老挝", LA: "LA",
LB: "黎巴嫩", LB: "LB",
LC: "圣卢西亚", LC: "LC",
LI: "列支敦士登", LI: "LI",
LK: "斯里兰卡", LK: "LK",
LR: "利比里亚", LR: "LR",
LS: "莱索托", LS: "LS",
LT: "立陶宛", LT: "LT",
LU: "卢森堡", LU: "LU",
LV: "拉脱维亚", LV: "LV",
LY: "利比亚", LY: "LY",
MA: "摩洛哥", MA: "MA",
MC: "摩纳哥", MC: "MC",
MD: "摩尔多瓦", MD: "MD",
ME: "黑山", ME: "ME",
MF: "圣马丁", MF: "MF",
MG: "马达加斯加", MG: "MG",
MH: "马绍尔群岛", MH: "MH",
MK: "北马其顿", MK: "MK",
ML: "马里", ML: "ML",
MM: "缅甸", MM: "MM",
MN: "蒙古", MN: "MN",
MO: "中国澳门特别行政区", MO: "MO",
MP: "北马里亚纳群岛", MP: "MP",
MQ: "马提尼克", MQ: "MQ",
MR: "毛里塔尼亚", MR: "MR",
MS: "蒙特塞拉特", MS: "MS",
MT: "马耳他", MT: "MT",
MU: "毛里求斯", MU: "MU",
MV: "马尔代夫", MV: "MV",
MW: "马拉维", MW: "MW",
MX: "墨西哥", MX: "MX",
MY: "马来西亚", MY: "MY",
MZ: "莫桑比克", MZ: "MZ",
NA: "纳米比亚", NA: "NA",
NC: "新喀里多尼亚", NC: "NC",
NE: "尼日尔", NE: "NE",
NF: "诺福克岛", NF: "NF",
NG: "尼日利亚", NG: "NG",
NI: "尼加拉瓜", NI: "NI",
NL: "荷兰", NL: "NL",
NO: "挪威", NO: "NO",
NP: "尼泊尔", NP: "NP",
NR: "瑙鲁", NR: "NR",
NU: "纽埃", NU: "NU",
NZ: "新西兰", NZ: "NZ",
OM: "阿曼", OM: "OM",
PA: "巴拿马", PA: "PA",
PE: "秘鲁", PE: "PE",
PF: "法属玻利尼西亚", PF: "PF",
PG: "巴布亚新几内亚", PG: "PG",
PH: "菲律宾", PH: "PH",
PK: "巴基斯坦", PK: "PK",
PL: "波兰", PL: "PL",
PM: "圣皮埃尔和密克隆群岛", PM: "PM",
PN: "皮特凯恩群岛", PN: "PN",
PR: "波多黎各", PR: "PR",
PS: "巴勒斯坦", PS: "PS",
PT: "葡萄牙", PT: "PT",
PW: "帕劳", PW: "PW",
PY: "巴拉圭", PY: "PY",
QA: "卡塔尔", QA: "QA",
RE: "留尼汪", RE: "RE",
RO: "罗马尼亚", RO: "RO",
RS: "塞尔维亚", RS: "RS",
RU: "俄罗斯", RU: "RU",
RW: "卢旺达", RW: "RW",
SA: "沙特阿拉伯", SA: "SA",
SB: "索罗门群岛", SB: "SB",
SC: "塞舌尔", SC: "SC",
SD: "苏丹", SD: "SD",
SE: "瑞典", SE: "SE",
SG: "新加坡", SG: "SG",
SI: "斯洛文尼亚", SI: "SI",
SJ: "斯瓦尔巴和扬马延", SJ: "SJ",
SK: "斯洛伐克", SK: "SK",
SL: "塞拉利昂", SL: "SL",
SM: "圣马利诺", SM: "SM",
SN: "塞内加尔", SN: "SN",
SO: "索马里", SO: "SO",
SR: "苏里南", SR: "SR",
SS: "南苏丹", SS: "SS",
ST: "圣多美和普林西比", ST: "ST",
SV: "萨尔瓦多", SV: "SV",
SX: "荷属圣马丁", SX: "SX",
SY: "叙利亚", SY: "SY",
SZ: "斯威士兰", SZ: "SZ",
TC: "特克斯和凯科斯群岛", TC: "TC",
TD: "乍得", TD: "TD",
TF: "法属南部领地", TF: "TF",
TG: "多哥", TG: "TG",
TH: "泰国", TH: "TH",
TJ: "塔吉克斯坦", TJ: "TJ",
TK: "托克劳群岛", TK: "TK",
TL: "东帝汶", TL: "TL",
TM: "土库曼斯坦", TM: "TM",
TN: "突尼斯", TN: "TN",
TO: "汤加", TO: "TO",
TR: "土耳其", TR: "TR",
TT: "特立尼达和多巴哥", TT: "TT",
TV: "图瓦卢", TV: "TV",
TW: "台湾", TW: "TW",
TZ: "坦桑尼亚", TZ: "TZ",
UA: "乌克兰", UA: "UA",
UG: "乌干达", UG: "UG",
UM: "美国本土外小岛屿", UM: "UM",
US: "美国", US: "US",
UY: "乌拉圭", UY: "UY",
UZ: "乌兹别克斯坦", UZ: "UZ",
VA: "梵蒂冈", VA: "VA",
VC: "圣文森特", VC: "VC",
VE: "委内瑞拉", VE: "VE",
VG: "英属维尔京群岛", VG: "VG",
VI: "美属维尔京群岛", VI: "VI",
VN: "越南", VN: "VN",
VN1: "越南", VN1: "VN1",
VU: "瓦努阿图", VU: "VU",
WS: "萨摩亚", WS: "WS",
YE: "也门", YE: "YE",
YT: "马约特岛", YT: "YT",
ZA: "南非", ZA: "ZA",
ZM: "赞比亚", ZM: "ZM",
ZW: "津巴布韦" ZW: "ZW"
}; };
export function getCountryName(code) { export function getCountryName(code) {
return CountryCode[code] || null; if (!code) return null;
// 使用 i18n 获取国家名称翻译
const countryName = i18n.global.t(`countries.${code}`);
// 如果找不到翻译,返回 null
return countryName === `countries.${code}` ? null : countryName;
} }

View File

@@ -48,6 +48,32 @@ export function usePythonBridge() {
}); });
}; };
//设置储存数据
const storageSetInfos = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.storageSetInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//设置储存数据
const readSetInfos = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.readSetInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
// 查询获取大哥的数据 // 查询获取大哥的数据
const controlTask = (data) => { const controlTask = (data) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -120,6 +146,19 @@ export function usePythonBridge() {
} }
}); });
}; };
const setClipboards = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.setClipboard(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//跳转到主播页面 //跳转到主播页面
const givePyAnchorId = (id) => { const givePyAnchorId = (id) => {
console.log("id",id); console.log("id",id);
@@ -178,6 +217,9 @@ export function usePythonBridge() {
Specifystreaming, Specifystreaming,
setStorageStreamId, setStorageStreamId,
getStorageStreamId, getStorageStreamId,
openAnchorIdRooms openAnchorIdRooms,
storageSetInfos,
readSetInfos,
setClipboards
}; };
} }

View File

@@ -9,11 +9,26 @@
<div class="setup"> <div class="setup">
<div class="setup-item center-justify"> <div class="setup-item center-justify">
<div></div> <div></div>
<span> 网络设置 </span> <span>{{ $t('common.networkSettings') }}</span>
</div> </div>
<div class="setup-item center-justify"> <!-- <div class="setup-item center-justify"> -->
<div></div> <div class="">
<span> 简体中文 </span> <el-select
v-model="currentLanguage"
:placeholder="$t('common.language')"
@change="changeLanguage"
size="small"
style="width: 120px;"
>
<el-option
:label="$t('common.simplifiedChinese')"
value="zh-CN"
/>
<el-option
:label="$t('common.english')"
value="en-US"
/>
</el-select>
</div> </div>
</div> </div>
</div> </div>
@@ -28,7 +43,7 @@
<!-- From --> <!-- From -->
<div class="from"> <div class="from">
<div class="from-title center-justify"> <div class="from-title center-justify">
<div>账号登陆</div> <div>{{ $t('common.accountLogin') }}</div>
</div> </div>
<div class="from-input"> <div class="from-input">
@@ -38,7 +53,7 @@
<el-input <el-input
style="height: 25px" style="height: 25px"
v-model="formData.tenantName" v-model="formData.tenantName"
placeholder="租户名称" :placeholder="$t('common.tenantName')"
clearable clearable
@keyup.enter="onSubmit" @keyup.enter="onSubmit"
/> />
@@ -48,7 +63,7 @@
<el-input <el-input
style="height: 25px" style="height: 25px"
v-model="formData.userId" v-model="formData.userId"
placeholder="账号" :placeholder="$t('common.account')"
clearable clearable
@keyup.enter="onSubmit" @keyup.enter="onSubmit"
/> />
@@ -59,7 +74,7 @@
style="height: 25px" style="height: 25px"
v-model="formData.password" v-model="formData.password"
type="password" type="password"
placeholder="密码" :placeholder="$t('common.password')"
show-password show-password
@keyup.enter="onSubmit" @keyup.enter="onSubmit"
/> />
@@ -71,14 +86,14 @@
color="#8f7ee7" color="#8f7ee7"
type="primary" type="primary"
@click="onSubmit" @click="onSubmit"
>登录</el-button >{{ $t('common.login') }}</el-button
> >
</div> </div>
</el-form> </el-form>
</div> </div>
</div> </div>
</div> </div>
<div class="version center-justify">版本号{{ version }}</div> <div class="version center-justify">{{ $t('common.version') }}{{ version }}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -87,6 +102,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useI18n } from 'vue-i18n';
import { login, rentgetloginID } from "@/api/account"; import { login, rentgetloginID } from "@/api/account";
import { getToken, setToken, setUser, setUserPass, getUserPass } from "@/utils/storage"; import { getToken, setToken, setUser, setUserPass, getUserPass } from "@/utils/storage";
import { ElLoading } from "element-plus"; import { ElLoading } from "element-plus";
@@ -94,11 +110,15 @@ import { usePythonBridge } from "@/utils/pythonBridge";
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { tokenStore,UserStore } from '@/stores/notice' import { tokenStore,UserStore } from '@/stores/notice'
const { t, locale } = useI18n();
const tokenCache = tokenStore() const tokenCache = tokenStore()
const userCache = UserStore() const userCache = UserStore()
const { getVersion } = usePythonBridge(); const { getVersion } = usePythonBridge();
let version = ref("0.0.0"); let version = ref("0.0.0");
// 当前语言
const currentLanguage = ref(localStorage.getItem('language') || 'zh-CN');
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {
getVersion().then((res) => { getVersion().then((res) => {
@@ -117,6 +137,14 @@ async function getpassword(){
}; };
} }
// 切换语言
const changeLanguage = (lang) => {
locale.value = lang;
currentLanguage.value = lang;
localStorage.setItem('language', lang);
// 重新加载页面以应用Element Plus语言包
window.location.reload();
};
const router = useRouter(); const router = useRouter();
@@ -213,6 +241,7 @@ const onSubmit = () => {
.setup { .setup {
display: flex; display: flex;
align-items: center;
color: #fff; color: #fff;
.setup-item { .setup-item {

View File

@@ -1,19 +1,19 @@
<template> <template>
<div class="hostList"> <div class="hostList">
<div> <div>
<!-- --> <!-- 顶部条件 -->
<div style="display: flex"> <div style="display: flex">
<el-checkbox <el-checkbox
class="isFilter" class="isFilter"
v-model="queryFormData.isFilter" v-model="queryFormData.isFilter"
label="过滤隐私用户" :label="t('hostsList.filterPrivateUsers')"
size="large" size="large"
border border
/> />
<el-input <el-input
v-model="queryFormData.coinMin" v-model="queryFormData.coinMin"
placeholder="最小金币" :placeholder="t('hostsList.minCoins')"
size="large" size="large"
style="width: 180px" style="width: 180px"
type="number" type="number"
@@ -22,7 +22,7 @@
<el-input <el-input
v-model="queryFormData.coinMax" v-model="queryFormData.coinMax"
placeholder="最大金币" :placeholder="t('hostsList.maxCoins')"
size="large" size="large"
style="width: 180px" style="width: 180px"
class="right-input" class="right-input"
@@ -32,7 +32,7 @@
<el-input <el-input
v-model="queryFormData.levelMin" v-model="queryFormData.levelMin"
placeholder="最小等级" :placeholder="t('hostsList.minLevel')"
size="large" size="large"
style="width: 180px" style="width: 180px"
class="right-input" class="right-input"
@@ -42,7 +42,7 @@
<el-input <el-input
v-model="queryFormData.levelMax" v-model="queryFormData.levelMax"
placeholder="最大等级" :placeholder="t('hostsList.maxLevel')"
size="large" size="large"
style="width: 180px" style="width: 180px"
class="right-input" class="right-input"
@@ -57,49 +57,64 @@
type="primary" type="primary"
> >
<div> <div>
{{ streamdialogVisibletext ? "已指定直播间" : "指定直播间" }} {{
</div></el-button streamdialogVisibletext
> ? t('hostsList.specifiedRooms')
: t('hostsList.specifyRooms')
}}
</div>
</el-button>
<div <div
class="right-input right-text" class="right-input right-text"
style="width: 160px; height: 50px; line-height: 50px" style="width: 160px; height: 50px; line-height: 50px"
> >
总数{{ getBrotherInfodata.total }} {{ t('hostsList.total') }}{{ getBrotherInfodata.total }}
</div> </div>
<div <div
class="right-input right-text" class="right-input right-text"
style="width: 160px; height: 50px; line-height: 50px" style="width: 160px; height: 50px; line-height: 50px"
> >
有效数{{ getBrotherInfodata.valid }} {{ t('hostsList.valid') }}{{ getBrotherInfodata.valid }}
</div> </div>
<el-button class="serch-button right-input" type="primary" @click="Resetss" <el-button
>重置</el-button class="serch-button right-input"
type="primary"
@click="Resetss"
> >
{{ t('hostsList.reset') }}
</el-button>
<el-button <el-button
v-show="queryFormData.isRunning" v-show="queryFormData.isRunning"
class="serch-button right-input" class="serch-button right-input"
type="primary" type="primary"
@click="getBigBrother" @click="getBigBrother"
>开始</el-button
> >
{{ t('hostsList.start') }}
</el-button>
<el-button <el-button
v-show="!queryFormData.isRunning" v-show="!queryFormData.isRunning"
class="serch-button right-input" class="serch-button right-input"
type="primary" type="primary"
@click="BigBrotherstop" @click="BigBrotherstop"
>结束</el-button
> >
{{ t('hostsList.end') }}
</el-button>
</div> </div>
<!-- `````````````````````````````````````````````````````````````````````````````````````````````````` -->
<div style="width: 100%; border-bottom: 1px solid #e9e9e9; margin-top: 30px"></div> <div
<!-- ···································································································· --> style="width: 100%; border-bottom: 1px solid #e9e9e9; margin-top: 30px"
></div>
<!-- 第二行筛选 -->
<div style="display: flex; margin-top: 30px; flex-wrap: wrap"> <div style="display: flex; margin-top: 30px; flex-wrap: wrap">
<el-select <el-select
v-model="searchForm.region" v-model="searchForm.region"
filterable filterable
placeholder="选择国家" :placeholder="t('hostsList.selectCountry')"
size="large" size="large"
style="width: 160px" style="width: 160px"
> >
@@ -113,57 +128,73 @@
<el-input <el-input
v-model="searchForm.displayId" v-model="searchForm.displayId"
placeholder="大哥id" :placeholder="t('hostsList.bigBrotherId')"
size="large" size="large"
style="width: 160px" style="width: 160px"
class="right-input" class="right-input"
clearable clearable
/> />
<el-button class="serch-button right-input" type="primary" @click="serch" <el-button
>查询</el-button class="serch-button right-input"
type="primary"
@click="serch"
> >
{{ t('hostsList.search') }}
</el-button>
<el-button class="serch-button" type="primary" @click="reset"> 重置 </el-button> <el-button class="serch-button" type="primary" @click="reset">
{{ t('hostsList.reset') }}
</el-button>
<el-button <el-button
class="put-button" class="put-button"
:disabled="tableData.length == 0" :disabled="tableData.length == 0"
type="primary" type="primary"
@click="exportList" @click="exportList"
>导出Excel数据</el-button
> >
{{ t('hostsList.exportExcel') }}
</el-button>
<el-button <el-button
@click="filterdialogVisible = true" @click="filterdialogVisible = true"
class="put-button buttoMore-filters" class="put-button buttoMore-filters"
type="primary" type="primary"
><img class="filters-img" src="@/assets/filter.png" />
<div style="margin-left: 10px">更多筛选</div></el-button
> >
<img class="filters-img" src="@/assets/filter.png" />
<div style="margin-left: 10px">
{{ t('hostsList.moreFilters') }}
</div>
</el-button>
<el-button <el-button
class="serch-button right-input" class="serch-button right-input"
style="width: 150px" style="width: 150px"
type="primary" type="primary"
@click="openTikTok" @click="openTikTok"
>打开 TikTok 登录</el-button
> >
{{ t('hostsList.openTikTok') }}
</el-button>
<div <div
class="right-input right-text" class="right-input right-text"
style="width: auto; height: 50px; line-height: 50px" style="width: auto; height: 50px; line-height: 50px"
> >
当前网络{{ countryData }} {{ t('hostsList.currentNetwork') }}{{ countryData }}
</div> </div>
<div <div
class="right-input right-text" class="right-input right-text"
style="width: auto; height: 50px; line-height: 50px" style="width: auto; height: 50px; line-height: 50px"
> >
运行时间{{ String(hourstuo).padStart(2, "0") }}:{{ {{ t('hostsList.runningTime') }}
String(minutestuo).padStart(2, "0") {{ String(hourstuo).padStart(2, '0') }}:{{
}}:{{ String(secondstuo).padStart(2, "0") }} String(minutestuo).padStart(2, '0')
}}:{{ String(secondstuo).padStart(2, '0') }}
</div> </div>
</div> </div>
<!-- ····················································································································· -->
<!-- 表格 -->
<div class="hostTable center-justify"> <div class="hostTable center-justify">
<el-table <el-table
ref="multipleTableRef" ref="multipleTableRef"
@@ -176,9 +207,17 @@
> >
<!-- <el-table-column type="selection" width="35" /> --> <!-- <el-table-column type="selection" width="35" /> -->
<el-table-column fixed prop="displayId" label="Id" :width="screenWidth"> <el-table-column
fixed
prop="displayId"
:label="t('hostsList.id')"
:width="screenWidth"
>
<template #default="scope"> <template #default="scope">
<div class="hostIdText" @click="openHTML(scope.row.displayId)"> <div
class="hostIdText"
@click="openHTML(scope.row.displayId)"
>
{{ scope.row.displayId }} {{ scope.row.displayId }}
</div> </div>
</template> </template>
@@ -186,10 +225,9 @@
<el-table-column <el-table-column
prop="hostDisplayId" prop="hostDisplayId"
label="所在直播间主播id" :label="t('hostsList.hostId')"
:width="screenWidth" :width="screenWidth"
> >
<!-- @click="openhostDisplayId(scope.row.hostDisplayId)" -->
<template #default="scope"> <template #default="scope">
<div <div
class="hostIdText" class="hostIdText"
@@ -207,11 +245,15 @@
:label="label.paramCodeMeaning" :label="label.paramCodeMeaning"
:width="screenWidth" :width="screenWidth"
> >
<template v-if="label.paramCode != 'createDt'" #default="scope"> </template> <template
v-if="label.paramCode != 'createDt'"
#default="scope"
>
</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- ······································································································································ -->
<!-- 分页 --> <!-- 分页 -->
<div class="center-justify" style="margin-top: 30px"> <div class="center-justify" style="margin-top: 30px">
<el-pagination <el-pagination
@@ -226,21 +268,33 @@
/> />
</div> </div>
<!-- ·······································································弹窗······································································· --> <!-- 更多筛选弹窗 -->
<el-dialog v-model="filterdialogVisible" width="800px" :before-close="handleClose"> <el-dialog
v-model="filterdialogVisible"
width="800px"
:before-close="handleClose"
>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="4"> <el-col :span="4">
<!-- <label>选择筛选条件</label> --> <div
<div style="height: 100%; padding-top: 10px" class="center-justify">时间</div> style="height: 100%; padding-top: 10px"
class="center-justify"
>
{{ t('hostsList.time') }}
</div>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<div><label>开始时间/结束时间</label></div> <div>
<label>
{{ t('hostsList.startTime') }}/{{ t('hostsList.endTime') }}
</label>
</div>
<el-date-picker <el-date-picker
v-model="createTimes" v-model="createTimes"
type="datetimerange" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择查询时间" :placeholder="t('hostsList.selectTime')"
size="large" size="large"
style="width: 600px; margin-top: 10px" style="width: 600px; margin-top: 10px"
/> />
@@ -259,37 +313,47 @@
</div> </div>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<div><label>最小值</label></div> <div>
<label>{{ t('hostsList.minValue') }}</label>
</div>
<el-input <el-input
type="number" type="number"
:oninput="'if(value.length>9)value=value.slice(0,9)'" :oninput="'if(value.length>9)value=value.slice(0,9)'"
v-model.number="searchForm[field.minModel]" v-model.number="searchForm[field.minModel]"
placeholder="请输入最小值" :placeholder="t('hostsList.enterMinValue')"
/> />
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<div><label>最大值</label></div> <div>
<label>{{ t('hostsList.maxValue') }}</label>
</div>
<el-input <el-input
type="number" type="number"
:oninput="'if(value.length>9)value=value.slice(0,9)'" :oninput="'if(value.length>9)value=value.slice(0,9)'"
v-model.number="searchForm[field.maxModel]" v-model.number="searchForm[field.maxModel]"
placeholder="请输入最大值" :placeholder="t('hostsList.enterMaxValue')"
/> />
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="4"> <el-col :span="4">
<!-- <label>选择筛选条件</label> --> <div
<div style="height: 100%; padding-top: 10px" class="center-justify">排序</div> style="height: 100%; padding-top: 10px"
class="center-justify"
>
{{ t('hostsList.sort') }}
</div>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<div><label>排序类型</label></div> <div>
<label>{{ t('hostsList.sortType') }}</label>
</div>
<el-select <el-select
v-model="sortData.sortName" v-model="sortData.sortName"
filterable filterable
placeholder="请选择" :placeholder="t('hostsList.pleaseSelect')"
style="width: 240px" style="width: 240px"
> >
<el-option <el-option
@@ -301,18 +365,20 @@
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<div><label>升序/降序</label></div> <div>
<label>{{ t('hostsList.sortOrder') }}</label>
</div>
<el-select <el-select
v-model="sortData.sort" v-model="sortData.sort"
filterable filterable
placeholder="请选择" :placeholder="t('hostsList.pleaseSelect')"
style="width: 240px" style="width: 240px"
> >
<el-option <el-option
v-for="item in [ v-for="item in [
{ label: '升序', value: 'asc' }, { label: t('hostsList.ascending'), value: 'asc' },
{ label: '降序', value: 'desc' }, { label: t('hostsList.descending'), value: 'desc' },
]" ]"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
@@ -324,42 +390,81 @@
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button type="primary" @click="reset"> 重置 </el-button> <el-button type="primary" @click="reset">
<!-- <el-button @click="filterdialogVisible = false">取消</el-button> --> {{ t('hostsList.reset') }}
<el-button type="primary" @click="handelClick"> 确认 </el-button> </el-button>
<el-button type="primary" @click="handelClick">
{{ t('hostsList.confirm') }}
</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog v-model="streamdialogVisible" width="800px" :before-close="handleClose"> <!-- 指定直播间弹窗 -->
<el-dialog
v-model="streamdialogVisible"
width="800px"
:before-close="handleClose"
>
<div class="specify-dialog"> <div class="specify-dialog">
<el-input <el-input
v-model="textarea" v-model="textarea"
style="width: 100%" style="width: 100%"
:rows="15" :rows="15"
type="textarea" type="textarea"
placeholder="请输入直播间id多个id用回车键隔开" :placeholder="t('hostsList.enterRoomIds')"
@input="handleInput" @input="handleInput"
/> />
<div class="specify-footer"> <div class="specify-footer">
<el-button class="specify-button" @click="specifyCancel" <el-button class="specify-button" @click="specifyCancel">
>取消指定直播间</el-button {{ t('hostsList.cancelSpecify') }}
>
<el-button class="specify-button" type="primary" @click="specifyreset">
重置
</el-button> </el-button>
<el-button class="specify-button" type="primary" @click="specifyClick"> <el-button
确认 class="specify-button"
type="primary"
@click="specifyreset"
>
{{ t('hostsList.specifyReset') }}
</el-button>
<el-button
class="specify-button"
type="primary"
@click="specifyClick"
>
{{ t('hostsList.specifyConfirm') }}
</el-button>
<el-button
class="specify-button"
type="primary"
@click="specifyClickStart"
>
{{ t('hostsList.specifyStart') }}
</el-button> </el-button>
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
</div> </div>
<!-- 网络不可访问弹窗 -->
<el-dialog
v-model="Inaccessible"
width="800px"
:before-close="handleClose"
>
<div class="inaccessible-dialog">
<img class="inaccessible-img" src="@/assets/wifi.png" />
</div>
<div class="inaccessible-dialog">
{{ t('hostsList.networkFailed') }}
</div>
</el-dialog>
</template> </template>
<script setup> <script setup>
// import { getToken, setToken, removeToken } from '@/utils/storage' // import { getToken, setToken, removeToken } from '@/utils/storage'
import { useI18n } from 'vue-i18n';
import { import {
tkhostdata, tkhostdata,
dicts, dicts,
@@ -369,6 +474,7 @@ import {
managerhosts, managerhosts,
upholdinfo, upholdinfo,
getCountryinfo, getCountryinfo,
tokenVerification,
accountName, accountName,
} from "@/api/account"; } from "@/api/account";
import { usePythonBridge } from "@/utils/pythonBridge"; import { usePythonBridge } from "@/utils/pythonBridge";
@@ -383,56 +489,64 @@ import { color } from "echarts";
import { getCountryName } from "@/utils/countryUtil"; import { getCountryName } from "@/utils/countryUtil";
import { ElLoading } from "element-plus"; import { ElLoading } from "element-plus";
const { t } = useI18n();
const Inaccessible = ref(false);
// 网络检测
const checkVPN = async () => {
try {
const timeout = new Promise(
(_, reject) => setTimeout(() => reject(new Error("请求超时")), 10000)
);
const response = await Promise.race([
fetch("https://www.google.com", { method: "HEAD", mode: "no-cors" }),
timeout,
]);
if (response && response.type === "opaque") {
Inaccessible.value = false;
} else {
Inaccessible.value = true;
}
} catch (error) {
Inaccessible.value = true;
}
};
// 长按进入直播间 // 长按进入直播间
function handleLongPress(event) { function handleLongPress(event) {
openAnchorIdRooms(event); openAnchorIdRooms(event);
} }
// 复制单元格内容 // 复制单元格内容
function handleCellDbClick(row, column, cell, event) { function handleCellDbClick(row, column, cell, event) {
const text = cell?.textContent?.trim(); const text = cell?.textContent?.trim();
if (!text) { if (!text) {
ElMessage({ ElMessage({
type: "warning", type: "warning",
message: "无内容可复制", message: t("hostsList.noContentToCopy"),
}); });
return; return;
} }
copyToClipboard(text); copyToClipboard(text);
} }
//复制到剪切板
async function copyToClipboard(text) { async function copyToClipboard(text) {
try { setClipboards(text)
// 尝试使用现代Clipboard API .then((res) => {
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
ElMessage({ ElMessage({
type: "success", type: "success",
message: "复制成功", message: t("hostsList.copySuccess"),
}); });
return; })
} .catch((err) => {
// 备用方法使用document.execCommand
const textarea = document.createElement("textarea");
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
if (document.execCommand("copy")) {
ElMessage({
type: "success",
message: "复制成功",
});
} else {
throw new Error("execCommand failed");
}
document.body.removeChild(textarea);
} catch (err) {
console.error("复制失败:", err);
ElMessage({ ElMessage({
type: "error", type: "error",
message: "复制失败", message: t("hostsList.copyFailed"),
});
}); });
}
} }
// ip 国家 // ip 国家
@@ -452,41 +566,43 @@ const getIpInfo = async () => {
countryData.value = getCountryName(data.country); countryData.value = getCountryName(data.country);
} catch (error) { } catch (error) {
console.error("请求出错:", error); console.error("请求出错:", error);
ElMessageBox.prompt("请输入将要获取国家的中文名", "获取国家失败", { ElMessageBox.prompt(
confirmButtonText: "确认", t("hostsList.pleaseEnterCountryName"),
cancelButtonText: "取消", t("hostsList.getCountryFailed"),
{
confirmButtonText: t("hostsList.confirm"),
cancelButtonText: t("hostsList.cancel"),
showClose: false, showClose: false,
closeOnClickModal: false, closeOnClickModal: false,
showCancelButton: false, showCancelButton: false,
}).then(({ value }) => { }
).then(({ value }) => {
countryData.value = value; countryData.value = value;
}); });
// .catch(() => {
// ElMessage({
// type: 'info',
// message: 'Input canceled',
// })
// })
} }
}; };
// 打开 TikTok 登录 // 打开 TikTok 登录
function openTikTok() { function openTikTok() {
loginTikTok(); loginTikTok();
} }
// 重置 // 重置
function Resetss() { function Resetss() {
queryFormData.value = {}; queryFormData.value = {};
} }
// 大哥 climb // 大哥 climb
const queryFormData = ref({ const queryFormData = ref({
coinMin: "", coinMin: "", //打赏的金币最小值
coinMax: "", coinMax: "", //打赏的金币最大值
levelMin: "", levelMin: "", //等级最小值
levelMax: "", levelMax: "", //等级最大值
isFilter: false, isFilter: false, //是否筛选
isRunning: true, isRunning: true, //是否在运行
anchor_ids: [], anchor_ids: [], //指定直播间id
}); });
// 时间 // 时间
const timerId = ref(null); const timerId = ref(null);
const getBrotherInfodata = ref({ const getBrotherInfodata = ref({
@@ -498,7 +614,7 @@ const getBrotherInfodata = ref({
function BigBrotherstop() { function BigBrotherstop() {
const loading = ElLoading.service({ const loading = ElLoading.service({
lock: true, lock: true,
text: "正在停止...", text: t("hostsList.stopping"),
background: "rgba(0, 0, 0, 0.7)", background: "rgba(0, 0, 0, 0.7)",
}); });
stopTimerfun(); stopTimerfun();
@@ -520,19 +636,40 @@ function BigBrotherstop() {
}); });
} }
//指定直播间 // 指定直播间开始
function specifyClick() { function specifyClickStart() {
if (textarea.value == "") { if (textarea.value == "") {
ElMessage({ ElMessage({
type: "error", type: "error",
message: "请输入直播间id", message: t("hostsList.enterRoomId"),
}); });
return; return;
} }
setStorageStreamId(textarea.value).then((res) => {});
queryFormData.value.anchor_ids = textarea.value.split("\n"); queryFormData.value.anchor_ids = textarea.value.split("\n");
streamdialogVisible.value = false; streamdialogVisible.value = false;
streamdialogVisibletext.value = true; streamdialogVisibletext.value = true;
getBigBrother();
}
function specifyClick() {
if (
textarea.value == "" ||
textarea.value == null ||
textarea.value == undefined ||
textarea.value.length == 0
) {
streamdialogVisibletext.value = false;
streamdialogVisible.value = false;
queryFormData.value.anchor_ids = [];
} else {
streamdialogVisibletext.value = true;
streamdialogVisible.value = false;
queryFormData.value = {
...queryFormData.value,
anchor_ids: textarea.value.split("\n").filter((id) => id.trim() !== ""),
};
}
} }
// 指定直播间重置 // 指定直播间重置
@@ -551,14 +688,19 @@ function specifyCancel() {
function handleInput() { function handleInput() {
streamdialogVisibletext.value = false; streamdialogVisibletext.value = false;
} }
//获取主播列表
//开始爬取 // 获取主播列表 - 开始爬取
function getBigBrother() { function getBigBrother() {
const loading = ElLoading.service({ const loading = ElLoading.service({
lock: true, lock: true,
text: "正在启动...", text: t("hostsList.starting"),
background: "rgba(0, 0, 0, 0.7)", background: "rgba(0, 0, 0, 0.7)",
}); });
const storageobject = ref({
key: "UserSettings",
data: queryFormData.value,
});
storageSetInfos(storageobject.value).then((res) => {});
queryFormData.value.tenantId = userInfo.value.tenantId; queryFormData.value.tenantId = userInfo.value.tenantId;
queryFormData.value.region = countryData.value; queryFormData.value.region = countryData.value;
startTimerfun(); startTimerfun();
@@ -603,6 +745,9 @@ const {
setStorageStreamId, setStorageStreamId,
getStorageStreamId, getStorageStreamId,
openAnchorIdRooms, openAnchorIdRooms,
storageSetInfos,
readSetInfos,
setClipboards,
} = usePythonBridge(); } = usePythonBridge();
let num = ref(0); let num = ref(0);
@@ -611,15 +756,15 @@ const userInfo = ref({});
// 主播列表 DOM // 主播列表 DOM
const multipleTableRef = ref(null); const multipleTableRef = ref(null);
let labelList = ref([ let labelList = ref([
{ paramCode: "userIdStr", paramCodeMeaning: "用户id" }, { paramCode: "userIdStr", paramCodeMeaning: t("hostsList.userId") },
{ paramCode: "level", paramCodeMeaning: "等级" }, { paramCode: "level", paramCodeMeaning: t("hostsList.level") },
{ paramCode: "fansLevel", paramCodeMeaning: "粉丝团等级" }, { paramCode: "fansLevel", paramCodeMeaning: t("hostsList.fansLevel") },
{ paramCode: "hostcoins", paramCodeMeaning: "打赏的金币" }, { paramCode: "hostcoins", paramCodeMeaning: t("hostsList.coins") },
{ paramCode: "hostDisplayId", paramCodeMeaning: "所在直播间主播id" }, { paramCode: "region", paramCodeMeaning: t("hostsList.region") },
{ paramCode: "region", paramCodeMeaning: "地区" }, { paramCode: "followerCount", paramCodeMeaning: t("hostsList.followerCount") },
{ paramCode: "followerCount", paramCodeMeaning: "粉丝数" }, { paramCode: "followingCount", paramCodeMeaning: t("hostsList.followingCount") },
{ paramCode: "followingCount", paramCodeMeaning: "关注数" }, { paramCode: "createTime", paramCodeMeaning: t("hostsList.createTime") },
{ paramCode: "createTime", paramCodeMeaning: "创建时间" }, { paramCode: "totalGiftCoins", paramCodeMeaning: t("hostsList.totalGiftCoins") },
]); ]);
const tableData = ref([]); const tableData = ref([]);
@@ -630,18 +775,17 @@ const createTimes = ref([]);
const page = ref(1); const page = ref(1);
const pageSize = ref(10); const pageSize = ref(10);
const fields = [ const fields = [
// { label: "打赏的金币", minModel: "hostcoinsMin", maxModel: "hostcoinsMax" }, { label: t("hostsList.level"), minModel: "levelMin", maxModel: "levelMax" },
{ label: "等级", minModel: "levelMin", maxModel: "levelMax" },
]; ];
// 排序 // 排序
let sortData = ref({ sortName: "createTime", sort: "desc" }); let sortData = ref({ sortName: "createTime", sort: "desc" });
// 排序类型 // 排序类型
let sortNameOptions = ref([ let sortNameOptions = ref([
{ label: "创建时间", type: "createTime" }, { label: t("hostsList.createTime"), type: "createTime" },
{ label: "打赏的金币", type: "hostsCoins" }, { label: t("hostsList.coins"), type: "hostsCoins" },
{ label: "打赏金币总和", type: "totalGiftCoins" }, { label: t("hostsList.totalGiftCoins"), type: "totalGiftCoins" },
{ label: "等级", type: "level" }, { label: t("hostsList.level"), type: "level" },
]); ]);
// 是否在运行 // 是否在运行
let isRunnings = ref(false); let isRunnings = ref(false);
@@ -672,15 +816,49 @@ let version = ref("0.0.0");
const lastVisibleTime = ref(null); const lastVisibleTime = ref(null);
function tokenVerificationfun(params) {
tokenVerification(params)
.then((res) => {})
.catch((err) => {});
}
onMounted(() => { onMounted(() => {
window.addEventListener("resize", handleResize); window.addEventListener("resize", handleResize);
window.addEventListener("visibilitychange", handleVisibilityChange); window.addEventListener("visibilitychange", handleVisibilityChange);
setTimeout(() => { setTimeout(() => {
getUserdata(); getUserdata();
getStorageStreamId(1).then((res) => { readSetInfos("UserSettings").then((res) => {
textarea.value = res == "" ? "" : JSON.parse(res); const storageData = (queryFormData.value =
res == ""
? {
coinMin: "",
coinMax: "",
levelMin: "",
levelMax: "",
isFilter: false,
isRunning: true,
anchor_ids: [],
}
: JSON.parse(res));
if (
storageData.anchor_ids == "" ||
storageData.anchor_ids == null ||
storageData.anchor_ids == undefined ||
storageData.anchor_ids.length == 0
) {
streamdialogVisibletext.value = false;
textarea.value = "";
} else {
streamdialogVisibletext.value = true;
textarea.value = storageData.anchor_ids.join("\n");
}
}); });
}, 500); }, 500);
setInterval(() => {
checkVPN();
tokenVerificationfun();
}, 10000);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@@ -691,13 +869,13 @@ onBeforeUnmount(() => {
function handleVisibilityChange() { function handleVisibilityChange() {
if (isRunnings.value) { if (isRunnings.value) {
if (document.hidden) { if (document.hidden) {
// 页面变为不可见
lastVisibleTime.value = Date.now(); lastVisibleTime.value = Date.now();
stopTimerfun(); stopTimerfun();
} else { } else {
// 页面重新可见
if (lastVisibleTime.value) { if (lastVisibleTime.value) {
const hiddenDuration = Math.floor((Date.now() - lastVisibleTime.value) / 1000); const hiddenDuration = Math.floor(
(Date.now() - lastVisibleTime.value) / 1000
);
secondstuo.value += hiddenDuration % 60; secondstuo.value += hiddenDuration % 60;
minutestuo.value += Math.floor(hiddenDuration / 60) % 60; minutestuo.value += Math.floor(hiddenDuration / 60) % 60;
hourstuo.value += Math.floor(hiddenDuration / 3600); hourstuo.value += Math.floor(hiddenDuration / 3600);
@@ -749,6 +927,7 @@ function serch() {
function exportList() { function exportList() {
exportToExcel(requestParams.value); exportToExcel(requestParams.value);
} }
// 分页每页条数 // 分页每页条数
function handleSizeChange(val) { function handleSizeChange(val) {
console.log(`${val} items per page`); console.log(`${val} items per page`);
@@ -766,13 +945,12 @@ function handleSelectionChange(data) {
data.forEach((item) => { data.forEach((item) => {
selectHostList.value.push(item.hostId); selectHostList.value.push(item.hostId);
}); });
// multipleTableRef.value = data
// console.log(multipleTableRef.value)
} }
// 时间格式化 // 时间格式化
function formatDate(date) { function formatDate(date) {
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从 0开始需要+1 const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0"); const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0"); const minutes = String(date.getMinutes()).padStart(2, "0");
@@ -780,43 +958,42 @@ function formatDate(date) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} }
// 获取主播列表 // 获取主播列表
//记录请求参数
let requestParams = ref({}); let requestParams = ref({});
const getlist = () => { const getlist = () => {
loading.value = true; loading.value = true;
tkhostdata({ tkhostdata({
tenantId: Number(userInfo.value.tenantId), tenantId: Number(userInfo.value.tenantId),
sort: sortData.value.sort, //正序倒序 sort: sortData.value.sort,
sortName: sortData.value.sortName, //排序类型 sortName: sortData.value.sortName,
current: page.value, current: page.value,
pageSize: pageSize.value, pageSize: pageSize.value,
createTimeStart: createTimes.value[0], createTimeStart: createTimes.value[0],
createTimeEnd: createTimes.value[1], createTimeEnd: createTimes.value[1],
...searchForm.value, //筛选条件 ...searchForm.value,
}).then((res) => { }).then((res) => {
loading.value = false; loading.value = false;
console.log(res);
if (res) { if (res) {
requestParams.value = res.records; requestParams.value = res.records;
console.log(res.records);
total.value = Number(res.total); total.value = Number(res.total);
tableData.value = res.records.map((item) => ({ tableData.value = res.records.map((item) => ({
level: item.level, // 等级 level: item.level,
fansLevel: item.fansLevel, // 粉丝团等级 fansLevel: item.fansLevel,
createTime: formatDate(new Date(item.createTime)), // 创建时间 createTime: formatDate(new Date(item.createTime)),
followerCount: item.followerCount, // 粉丝数 followerCount: item.followerCount,
followingCount: item.followingCount, // 关注数 followingCount: item.followingCount,
hostDisplayId: item.hostDisplayId, // 所在直播间主播id hostDisplayId: item.hostDisplayId,
hostcoins: item.hostcoins, // 打赏的金币 hostcoins: item.hostcoins,
region: item.region, // 地区 region: item.region,
totalGiftCoins: item.totalGiftCoins, // 打赏金币总和 totalGiftCoins: item.totalGiftCoins,
userIdStr: item.userIdStr, // 用户id userIdStr: item.userIdStr,
displayId: item.displayId, displayId: item.displayId,
})); }));
} }
}); });
}; };
function handelClick() { function handelClick() {
filterdialogVisible.value = false; filterdialogVisible.value = false;
} }
@@ -837,17 +1014,25 @@ function filterTag(value, row) {
return row.useable === value; return row.useable === value;
} }
const { locale } = useI18n();
// 获取国家 // 获取国家
function getCountry() { function getCountry() {
getCountryinfo({}) getCountryinfo({})
.then((res) => { .then((res) => {
res.forEach((item) => { res.forEach((item) => {
if (item.countryGroupName) { if (item.countryGroupName) {
if (locale.value === 'en-US') {
options.value.push({
value: item.countryGroupName,
label: item.countryNameEnglish,
});
} else if (locale.value === 'zh-CN') {
options.value.push({ options.value.push({
value: item.countryGroupName, value: item.countryGroupName,
label: item.countryGroupName, label: item.countryGroupName,
}); });
} }
}
}); });
}) })
.catch((err) => { .catch((err) => {
@@ -865,7 +1050,6 @@ function closePopover(hostId, paramCode) {
function openHTML(id) { function openHTML(id) {
console.log(id); console.log(id);
givePyAnchorId(id); givePyAnchorId(id);
} }
function openhostDisplayId(hostDisplayId) { function openhostDisplayId(hostDisplayId) {
@@ -873,6 +1057,7 @@ function openhostDisplayId(hostDisplayId) {
} }
</script> </script>
<style lang="less"> <style lang="less">
.hostList { .hostList {
box-sizing: border-box; box-sizing: border-box;
@@ -993,4 +1178,19 @@ function openhostDisplayId(hostDisplayId) {
width: 200px; width: 200px;
height: 50px; height: 50px;
} }
.inaccessible-dialog {
width: 100%;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
color: #666;
text-align: center;
}
.inaccessible-img {
width: 200px;
height: 200px;
}
</style> </style>

View File

@@ -11,12 +11,13 @@
<!-- <div style="position: absolute; bottom: 0; right: 0;">{{ version }}</div> --> <!-- <div style="position: absolute; bottom: 0; right: 0;">{{ version }}</div> -->
</div> </div>
<div class="footer"> <div class="footer">
到期时间{{ time }} {{ $t('common.expirationtime') }}{{ time }}
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { useI18n } from 'vue-i18n';
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
// import Sidebar from '../components/Sidebar.vue'; // import Sidebar from '../components/Sidebar.vue';
import { RouterLink, RouterView } from 'vue-router' import { RouterLink, RouterView } from 'vue-router'
@@ -26,6 +27,7 @@ import { getUser } from "@/utils/storage";
// import workbenches from '@/views/hosts/workbenches.vue' // import workbenches from '@/views/hosts/workbenches.vue'
import { tokenStore,UserStore } from '@/stores/notice' import { tokenStore,UserStore } from '@/stores/notice'
const userCache = UserStore() const userCache = UserStore()
const { t } = useI18n();
const time = ref(formatTimestamp(userCache.user.brotherExpireTime)) const time = ref(formatTimestamp(userCache.user.brotherExpireTime))
// 时间格式化方法 - 将12位时间戳转为YYYY-MM-DD HH:mm:ss格式 // 时间格式化方法 - 将12位时间戳转为YYYY-MM-DD HH:mm:ss格式
function formatTimestamp(timestamp) { function formatTimestamp(timestamp) {