Compare commits

...

20 Commits

Author SHA1 Message Date
pengxiaolong
812fade238 国际化 2025-12-02 21:23:21 +08:00
pengxiaolong
84bab8b8c8 优化代码 2025-10-13 13:19:08 +08:00
pengxiaolong
bf61508796 优化代码 2025-09-29 20:45:59 +08:00
pengxiaolong
73d8b38f99 优化代码 2025-09-25 21:23:42 +08:00
pengxiaolong
6959fd2d7a 优化代码 2025-09-25 20:23:44 +08:00
pengxiaolong
ea50c14175 优化代码 2025-09-25 19:40:54 +08:00
pengxiaolong
8cabf98bd9 优化 2025-07-09 13:27:43 +08:00
pengxiaolong
51ce853bde 优化 2025-07-07 18:46:02 +08:00
pengxiaolong
a46cbf79f6 优化 2025-07-03 19:34:19 +08:00
pengxiaolong
52415dee0b 优化 2025-07-03 19:14:34 +08:00
pengxiaolong
01e9c26821 上传代码 2025-07-01 21:22:43 +08:00
560581f1a4 yolo 2025-06-24 13:35:33 +08:00
17d2251a70 添加 计时器功能 详细筛选功能 分配状态字段 2025-05-15 18:29:53 +08:00
dcd677bbab 近7日数据改为updata 2025-05-12 21:09:32 +08:00
60f6fc4873 版本号更新 2025-05-06 15:38:23 +08:00
5e34aaf402 后端服务地址更新 2025-04-24 22:00:51 +08:00
c5fb3ea0b2 初始化列表 2025-04-24 16:11:22 +08:00
666e27246f 工作台输入框禁用状态,开始按钮禁用状态 2025-04-17 22:36:14 +08:00
91ee2dd7cb 未登录账号禁用爬取按钮 2025-04-17 13:22:24 +08:00
7df29e741c 列表隐藏主播名,添加爬取时间 2025-04-16 19:25:21 +08:00
41 changed files with 4272 additions and 1185 deletions

101
package-lock.json generated
View File

@@ -12,9 +12,10 @@
"core-js": "^3.8.3",
"echarts": "^5.6.0",
"element-plus": "^2.9.7",
"pinia": "^3.0.1",
"pinia": "^3.0.3",
"qwebchannel": "^6.2.0",
"vue": "^3.2.13",
"vue-i18n": "^9.14.5",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},
@@ -91,6 +92,7 @@
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2",
@@ -1767,6 +1769,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -1790,6 +1793,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@@ -1874,6 +1878,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -2221,6 +2226,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -2871,6 +2877,50 @@
"@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": {
"version": "0.3.8",
"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",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/lodash": "*"
}
@@ -3648,6 +3699,7 @@
"integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-compilation-targets": "^7.12.16",
"@soda/friendly-errors-webpack-plugin": "^1.8.0",
@@ -4386,6 +4438,7 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -4912,6 +4965,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73",
@@ -5718,6 +5772,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -5732,6 +5787,7 @@
"integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"icss-utils": "^5.1.0",
"postcss": "^8.4.33",
@@ -5820,6 +5876,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -8467,13 +8524,15 @@
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/lodash-unified": {
"version": "1.0.3",
@@ -8910,6 +8969,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -9709,9 +9769,9 @@
}
},
"node_modules/pinia": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.1.tgz",
"integrity": "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz",
"integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.2"
@@ -9784,6 +9844,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
@@ -10712,6 +10773,7 @@
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -12880,6 +12942,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -13269,6 +13332,7 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
"integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.13",
"@vue/compiler-sfc": "3.5.13",
@@ -13292,6 +13356,26 @@
"dev": true,
"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": {
"version": "17.4.2",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz",
@@ -13431,6 +13515,7 @@
"integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
@@ -13567,6 +13652,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -13684,6 +13770,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -13778,6 +13865,7 @@
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
}
@@ -13795,6 +13883,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",

View File

@@ -11,9 +11,10 @@
"core-js": "^3.8.3",
"echarts": "^5.6.0",
"element-plus": "^2.9.7",
"pinia": "^3.0.1",
"pinia": "^3.0.3",
"qwebchannel": "^6.2.0",
"vue": "^3.2.13",
"vue-i18n": "^9.14.5",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},

View File

@@ -5,23 +5,28 @@
</template>
<script>
const debounce = (fn, delay) => {
let timer
let timer;
return (...args) => {
if (timer) {
clearTimeout(timer)
clearTimeout(timer);
}
timer = setTimeout(() => {
fn(...args)
}, delay)
}
}
const _ResizeObserver = window.ResizeObserver
fn(...args);
}, delay);
};
};
const _ResizeObserver = window.ResizeObserver;
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
constructor(callback) {
callback = debounce(callback, 200)
super(callback)
callback = debounce(callback, 200);
super(callback);
}
}
</script>
};
</script>
<!-- <style lang="less">
/*每个页面公共css */
@import "@/static/css/app.less";
</style> -->

View File

@@ -1,17 +1,43 @@
import { getAxios, postAxios, downFile } from '@/utils/axios.js'
import { ElMessage } from 'element-plus';
//租户获取登录id
export function rentgetloginID(data) {
return getAxios({ url: `/api/tenant/get-id-by-name?name=${data.name}`})
}
//登录
export function login(data) {
return postAxios({ url: '/api/user/bigbrother-doLogin', data })
}
//获取国家
export function getCountryinfo(data) {
return postAxios({ url: '/api/common/country_info', data })
}
//查询tk账号查询次数
export function tkaccountuseinfo(accountName) {
return getAxios({ url: `/api/common/accountCount?accountName=${accountName}` })
}
export function tokenVerification() {
return getAxios({ url: `/api/common/health` })
}
export function tkhostdata(data) {
return postAxios({ url: '/api/big-brother/page', data })
}
export function apiGetCart() {
return getAxios({ url: '/cgi-bin/cart/latest' })
}
export function login(data) {
return postAxios({ url: 'api/account/login', data })
}
// export function login(data) {
// return postAxios({ url: 'api/account/login', data })
// }
export function cheekalive(data) {
return postAxios({ url: 'api/account/cheekalive', data })
}
export function tkhostdata(data) {
return postAxios({ url: 'api/tkinfo/tkhostdata', data })
}
export function dicts(data) {
return postAxios({ url: 'api/param/dicts', data })
}
@@ -25,10 +51,7 @@ export function exporthosts(data) {
export function downList(url, data) {
return downFile(url, data)
}
//查询tk账号查询次数
export function tkaccountuseinfo(data) {
return postAxios({ url: 'api/tkinfo/tkaccountuseinfo', data })
}
//查询员工
export function getStaffList(data) {
return postAxios({ url: 'api/account/list', data })
@@ -41,7 +64,9 @@ export function managerhosts(data) {
export function upholdinfo(data) {
return postAxios({ url: 'api/tkinfo/upholdinfo', data })
}
//获取
export function getCountryinfo(data) {
return postAxios({ url: 'api/tkinfo/countryinfo', data })
//查看名字
export function accountName(str) {
return postAxios({ url: 'api/account/accountName?accounts=' + str })
}

BIN
src/assets/MiniProgram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
src/assets/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
src/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 88 KiB

BIN
src/assets/logo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
src/assets/logoBg1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 106 KiB

BIN
src/assets/logotext1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
src/assets/logotext12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src/assets/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/assets/plus2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/assets/wifi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -38,6 +38,8 @@ export default {
this.inputTime = this.time
this.getTkhostdetail();
console.log(this.time)
console.log(this.getPrevious7Days(this.inputTime))
},
@@ -93,16 +95,6 @@ export default {
res[0][this.getPrevious7Days(this.inputTime)[6]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[6]][this.dataType]),
]
// this.seriesData = {
// [this.getPrevious7Days(this.inputTime)[0]]: res[0][this.getPrevious7Days(this.inputTime)[0]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[0]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[1]]: res[0][this.getPrevious7Days(this.inputTime)[1]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[1]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[2]]: res[0][this.getPrevious7Days(this.inputTime)[2]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[2]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[3]]: res[0][this.getPrevious7Days(this.inputTime)[3]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[3]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[4]]: res[0][this.getPrevious7Days(this.inputTime)[4]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[4]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[5]]: res[0][this.getPrevious7Days(this.inputTime)[5]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[5]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[6]]: res[0][this.getPrevious7Days(this.inputTime)[6]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[6]][this.dataType],
// }
this.initChart();
this.num++
console.log("返回数据", this.seriesData)

View File

@@ -1,11 +1,11 @@
<template>
<div class="sidebar">
<div class="logo">
<img style="margin-right: 10px;" src="@/assets/logo.png">
<!-- <img style="margin-right: 10px;" src="@/assets/logo.png"> -->
<img src="@/assets/logotext.png">
</div>
<ul>
<li @click="updateActiveIndex(1)" v-show="userInfo.userType == 3">
<li @click="updateActiveIndex(1)">
<div>
<img v-show="activeIndex == 1" src="@/assets/navAction.png" autoplay loop muted class="background-img">
<div style="display: flex;">
@@ -45,11 +45,13 @@ import { ref, reactive, onMounted } from 'vue';
import { getUser } from '@/utils/storage'
import { defineEmits } from 'vue';
const userInfo = ref(getUser())
let activeIndex = ref(userInfo.value.userType == 3 ? 1 : 2);
let activeIndex = ref(1);
const emit = defineEmits(['update:activeIndex']);
const emit = defineEmits(['activeIndex']);
const updateActiveIndex = (index) => {
@@ -58,23 +60,23 @@ const updateActiveIndex = (index) => {
};
</script>
<style scoped>
<style scoped lang="less">
.sidebar {
position: fixed;
left: 0;
top: 0;
height: 900px;
width: 280px;
background-color: #338F6A;
background-color: @bg-color;
padding: 20px;
box-sizing: border-box;
.logo {
border-bottom: 1px solid #fff;
padding-bottom: 29px;
padding-top: 20px;
img:nth-of-type(1) {
height: 40px;
height: 66px;
}
img:nth-of-type(2) {

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,19 @@ import store from './store'
import { createPinia } from 'pinia';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// createApp(App).use(store).use(router).mount('#app')
import i18n, { elementPlusLocale } from './i18n'
const app = createApp(App);
app.use(ElementPlus) // 注册 ElementPlus
// 获取当前语言
const currentLocale = localStorage.getItem('language') || 'zh-CN'
app.use(ElementPlus, {
locale: elementPlusLocale[currentLocale] // 动态配置Element Plus语言
});
app.use(i18n); // 注册 i18n
app.use(createPinia()); // 注册 Pinia
app.use(store); // 注册 store
app.use(router); // 注册 router
app.mount('#app');
app.mount('#app');

View File

@@ -19,11 +19,12 @@ const routes = [
name: 'hostsList',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/hostsList.vue')
},
{
path: 'workBenches',
name: 'workBenches',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/workbenches.vue')
},]
// {
// path: 'workBenches',
// name: 'workBenches',
// component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/workbenches.vue')
// },
]
}
]
const router = createRouter({

4
src/static/css/app.less Normal file
View File

@@ -0,0 +1,4 @@
@bg-color: #022b4e; // 主色
@bg-color-light: #022b4eaf; // 浅主色
@bg-color-light-light: #022b4e1c; // 浅浅主色
@btn-bg-color: #045dac; // 黄色按钮主色

View File

@@ -9,4 +9,29 @@ export const noticeStore = defineStore('noticeNum', {
this.data.num++;
},
},
});
});
export const tokenStore = defineStore('token', {
state: () => {
return { token: '' }
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
setToken(token){
this.token = token
}
},
})
export const UserStore = defineStore('User', {
state: () => {
return { user: {} }
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
setUser(user){
this.user = user
}
},
})

View File

@@ -4,33 +4,53 @@
*/
import axios from 'axios'
import { getToken, getUser } from '@/utils/storage'
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import router from '@/router'
import { ElMessage } from 'element-plus';
import { usePythonBridge, } from '@/utils/pythonBridge'
import { ref } from 'vue';
import { defineStore } from 'pinia'
import { tokenStore,UserStore } from '@/stores/notice'
const { stopScript } = usePythonBridge();
const router = useRouter();
// 请求地址前缀
let baseURL = ''
if (process.env.NODE_ENV === 'development') {
// 生产环境
baseURL = "http://120.26.251.180:8085/"
// baseURL = "http://192.168.0.115:8085/"
baseURL = "https://crawlclient.api.yolozs.com"
// baseURL = "http://47.79.98.113:8101"
// baseURL = "http://192.168.0.103:8085/"
// baseURL = "http://192.168.1.144:8101"
} else {
// 测试环境
// baseURL = "http://120.26.251.180:8085/"
// 开发环境
baseURL = "http://120.26.251.180:8085/"
baseURL = "https://crawlclient.api.yolozs.com"
// baseURL = "http://47.79.98.113:8101"
// baseURL = "http://192.168.1.144:8101"
// baseURL = "http://api.tkpage.vvtiktok.cn"
}
// 请求拦截器
axios.interceptors.request.use((config) => {
// if (getToken()) {
// config.headers['token'] = getToken();
// }
const tokenCache = tokenStore()
console.log("config", config)
const url = sliceUrl(config.url)
console.log("url", url)
// 请求超时时间 - 毫秒
config.timeout = 60000
config.baseURL = baseURL
// 自定义Content-type
config.headers['Content-type'] = 'application/json'
if (!(config.url == 'bigbrother-doLogin' || config.url == 'get-id-by-name')) {
config.headers['vvtoken'] = tokenCache.token;
}
return config;
}, (error) => {
return Promise.reject(error)
@@ -38,7 +58,19 @@ axios.interceptors.request.use((config) => {
// 响应拦截器
axios.interceptors.response.use((response) => {
return response
console.log("response", response.data)
if (response.data.code == 0) {
console.log("response", response.data.data)
return response.data.data
} else if (response.data.code == 40400) {
router.push('/')
ElMessage.error(response.data.code + '' + response.data.message);
}else{
ElMessage.error(response.data.code + '' + response.data.message);
}
}, (error) => {
// 可添加请求失败后的处理逻辑
return Promise.reject(error)
@@ -54,10 +86,9 @@ export function getAxios({ url, params }) {
params
// 请求成功将返回的数据传递给resolve函数
}).then(res => {
resolve(res.data)
resolve(res)
// 请求失败将错误信息传递给reject函数
}).catch(err => {
console.log(err)
reject(err)
})
})
@@ -65,13 +96,6 @@ export function getAxios({ url, params }) {
// axios的post请求
export function postAxios({ url, data }) {
if (url != 'api/account/login') {
throttledCheekalive();
}
return new Promise((resolve, reject) => {
axios.post(
url,
@@ -82,20 +106,9 @@ export function postAxios({ url, data }) {
}
}
).then(res => {
resolve(res.data)
resolve(res)
}).catch(err => {
if (err.message == "Network Error") {
// alert("网络错误,请检查网络连接")
ElMessage.error('网络连接错误');
reject('网络连接错误')
} else {
ElMessage.error(err.message);
reject(err.message)
}
// console.log(err)
// reject(err)
reject(err)
})
})
}
@@ -132,9 +145,11 @@ export const downFile = async (urlstr, data) => {
}
//请求前验证
function cheekalive() {
const userCache = UserStore()
const tokenCache = tokenStore()
axios.post('api/account/cheekalive', {
userId: getUser().userId,
currcode: getToken(),
userId: userCache.user.id,
currcode: tokenCache.token,
},
{
headers: {
@@ -146,6 +161,7 @@ function cheekalive() {
if (res.data) {
} else {
stopScript();
alert("账号在其他地方登录!")
window.location.href = '/';
}
@@ -167,6 +183,18 @@ function throttle(func, limit) {
}
}
const throttledCheekalive = throttle(cheekalive, 5000);
function sliceUrl(url) {
const lastSlash = url.lastIndexOf('/');
const questionMark = url.indexOf('?');
if (questionMark == -1) {
const result = url.slice(lastSlash + 1, url.length);
return result;
} else {
const result = url.slice(lastSlash + 1, questionMark);
return result;
}
}
export default axios

View File

@@ -1,261 +1,270 @@
// country-utils.js
import i18n from '../i18n'
// 国家代码枚举(只包含代码,不包含名称)
export const CountryCode = {
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: "津巴布韦"
AD: "AD",
AE: "AE",
AF: "AF",
AG: "AG",
AI: "AI",
AL: "AL",
AM: "AM",
AO: "AO",
AQ: "AQ",
AR: "AR",
AS: "AS",
AT: "AT",
AU: "AU",
AU1: "AU1",
AW: "AW",
AX: "AX",
AZ: "AZ",
BA: "BA",
BB: "BB",
BD: "BD",
BE: "BE",
BF: "BF",
BG: "BG",
BH: "BH",
BI: "BI",
BJ: "BJ",
BL: "BL",
BM: "BM",
BN: "BN",
BO: "BO",
BQ: "BQ",
BR: "BR",
BS: "BS",
BT: "BT",
BV: "BV",
BW: "BW",
BY: "BY",
BZ: "BZ",
CA: "CA",
CA1: "CA1",
CC: "CC",
CD: "CD",
CF: "CF",
CG: "CG",
CH: "CH",
CI: "CI",
CK: "CK",
CL: "CL",
CM: "CM",
CN: "CN",
CO: "CO",
CR: "CR",
CU: "CU",
CV: "CV",
CW: "CW",
CX: "CX",
CY: "CY",
CZ: "CZ",
DE: "DE",
DG: "DG",
DJ: "DJ",
DK: "DK",
DM: "DM",
DO: "DO",
DZ: "DZ",
EC: "EC",
EE: "EE",
EG: "EG",
EH: "EH",
ER: "ER",
ES: "ES",
ET: "ET",
FI: "FI",
FJ: "FJ",
FK: "FK",
FM: "FM",
FO: "FO",
FR: "FR",
GA: "GA",
GB: "GB",
GD: "GD",
GE: "GE",
GF: "GF",
GG: "GG",
GH: "GH",
GI: "GI",
GL: "GL",
GM: "GM",
GN: "GN",
GP: "GP",
GQ: "GQ",
GR: "GR",
GS: "GS",
GT: "GT",
GU: "GU",
GW: "GW",
GY: "GY",
HK: "HK",
HM: "HM",
HN: "HN",
HR: "HR",
HT: "HT",
HU: "HU",
ID: "ID",
IE: "IE",
IL: "IL",
IM: "IM",
IN: "IN",
IO: "IO",
IQ: "IQ",
IR: "IR",
IS: "IS",
IT: "IT",
JE: "JE",
JM: "JM",
JO: "JO",
JP: "JP",
JP1: "JP1",
KE: "KE",
KG: "KG",
KH: "KH",
KI: "KI",
KM: "KM",
KN: "KN",
KP: "KP",
KR: "KR",
KR1: "KR1",
KR1_UXWAUDIT: "KR1_UXWAUDIT",
KW: "KW",
KY: "KY",
KZ: "KZ",
LA: "LA",
LB: "LB",
LC: "LC",
LI: "LI",
LK: "LK",
LR: "LR",
LS: "LS",
LT: "LT",
LU: "LU",
LV: "LV",
LY: "LY",
MA: "MA",
MC: "MC",
MD: "MD",
ME: "ME",
MF: "MF",
MG: "MG",
MH: "MH",
MK: "MK",
ML: "ML",
MM: "MM",
MN: "MN",
MO: "MO",
MP: "MP",
MQ: "MQ",
MR: "MR",
MS: "MS",
MT: "MT",
MU: "MU",
MV: "MV",
MW: "MW",
MX: "MX",
MY: "MY",
MZ: "MZ",
NA: "NA",
NC: "NC",
NE: "NE",
NF: "NF",
NG: "NG",
NI: "NI",
NL: "NL",
NO: "NO",
NP: "NP",
NR: "NR",
NU: "NU",
NZ: "NZ",
OM: "OM",
PA: "PA",
PE: "PE",
PF: "PF",
PG: "PG",
PH: "PH",
PK: "PK",
PL: "PL",
PM: "PM",
PN: "PN",
PR: "PR",
PS: "PS",
PT: "PT",
PW: "PW",
PY: "PY",
QA: "QA",
RE: "RE",
RO: "RO",
RS: "RS",
RU: "RU",
RW: "RW",
SA: "SA",
SB: "SB",
SC: "SC",
SD: "SD",
SE: "SE",
SG: "SG",
SI: "SI",
SJ: "SJ",
SK: "SK",
SL: "SL",
SM: "SM",
SN: "SN",
SO: "SO",
SR: "SR",
SS: "SS",
ST: "ST",
SV: "SV",
SX: "SX",
SY: "SY",
SZ: "SZ",
TC: "TC",
TD: "TD",
TF: "TF",
TG: "TG",
TH: "TH",
TJ: "TJ",
TK: "TK",
TL: "TL",
TM: "TM",
TN: "TN",
TO: "TO",
TR: "TR",
TT: "TT",
TV: "TV",
TW: "TW",
TZ: "TZ",
UA: "UA",
UG: "UG",
UM: "UM",
US: "US",
UY: "UY",
UZ: "UZ",
VA: "VA",
VC: "VC",
VE: "VE",
VG: "VG",
VI: "VI",
VN: "VN",
VN1: "VN1",
VU: "VU",
WS: "WS",
YE: "YE",
YT: "YT",
ZA: "ZA",
ZM: "ZM",
ZW: "ZW"
};
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

@@ -1,117 +1,225 @@
// pythonBridge.js
import { ref, onMounted } from 'vue';
const bridge = ref(null);
import { ElMessage } from 'element-plus';
const pyBridge = ref(null);
// 初始化 QWebChannel
const initBridge = () => {
if (/localhost/.test(window.location.href)) return
new QWebChannel(qt.webChannelTransport, (channel) => {
bridge.value = channel.objects.bridge;
pyBridge.value = channel.objects.bridge;
});
};
export function usePythonBridge() {
// 调用 Python 方法
const fetchDataConfig = (data) => {
//删除前端储存数据
const deleteStorageData = (data) => {
return new Promise((resolve, reject) => {
if (bridge.value) {
bridge.value.fetchDataConfig(data, function (result) {
if (pyBridge.value) {
pyBridge.value.deleteAccountInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
// 查询获取主播的数据
const fetchDataCount = () => {
//获取前端储存数据
const getStorageData = (data) => {
return new Promise((resolve, reject) => {
if (bridge.value) {
bridge.value.fetchDataCount(function (result) {
if (pyBridge.value) {
pyBridge.value.readAccountInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//设置前端储存数据
const setStorageData = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.storageAccountInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//设置储存数据
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) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.control_task(data,function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//总数有效数
const getBrotherInfo = () => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.getBrotherInfo(function (result) {
resolve(JSON.parse(result));
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
// 打开tk后台
const loginTikTok = () => {
if (bridge.value) {
bridge.value.loginTikTok(function (result) {
if (pyBridge.value) {
pyBridge.value.loginTikTok(function (result) {
});
}else{
console.log("pyBridge is null未连接上")
}
};
// 登录tk后台
const loginBackStage = (data) => {
if (bridge.value) {
if (data.index == 0) {
bridge.value.loginBackStage(JSON.stringify(data));
} else if (data.index == 1) {
bridge.value.loginBackStageCopy(JSON.stringify(data));
//指定直播间
const Specifystreaming = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.find_big_brother_in_rooms(data,function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//储存直播间id
const setStorageStreamId = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.saveFrontString(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//获取直播间id
const getStorageStreamId = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.loadFrontStringRaw(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
}
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) => {
if (bridge.value) {
bridge.value.givePyAnchorId(id, function (result) {
console.log("id",id);
if (pyBridge.value) {
pyBridge.value.givePyAnchorId(id, function (result) {
});
}
};
//跳转到主播页面
const openAnchorIdRooms = (id) => {
console.log("id",id);
if (pyBridge.value) {
pyBridge.value.openAnchorIdRoom(id, function (result) {
});
}
};
//查询登录状态
const backStageloginStatus = () => {
return new Promise((resolve, reject) => {
if (bridge.value) {
bridge.value.backStageloginStatus(function (result) {
resolve(result);
});
}
});
};
//查询登录状态
const backStageloginStatusCopy = () => {
return new Promise((resolve, reject) => {
if (bridge.value) {
bridge.value.backStageloginStatusCopy(function (result) {
resolve(result);
});
}
});
};
//导出表格
const exportToExcel = (data) => {
if (bridge.value) {
bridge.value.exportToExcel(JSON.stringify(data));
if (pyBridge .value) {
pyBridge .value.exportToExcel(JSON.stringify(data));
}
};
const stopScript = () => {
if (pyBridge .value) {
pyBridge .value.stopScript();
}
};
//获取版本号
const getVersion = () => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.currentVersion(function (result) {
resolve(result);
});
}
});
};
// 在组件挂载时初始化桥接
onMounted(initBridge);
return {
fetchDataConfig,
fetchDataCount,
loginBackStage,
loginTikTok,
exportToExcel,
stopScript,
controlTask,
getBrotherInfo,
getVersion,
givePyAnchorId,
backStageloginStatus,
backStageloginStatusCopy,
exportToExcel
getStorageData,
setStorageData,
deleteStorageData,
Specifystreaming,
setStorageStreamId,
getStorageStreamId,
openAnchorIdRooms,
storageSetInfos,
readSetInfos,
setClipboards
};
}

View File

@@ -1,45 +1,77 @@
export function setToken(token) {
localStorage.setItem('token', token);
import { usePythonBridge} from "@/utils/pythonBridge";
const { getStorageData, setStorageData,deleteStorageData} = usePythonBridge();
import { ElMessage } from 'element-plus';
export async function setToken(token) {
const res = await setStorageData({key: 'token',data: token}).then(res => {
});
}
export function getToken() {
return localStorage.getItem('token');
export async function getToken() {
const res = await getStorageData({ key: 'token' });
return JSON.parse(res);
}
export function removeToken() {
localStorage.removeItem('token');
export async function removeToken() {
const res = deleteStorageData({key: 'token'});
}
export function setUser(user) {
localStorage.setItem('user', JSON.stringify(user));
export async function setUser(user) {
const res = await setStorageData({key: 'user',data: user}).then(res => {
});
}
export function getUser() {
return JSON.parse(localStorage.getItem('user'));
export async function getUser() {
const res = await getStorageData({ key: 'user' });
return JSON.parse(res);
}
export function setNumData(numData) {
localStorage.setItem('num', JSON.stringify(numData));
export function setuserprmisson() {
return new Promise((resolve, reject) => {
const res = getStorageData({ key: 'user' });
resolve(res);
});
}
export function getNumData() {
return JSON.parse(localStorage.getItem('num'));
export async function setNumData(numData) {
const res = await setStorageData({key: 'num',data: numData})
}
export async function getNumData() {
const res = await getStorageData({ key: 'num' });
return JSON.parse(res);
}
// 导出一个函数,用于设置用户密码
export function setUserPass(userdata) {
localStorage.setItem('userPass', JSON.stringify(userdata));
export async function setUserPass(userdata) {
const res = await setStorageData({key: 'userPass',data:userdata})
}
// 导出一个函数,用于获取用户密码
export function getUserPass() {
return JSON.parse(localStorage.getItem('userPass'));
export async function getUserPass() {
const res = await getStorageData({ key: 'userPass' });
return JSON.parse(res);
}
// 用于设置tk账户密码
export function setTkUser(userdata) {
localStorage.setItem('tkuser', JSON.stringify(userdata));
export async function setTkUser(userdata) {
const res = await setStorageData({key: 'tkuser',data: userdata})
}
// 用于获取tk账户密码
export function getTkUser() {
return JSON.parse(localStorage.getItem('tkuser'));
}
export async function getTkUser() {
const res = await getStorageData({ key: 'tkuser' });
return JSON.parse(res);
}
// 用于列表筛选条件
export async function setSerch(data) {
const res = await setStorageData({key: 'Serch',data: data})
}
// 用于获取列表筛选条件
export async function getSerch() {
const res = await getStorageData({ key: 'Serch' });
return JSON.parse(res);
}

View File

@@ -1,295 +1,380 @@
<template>
<div class="main">
<div class="container">
<div class="right">
<img src="../assets/logoBg.png" class="background-video" alt="">
<!-- 设置 -->
<div class="center-align">
<div></div>
<div class="setup">
<div class="setup-item center-justify">
<div></div>
<span>
网络设置
</span>
</div>
<div class="setup-item center-justify">
<div></div>
<span>
简体中文
</span>
</div>
</div>
</div>
<div class="center-line" style="margin-top: 40px;">
<!-- logo -->
<div class="logo">
<div class="center-justify" style="height: 80px; width: 300px;">
<img style="margin-right: 20px;" src="@/assets/logo.png">
<img src="@/assets/logotext.png">
</div>
</div>
<div class="main">
<div class="container">
<div class="right">
<img src="../assets/logoBg.png" class="background-video" alt="" />
<!-- 设置 -->
<div class="center-align">
<div></div>
<div class="setup">
<div class="setup-item center-justify">
<div></div>
<span>{{ $t('common.networkSettings') }}</span>
</div>
<!-- <div class="setup-item center-justify"> -->
<div class="">
<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 class="center-line" style="margin-top: 40px">
<!-- logo -->
<div class="logo">
<!-- <div class="center-justify" style="height: 80px; width: 300px;">
<img style="height: 100%;" src="@/assets/logotext.png">
</div> -->
</div>
<!-- From -->
<div class="from">
<div class="from-title center-justify">
<div>账号登陆</div>
</div>
<!-- From -->
<div class="from">
<div class="from-title center-justify">
<div>{{ $t('common.accountLogin') }}</div>
</div>
<div class="from-input">
<el-form label-position="left" label-width="100px" :model="formData">
<div class="from-input">
<el-form label-position="left" label-width="100px" :model="formData">
<div class="from-input-item1">
<img src="@/assets/username.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.tenantName"
:placeholder="$t('common.tenantName')"
clearable
@keyup.enter="onSubmit"
/>
</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.userId"
:placeholder="$t('common.account')"
clearable
@keyup.enter="onSubmit"
/>
</div>
<div class="from-input-item1">
<img src="@/assets/password.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.password"
type="password"
:placeholder="$t('common.password')"
show-password
@keyup.enter="onSubmit"
/>
</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="">
<el-input style="height: 25px;" v-model="formData.userId" placeholder="账号" clearable
@keyup.enter="onSubmit" />
</div>
<div class="from-input-item1">
<img src="@/assets/password.png" alt="">
<el-input style="height: 25px; " v-model="formData.password" type="password"
placeholder="密码" show-password @keyup.enter="onSubmit" />
</div>
<div class="from-input-item">
<el-button class="loginButton" color="#8f7ee7" type="primary"
@click="onSubmit">登录</el-button>
</div>
</el-form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="from-input-item">
<el-button
class="loginButton"
color="#8f7ee7"
type="primary"
@click="onSubmit"
>{{ $t('common.login') }}</el-button
>
</div>
</el-form>
</div>
</div>
</div>
<div class="version center-justify">{{ $t('common.version') }}{{ version }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { login } from '@/api/account';
import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/utils/storage';
import { ElLoading } from 'element-plus';
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import { useI18n } from 'vue-i18n';
import { login, rentgetloginID } from "@/api/account";
import { getToken, setToken, setUser, setUserPass, getUserPass } from "@/utils/storage";
import { ElLoading } from "element-plus";
import { usePythonBridge } from "@/utils/pythonBridge";
import { ElMessage } from 'element-plus';
import { tokenStore,UserStore } from '@/stores/notice'
const { t, locale } = useI18n();
const tokenCache = tokenStore()
const userCache = UserStore()
const { getVersion } = usePythonBridge();
let version = ref("0.0.0");
// 当前语言
const currentLanguage = ref(localStorage.getItem('language') || 'zh-CN');
onMounted(() => {
setTimeout(() => {
getVersion().then((res) => {
version.value = res;
});
getpassword();
}, 500);
});
async function getpassword(){
const res = await getUserPass();
formData.value = {
tenantName: res == null ? "" : res.tenantName,
userId: res == null ? "" : res.userId,
password: res == null ? "" : res.password,
};
}
// 切换语言
const changeLanguage = (lang) => {
locale.value = lang;
currentLanguage.value = lang;
localStorage.setItem('language', lang);
// 重新加载页面以应用Element Plus语言包
window.location.reload();
};
const router = useRouter();
const formData = ref({
userId: getUserPass() == null ? '' : getUserPass().userId,
password: getUserPass() == null ? '' : getUserPass().password,
});
const formData = ref({});
const onSubmit = () => {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
});
setUserPass(formData.value);
login({
userId: formData.value.userId,
password: formData.value.password,
}).then((res) => {
loading.close();
console.log(res)
if (res) {
if (res.activeYn == 'Y') {
setToken(res.currcode);
setUser(res);
router.push('/nav');
} else {
alert('账号未启用');
}
const loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
});
rentgetloginID({
name: formData.value.tenantName,
})
.then((res) => {
console.log(res);
} else {
alert('账号或密码错误');
}
}).catch((err) => {
loading.close();
});
login({
username: formData.value.userId,
tenantId: res,
password: formData.value.password,
})
.then((res) => {
loading.close();
console.log("123123123123",res);
setToken(res.tokenValue);
tokenCache.setToken(res.tokenValue)
userCache.setUser(res)
setUser(res);
setUserPass(formData.value);
router.push("/nav");
})
.catch((err) => {
loading.close();
});
})
.catch((err) => {
loading.close();
console.log(err);
});
};
</script>
<style lang="less">
.main {
width: 1600px;
height: 900px;
overflow: hidden;
box-sizing: border-box;
width: 1600px;
height: 900px;
overflow: hidden;
box-sizing: border-box;
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.container {
display: flex;
box-sizing: border-box;
width: 1600px;
height: 900px;
.container {
display: flex;
box-sizing: border-box;
width: 1600px;
height: 900px;
.right {
box-sizing: border-box;
position: relative;
width: 1600px;
height: 900px;
padding: 20px 40px 20px 50px;
border-left: 3px solid #23516e;
position: relative;
/* 添加 position: relative */
overflow: hidden;
/* 防止内容溢出 */
.right {
box-sizing: border-box;
position: relative;
width: 1600px;
height: 900px;
padding: 20px 40px 20px 50px;
border-left: 3px solid #23516e;
position: relative;
/* 添加 position: relative */
overflow: hidden;
/* 防止内容溢出 */
.background-video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
/* 确保视频在内容之下 */
}
.version {
color: #fff;
position: absolute;
font-size: 20px;
bottom: 20px;
left: calc(50% - 50px);
// box-sizing: border-box;
// width: 1600px;
}
.setup {
display: flex;
color: #fff;
.background-video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
/* 确保视频在内容之下 */
}
.setup-item {
padding: 10px 6px;
display: flex;
.setup {
display: flex;
align-items: center;
color: #fff;
div {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rgb(255, 255, 255);
margin-right: 5px;
}
}
}
.setup-item {
padding: 10px 6px;
display: flex;
.logo {
padding: 20px 0;
}
div {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rgb(255, 255, 255);
margin-right: 5px;
}
}
}
.from {
width: 420px;
height: 320px;
color: #107A4E;
background-color: #ffffff44;
border-radius: 20px;
border: 1px solid #fff;
padding: 32px;
box-sizing: border-box;
.logo {
padding: 20px 0;
height: 80px;
}
.from-title {
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 24px;
color: #107A4E;
line-height: 37px;
.from {
width: 420px;
// height: 320px;
color: @bg-color;
background-color: #ffffff44;
border-radius: 20px;
border: 1px solid #fff;
padding: 32px;
box-sizing: border-box;
.from-title {
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 24px;
color: @bg-color;
line-height: 37px;
div {
font-size: 20px;
font-weight: 600;
// border-bottom: 4px solid #1db97d;
}
}
div {
font-size: 20px;
font-weight: 600;
// border-bottom: 4px solid #1db97d;
}
}
.from-input {
width: 100%;
padding: 15px 0;
.from-input {
width: 100%;
padding: 15px 0;
.from-input-item {
display: flex;
padding: 8px 0;
.from-input-item {
display: flex;
padding: 8px 0;
.from-input-item-title {
color: #107A4E;
font-size: 18px;
font-weight: 500;
width: 80px;
height: 50px;
}
.from-input-item-title {
color: @bg-color;
font-size: 18px;
font-weight: 500;
width: 80px;
height: 50px;
}
.loginButton {
width: 359px;
height: 50px;
background: #FFFFFF;
border-radius: 24px;
border: 1px solid #FFFFFF;
.loginButton {
width: 359px;
height: 50px;
background: #ffffff;
border-radius: 24px;
border: 1px solid #ffffff;
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 18px;
color: @bg-color;
line-height: 37px;
}
}
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 18px;
color: #107A4E;
line-height: 37px;
}
}
.from-input-item1 {
display: flex;
width: 359px;
height: 50px;
background: rgba(147, 174, 158, 0.37);
border-radius: 24px;
border: 1px solid #FFFFFF;
padding: 12px 25px 13px 25px;
box-sizing: border-box;
margin-bottom: 16px;
}
}
}
}
}
.from-input-item1 {
display: flex;
width: 359px;
height: 50px;
background: @bg-color-light-light;
border-radius: 24px;
border: 1px solid #ffffff;
padding: 12px 25px 13px 25px;
box-sizing: border-box;
margin-bottom: 16px;
}
}
}
}
}
}
.center-line {
display: flex;
flex-direction: column;
align-items: center;
display: flex;
flex-direction: column;
align-items: center;
}
.center-justify {
display: flex;
justify-content: space-around;
align-items: center;
display: flex;
justify-content: space-around;
align-items: center;
}
.center-align {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
}
.center-flex {
display: flex;
justify-content: center;
align-items: center;
display: flex;
justify-content: center;
align-items: center;
}
.el-input__wrapper {
--el-input-focus-border-color: rgba(255, 255, 0, 0);
--el-menu-hover-bg-color: rgba(255, 255, 0, 0);
--el-input-focus-border-color: rgba(255, 255, 0, 0);
--el-menu-hover-bg-color: rgba(255, 255, 0, 0);
}
</style>
<style scoped>
<style scoped lang="less">
::v-deep(.el-input__wrapper) {
background-color: rgba(255, 0, 0, 0);
box-shadow: none;
background-color: rgba(255, 0, 0, 0);
box-shadow: none;
}
::v-deep(.el-input__inner) {
color: #107A4E;
color: #fff;
}
::v-deep(.el-input__inner::placeholder) {
color: #107A4E;
color: @bg-color;
}
</style>
</style>

77
src/views/hosts/Home.vue Normal file
View File

@@ -0,0 +1,77 @@
<template>
<div class="home">
<!-- <div id="main" class="main"></div> -->
<div class="noData">
暂无数据
</div>
</div>
</template>
// <script setup>
// import { ref, watch, onMounted, onUpdated, onUnmounted } from "vue";
// const refname = ref('');
// const echarts = require('echarts/lib/echarts');
// require('echarts/lib/component/grid');
// require('echarts/lib/chart/bar');
// const initChart = () => {
// var chartDom = document.getElementById('main');
// var myChart = echarts.init(chartDom);
// var option = {
// xAxis: {
// type: 'category',
// data: ['用户量', '123', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
// },
// yAxis: {
// type: 'value'
// },
// series: [
// {
// data: [120, 200, 150, 80, 70, 110, 130],
// type: 'bar'
// }
// ]
// };
// myChart.setOption(option);
// };
// watch(refname, (newQuestion) => {
// // 变化后执行
// // 如果 refname 的变化影响图表数据,可以在这里更新 option 并调用 myChart.setOption(option)
// });
// onMounted(() => {
// initChart();
// });
// onUpdated(() => {
// // 组件更新后执行
// // 如果需要根据更新重新初始化图表或更新数据,可以在这里添加逻辑
// });
// onUnmounted(() => {
// // 组件销毁前执行
// // 如果需要销毁图表实例以释放资源,可以在这里添加逻辑
// });
</script>
<style scoped>
.home {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.noData{
font-size: 20px;
color: #999999;
}
.main{
width: 40%;
height: 40%;
}
</style>

View File

@@ -0,0 +1,288 @@
<template>
<div class="language-management">
<div class="content">
<div class="languagetitle">
<div class="title">语言管理</div>
<div class="add" @click="dialogVisible = true">
<img class="add-img" src="@/assets/plus2.png" alt="" />
</div>
</div>
<!-- 语言列表 -->
<div class="languagelist">
<el-table
:data="tableData"
stripe
style="width: 100%; margin-top: 10px"
max-height="700"
>
<el-table-column prop="language" label="语言名称" />
<el-table-column prop="" label="" />
<el-table-column prop="" label="" />
<el-table-column prop="" label="" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">
修改
</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<!-- 编辑弹窗 -->
<el-drawer v-model="dialogVisible" :with-header="false" @close="dialogVisible = false, languageNamedeta = {}">
<div class="dialog-content">
<div class="dialog-title">语言{{languageNamedeta.id? '编辑' : '新增'}}</div>
<el-input
v-model="languageNamedeta.language"
style="width: 100%; margin-top: 20px"
:rows="3"
type="textarea"
placeholder="请输入语言内容"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="languageSave">保存</div>
<div class="dialogCancel" @click="dialogVisible = false">取消</div>
</div>
</div>
</el-drawer>
</template>
<script setup>
import { ElMessage, ElMessageBox } from "element-plus";
import { getLanguageList,deleteLanguage,editLanguage,addLanguage} from "@/api/account";
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const tableData = ref([]);
const dialogVisible = ref(false);
let languageNamedeta = ref({});// 编辑添加弹窗数据
// 编辑弹窗
function handleEdit(index, row) {
dialogVisible.value = true;
languageNamedeta.value = row;
}
// 保存语言
function languageSave() {
if (languageNamedeta.value.language == null) {
ElMessage({
type: "error",
message: "请输入语言内容",
});
return;
}
if (languageNamedeta.value.id) {
// 修改语言
editLanguage(languageNamedeta.value)
.then(() => {
ElMessage({
type: "success",
message: "修改成功",
});
getLanguageListData();
})
.catch(() => {
ElMessage({
type: "error",
message: "修改失败",
});
});
dialogVisible.value = false;
}else{
// 新增语言
addLanguage(languageNamedeta.value)
.then(() => {
ElMessage({
type: "success",
message: "添加成功",
});
getLanguageListData();
})
.catch(() => {
ElMessage({
type: "error",
message: "添加失败",
});
});
dialogVisible.value = false;
}
}
// 删除语言
function handleDelete(index, row) {
ElMessageBox.confirm("您确认要删除这个语言吗?")
.then(() => {
deleteLanguage({ id: row.id })
.then(() => {
ElMessage({
type: "success",
message: "删除成功",
});
getLanguageListData();
})
.catch(() => {
ElMessage({
type: "error",
message: "删除失败",
});
});
})
.catch(() => {});
}
function getLanguageListData() {
// 获取语言列表
getLanguageList()
.then((res) => {
console.log(res);
tableData.value = res;
}).catch(() => {
ElMessage({
type: "error",
message: "获取语言列表失败",
});
});
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
getLanguageListData();
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.language-management {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.content {
width: 95%;
height: 95%;
background-color: #f5f5f5;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.3);
}
.languagetitle {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
background-color: #e2e2e2;
border-radius: 10px;
}
.title {
font-size: 24px;
font-weight: bold;
color: #999999;
margin-left: 40px;
}
.add {
width: 150px;
height: 50px;
background-color: #cccccc;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.add:hover {
background-color: #999999;
}
.add-img {
width: 40px;
height: 40px;
}
.languagelist {
width: 97%;
height: 85%;
background-color: #ffffff;
margin-bottom: 20px;
border-radius: 10px;
overflow: auto;
scrollbar-width: none;
}
.dialog-content {
width: 100%;
height: 100%;
}
.dialog-title {
width: 100%;
height: 50px;
font-size: 25px;
color: #b4b4b4;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
}
.dialog-btn {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.dialogSave {
width: 45%;
height: 50px;
background-color: #d6d6d6;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #ffffff;
}
.dialogSave:hover {
background-color: #b4b4b4;
}
.dialogCancel {
width: 45%;
height: 50px;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #d6d6d6;
border: 1px solid #d6d6d6;
}
.dialogCancel:hover {
background-color: #e7e6e6;
color: #ffffff;
}
</style>

View File

@@ -0,0 +1,416 @@
<template>
<div class="scriptManagement">
<div class="wordslist">
<!-- 话术列表 -->
<div class="wordscontent" ref="containerRef">
<div
class="words"
v-for="(item, index) in LanguageData"
:key="index"
:id="item.language"
>
<div class="wordsname">{{ item.language }}</div>
<div class="wordscontents">
<div
class="wordscontentstext"
v-for="(items, indexs) in item.content"
:key="index"
>
<div>{{ indexs + 1 + ".&nbsp;" + items.content }}</div>
<div class="wordscontentstextbtn">
<div style="margin-right: 10px" @click="handleEditor(items.id)">编辑</div>
<div @click="handleDelete(items.id)">删除</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="Languagelist">
<div class="Languagecontent">
<!-- 话术添加 -->
<div class="addwords" @click="wordsAddStatus = true">
<img class="addwordsimg" src="@/assets/plus.png" />
</div>
<!-- 语言选择 -->
<el-anchor
class="Languageanchor"
:container="containerRef"
direction="vertical"
type="default"
:offset="30"
@click="handleClick"
>
<el-anchor-link
class="anchorLink"
v-for="(item, index) in getLanguageListData"
:href="'#' + item.language"
:title="item.language"
:key="index"
/>
</el-anchor>
</div>
</div>
</div>
<!-- 话术编辑 -->
<el-drawer v-model="dialogVisible" :with-header="false">
<div class="dialog-content">
<div class="dialog-title">话术编辑</div>
<el-input
v-model="handletextareadeta.content"
style="width: 100%; margin-top: 20px"
:rows="25"
type="textarea"
placeholder="请输入话术内容"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="handleSave">保存</div>
<div class="dialogCancel" @click="dialogVisible = false">取消</div>
</div>
</div>
</el-drawer>
<!-- 话术添加 -->
<el-drawer v-model="wordsAddStatus" :with-header="false">
<div class="dialog-content">
<div class="dialog-title">话术编辑</div>
<el-select v-model="wordsAddData.language" placeholder="请选择语言">
<el-option
v-for="item in LanguageData"
:key="item.language"
:label="item.language"
:value="item.language"
/>
</el-select>
<el-input
v-model="wordsAddData.content"
style="width: 100%; margin-top: 20px"
:rows="25"
type="textarea"
placeholder="请输入话术内容"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="handleSaveWordsAdd">保存</div>
<div class="dialogCancel" @click="wordsAddStatus = false">取消</div>
</div>
</div>
</el-drawer>
</template>
<script setup>
import {
getDialogList,
getLanguageList,
editDialog,
deleteDialog,
addDialog,
} from "@/api/account";
import { ElMessage, ElMessageBox } from "element-plus";
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const containerRef = ref(null);
const getDialogListData = ref([]);
const getLanguageListData = ref([]);
const LanguageData = ref([]);
const wordsAddStatus = ref(false);
const wordsAddData = ref({
language: "",
content: "",
});
const handleClick = (e) => {
e.preventDefault();
};
//数据处理
function mergeArrays(arr1, arr2) {
const result = [];
const languages = new Set([...arr1.map(i => i.language), ...arr2.map(i => i.language)]);
for (const lang of languages) {
const contents = arr1.filter(item => item.language === lang)
.map(item => ({ id: item.id, content: item.content }));
result.push({
language: lang,
content: contents
});
}
return result;
}
const dialogVisible = ref(false);
const handletextareadeta = ref({});
//添加话术
function handleSaveWordsAdd() {
console.log("handleSaveWordsAdd",wordsAddData.value);
if (wordsAddData.value.language === "" || wordsAddData.value.content === "") {
ElMessage({
type: "error",
message: "请填写完整信息",
});
return;
}
wordsAddStatus.value = false;
addDialog(wordsAddData.value)
.then(() => {
ElMessage({
type: "success",
message: "添加成功",
});
getLanguageListDataFn();
})
.catch(() => {
ElMessage({
type: "error",
message: "添加失败",
});
});
}
// 编辑
function handleEditor(id) {
dialogVisible.value = true;
handletextareadeta.value = getDialogListData.value.find((item) => item.id === id);
}
// 保存
function handleSave() {
editDialog({
id: handletextareadeta.value.id,
content: handletextareadeta.value.content,
})
.then(() => {
ElMessage({
type: "success",
message: "保存成功",
});
getLanguageListDataFn();
dialogVisible.value = false;
})
.catch(() => {
ElMessage({
type: "error",
message: "保存失败",
});
});
}
// 删除
function handleDelete(id) {
ElMessageBox.confirm("您确认要删除这条话术吗?")
.then(() => {
deleteDialog({ id })
.then(() => {
ElMessage({
type: "success",
message: "删除成功",
});
getLanguageListDataFn();
})
.catch(() => {
ElMessage({
type: "error",
message: "删除失败",
});
});
})
.catch(() => {});
}
function getLanguageListDataFn() {
getDialogList().then((res) => {
getDialogListData.value = res;
getLanguageList().then((res) => {
getLanguageListData.value = res;
LanguageData.value = mergeArrays(
getDialogListData.value,
getLanguageListData.value
);
console.log("LanguageData", LanguageData.value);
});
});
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
getLanguageListDataFn();
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.scriptManagement {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
}
.wordslist {
width: 87%;
height: 97%;
padding: 1% 1%;
/* background-color: #00ff40; */
}
.Languagelist {
height: 97%;
width: 10%;
padding: 1% 1% 1% 0%;
/* background-color: #8d0000; */
}
.wordscontent {
height: 100%;
width: 100%;
border-radius: 10px;
background-color: rgb(243, 243, 243);
overflow-y: auto;
display: flex;
flex-direction: column;
align-items: center;
scrollbar-width: none;
}
.Languagecontent {
height: 100%;
width: 100%;
border-radius: 10px;
background-color: rgb(243, 243, 243);
display: flex;
flex-direction: column;
overflow-y: auto;
scrollbar-width: none; /* 隐藏滚动条 */
}
.addwords {
width: 100%;
height: 50px;
background-color: rgb(226, 226, 226);
display: flex;
justify-content: center;
align-items: center;
}
.addwords:hover {
background-color: rgb(214, 214, 214);
}
.addwordsimg {
width: 30px;
height: 30px;
}
.Language {
height: 50px;
width: 100%;
background-color: rgb(226, 226, 226);
margin-top: 20px;
}
.words {
width: 95%;
margin-top: 20px;
margin-bottom: 20px;
}
.Languageanchor {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgb(243, 243, 243);
}
.anchorLink {
width: 100px;
height: 30px;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
font-size: 16px;
line-height: 30px;
border-radius: 5px;
margin-left: -0.875vw;
}
.wordsname {
font-size: 18px;
color: #c9c9c9;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
}
.wordscontents {
width: 95%;
/* min-height: 200px; */
/* background-color: #ffffff; */
border-radius: 10px;
margin-left: 30px;
}
.wordscontentstext {
font-size: 16px;
color: #7a7a7a;
line-height: 24px;
margin-top: 10px;
margin-bottom: 10px;
text-align: left;
word-wrap: break-word;
word-break: break-all;
display: flex;
justify-content: space-between;
align-items: center;
}
.wordscontentstextbtn {
display: flex;
}
.dialog-content {
width: 100%;
height: 100%;
}
.dialog-title {
width: 100%;
height: 50px;
font-size: 25px;
color: #b4b4b4;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
}
.dialog-btn {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.dialogSave {
width: 45%;
height: 50px;
background-color: #d6d6d6;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #ffffff;
}
.dialogSave:hover {
background-color: #b4b4b4;
}
.dialogCancel {
width: 45%;
height: 50px;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #d6d6d6;
border: 1px solid #d6d6d6;
}
.dialogCancel:hover {
background-color: #e7e6e6;
color: #ffffff;
}
/* 样式定义 */
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
<template>
<div class="miniAM">
<div class="noData">
暂无数据
</div>
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.miniAM {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.noData{
font-size: 20px;
color: #999999;
}
/* 样式定义 */
</style>

View File

@@ -0,0 +1,315 @@
<template>
<div class="mini-integral">
<div class="content">
<!-- 积分配置列表 -->
<div class="integral-list">
<el-table
:data="tableData"
stripe
style="width: 100%; margin-top: 10px"
max-height="700"
>
<el-table-column prop="functionName" label="事件" />
<el-table-column prop="" label="" />
<el-table-column prop="functionValue" label="积分" />
<el-table-column prop="" label="" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="miniIntegralEdit(scope.$index, scope.row)">
修改
</el-button>
<el-button
size="small"
type="danger"
@click="miniIntegralDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 积分配置 -->
<div class="integral-title">
<div class="add" @click="dialogVisible = true">
<img class="add-img" src="@/assets/plus2.png" alt="">
</div>
<div class="integral-text">
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
</div>
</div>
</div>
</div>
<!-- 新增修改积分配置弹窗 -->
<el-drawer v-model="dialogVisible" :with-header="false" @close="dialogVisible = false, miniIntegraldeta = {}">
<div class="dialog-content">
<div class="dialog-title">事件{{miniIntegraldeta.id? '编辑' : '新增'}}</div>
<el-input
v-if="!miniIntegraldeta.id"
v-model="miniIntegraldeta.functionName"
style="width: 100%; margin-top: 20px"
:rows="3"
type="textarea"
placeholder="请输入事件名称"
/>
<el-input
v-model="miniIntegraldeta.functionValue"
style="width: 100%;height: 50px; margin-top: 20px"
type="number"
placeholder="请输入积分值"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="miniIntegralAddSave">保存</div>
<div class="dialogCancel" @click="dialogVisible = false">取消</div>
</div>
</div>
</el-drawer>
</template>
<script setup>
import { ElMessage, ElMessageBox } from "element-plus";
import {
getWxConfigList,
addWxConfig,
editWxConfig,
deleteWxConfig,
} from "@/api/account";
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const tableData = ref([]);
const dialogVisible = ref(false);
const miniIntegraldeta = ref({});
//保存积分配置
function miniIntegralAddSave() {
if (!miniIntegraldeta.value.functionName) {
ElMessage({
type: "error",
message: "事件名称不能为空",
});
return;
}
if (!miniIntegraldeta.value.functionValue) {
ElMessage({
type: "error",
message: "积分值不能为空",
});
return;
}
if (miniIntegraldeta.value.id) {
// 修改积分配置
editWxConfig(miniIntegraldeta.value)
.then(() => {
ElMessage({
type: "success",
message: "修改成功",
});
dialogVisible.value = false;
miniIntegraldeta.value = {};
getIntegralList()
})
.catch(() => {
ElMessage({
type: "error",
message: "修改失败",
});
});
} else {
// 新增积分配置
addWxConfig(miniIntegraldeta.value)
.then(() => {
ElMessage({
type: "success",
message: "新增成功",
});
dialogVisible.value = false;
miniIntegraldeta.value = {};
getIntegralList()
})
.catch(() => {
ElMessage({
type: "error",
message: "新增失败",
});
});
}
}
// 新增积分配置弹窗
function miniIntegralEdit(index, row) {
dialogVisible.value = true;
miniIntegraldeta.value = row;
}
//删除积分配置
function miniIntegralDelete(index, row) {
ElMessageBox.confirm("您确认要删除这个积分配置吗?")
.then(() => {
deleteWxConfig({ id: row.id })
.then(() => {
ElMessage({
type: "success",
message: "删除成功",
});
getIntegralList()
})
.catch(() => {
ElMessage({
type: "error",
message: "删除失败",
});
});
})
.catch(() => {});
}
// 获取积分配置列表
function getIntegralList() {
getWxConfigList()
.then((res) => {
tableData.value = res;
})
.catch(() => {
ElMessage({
type: "error",
message: "获取积分配置列表失败",
});
});
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
getIntegralList();
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.mini-integral {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content {
width: 80%;
height: 80%;
background-color: #f5f5f5;
border-radius: 10px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.3);
padding: 40px;
}
.integral-list{
width: 87%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
}
.integral-title{
width: 10%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
}
.add{
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: #c4c4c4;
border-radius: 10px 10px 0 0;
}
.add:hover{
background-color: #a9a9a9;
}
.add-img{
width: 40px;
height: 40px;
}
.integral-text{
width: 100%;
height: 520px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 20px;
color: #c4c4c4;
font-size: 25px;
font-family: "SimSun", "Microsoft YaHei", sans-serif;
}
.h1{
margin-top: 10px;
margin-bottom: 10px;
}
.dialog-title {
width: 100%;
height: 50px;
font-size: 25px;
color: #b4b4b4;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
}
.dialog-btn {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.dialogSave {
width: 45%;
height: 50px;
background-color: #d6d6d6;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #ffffff;
}
.dialogSave:hover {
background-color: #b4b4b4;
}
.dialogCancel {
width: 45%;
height: 50px;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #d6d6d6;
border: 1px solid #d6d6d6;
}
.dialogCancel:hover {
background-color: #e7e6e6;
color: #ffffff;
}
/* 样式定义 */
</style>

View File

@@ -3,13 +3,15 @@
<div class="center-align" style="width: 100%; margin: 0 20px;">
<div class="box-card-num1 center-line">
<div>总数量: <span>{{ hostData.totalCount }}</span></div>
<div>有效主播: <span>{{ hostData.validAnchorsCount }}</span></div>
<div>新建主播: <span>{{ hostData.validAnchorsCount }}</span></div>
<div> 已查询: <span>{{ hostData.checkedDataCount }}</span></div>
<div>可邀请: <span>{{ hostData.canInvitationCount }}</span></div>
<div>运行时间: <span>{{ formattedTime }}</span></div>
</div>
<div class="center-line" style="padding-top: 15vh;">
<el-button class="open-login" type="primary" @click="openTK">开启tk</el-button>
<!-- <el-button class="open-login" type="primary" @click="startTimer">计时开始</el-button> -->
</div>
<div>
@@ -20,21 +22,21 @@
<div class="from-input-item-title center-justify">
公会账号
</div>
<el-input :disabled="!(isTk && tkData[index].code == 0)" v-model="tkData[index].account"
placeholder="请输入登录账号" clearable />
<el-input :disabled="!(tkData[index].code == 0 && !isLogin[index])"
v-model="tkData[index].account" placeholder="请输入登录账号" clearable />
</div>
<div class="from-input-item">
<div class="from-input-item-title center-justify">
公会密码
</div>
<el-input :disabled="!(isTk && tkData[index].code == 0)" v-model="tkData[index].password"
type="password" placeholder="请输入登录密码" show-password />
<el-input :disabled="!(tkData[index].code == 0 && !isLogin[index])"
v-model="tkData[index].password" type="password" placeholder="请输入登录密码" show-password />
</div>
<el-button class="open-login" style="margin-left: 60px;"
:disabled="!(isTk && tkData[index].code == 0)" type="primary"
@click="loginTK(index)">登录tk</el-button>
:disabled="!(tkData[index].code == 0 && !isLogin[index])" type="primary"
@click="loginTK(index)">登录后台</el-button>
<div v-if="tkData[index].code == 0" class="loginState"></div>
<div v-if="tkData[index].code == 1" class="loginState" style="background-color: green;"></div>
@@ -86,14 +88,14 @@
<el-col :span="8">
<div class="input-group">
<label>后台查询频率</label>
<el-input type='number' v-model="pyData.frequency.hour" :min="0"
:max="pyData.frequency.day - 1" placeholder="次/小时" style="width: 100%"
:disabled="!pyData.isStart">
<!-- <el-input type='number' v-model="pyData.frequency.hour" @input="handleInputHour" -->
<el-input type='number' v-model="pyData.frequency.hour" placeholder="次/小时"
style="width: 100%" :disabled="!pyData.isStart">
<template #append>/小时</template>
</el-input>
<el-input type='number' v-model="pyData.frequency.day" :min="pyData.frequency.hour + 1"
:max="100" placeholder="次/24小时" style="width: 100%; margin-top: 10px"
:disabled="!pyData.isStart">
<!-- <el-input type='number' v-model="pyData.frequency.day" @input="handleInputDay" -->
<el-input type='number' v-model="pyData.frequency.day" placeholder="次/24小时"
style="width: 100%; margin-top: 10px" :disabled="!pyData.isStart">
<template #append>/24小时</template>
</el-input>
</div>
@@ -113,12 +115,13 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, computed } from 'vue';
import { usePythonBridge, } from '@/utils/pythonBridge'
import { setNumData, getNumData, getUser, setTkUser, getTkUser } from '@/utils/storage'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getCountryName } from '@/utils/countryUtil'
import { tkaccountuseinfo } from '@/api/account'
//导入python交互方法
const { fetchDataConfig, fetchDataCount, loginBackStage, loginTikTok, backStageloginStatus, backStageloginStatusCopy } = usePythonBridge();
@@ -132,15 +135,27 @@ let hostData = ref({
validAnchorsCount: 0,
canInvitationCount: 0,
checkedDataCount: 0,
});
//是否开启tk
let isTk = ref(true);
// let isTk = ref(true);
//账号是否登陆中
let isLogin = ref([false, false]);
//设置状态轮询定时器
let statusTimer = ref(null);
let statusTimerCopy = ref(null);
//设置次数最大值
let maxCount = ref([
{
hourMax: 50,
dayMax: 300,
},
{
hourMax: 100,
dayMax: 600,
},
]);
//tk账号信息
let tkData = ref([
@@ -157,9 +172,7 @@ let tkData = ref([
index: 2,
code: 0,
num: 0
},
]);
//python需要的数据
@@ -176,7 +189,8 @@ let pyData = ref({
});
//按钮提交状态
let submitting = ref(false);
let submitting = ref(true);
onMounted(() => {
//从缓存获取数据
@@ -193,7 +207,6 @@ onMounted(() => {
tkaccountuse(tkData.value[1].account, 1)
getIpInfo()
//查询次数查询
})
@@ -210,6 +223,22 @@ const getIpInfo = async () => {
countryData.value = getCountryName(data.country);
} catch (error) {
console.error('请求出错:', error);
ElMessageBox.prompt('请输入将要获取国家的中文名', '获取国家失败', {
confirmButtonText: '确认',
cancelButtonText: '取消',
showClose: false,
closeOnClickModal: false,
showCancelButton: false,
})
.then(({ value }) => {
countryData.value = value
})
// .catch(() => {
// ElMessage({
// type: 'info',
// message: 'Input canceled',
// })
// })
}
};
@@ -217,12 +246,26 @@ const getIpInfo = async () => {
const submit = () => {
pyData.value.country = countryData.value;
console.log('提交的区间值:', pyData.value);
if (tkData.value[0].account == '' || tkData.value[1].account == '') {
ElMessage.error('请输入账号密码');
// if (tkData.value[0].account == '' && tkData.value[1].account == '') {
// ElMessage.error('请输入账号密码');
// return;
// }
// if (tkData.value[0].password == '' && tkData.value[1].password == '') {
// ElMessage.error('请输入账号密码');
// return;
// }
if (((Number(pyData.value.gold.min) > Number(pyData.value.gold.max)) || (Number(pyData.value.fans.min) > Number(pyData.value.fans.max)))) {
ElMessage.error('请输入正确的区间值');
return;
}
if (tkData.value[0].password == '' || tkData.value[1].password == '') {
ElMessage.error('请输入账号密码');
if ((Number(pyData.value.gold.max) <= 0 || Number(pyData.value.fans.max <= 0)) || pyData.value.gold.max == '' || pyData.value.fans.max == '') {
ElMessage.error('请输入正确的区间值');
return;
}
if (Number(pyData.value.frequency.hour) <= 0 || Number(pyData.value.frequency.day) <= 0 || pyData.value.frequency.hour == '' || pyData.value.frequency.day == '') {
ElMessage.error('请输入正确的频率区间值');
return;
}
ElMessageBox.confirm(
@@ -250,14 +293,16 @@ const submit = () => {
tenantId: getUser().tenantId,
userId: getUser().userId,
})).then((res) => {
//开始计时器
startTimer();
//开启查询次数
getHostTimer.value = setInterval(() => {
fetchDataCount().then((res) => {
hostData.value = JSON.parse(res);
tkaccountuse(tkData.value[0].account, 0)
tkaccountuse(tkData.value[1].account, 1)
})
}, 1000);
}, 5000);
}).finally(() => {
@@ -287,6 +332,7 @@ const unsubmit = () => {
tenantId: getUser().tenantId,
userId: getUser().userId,
})).then((res) => {
pauseTimer();
pyData.value.isStart = true;
clearInterval(getHostTimer.value);
getHostTimer.value = null;
@@ -312,10 +358,12 @@ const loginTK = (index) => {
index: index
})
if (index == 0) {
isLogin.value[1] = true;
statusTimer = setInterval(() => {
getloginStatus();
}, 2000)
} else if (index == 1) {
isLogin.value[0] = true;
statusTimerCopy = setInterval(() => {
getloginStatusCopy();
}, 2000)
@@ -325,8 +373,8 @@ const loginTK = (index) => {
}
const openTK = () => {
isTk.value = true;
console.log(isTk.value)
// isTk.value = true;
// console.log(isTk.value)
loginTikTok();
}
@@ -339,6 +387,8 @@ function getloginStatus() {
if (data.code == 1) {
clearInterval(statusTimer);
statusTimer = null;
submitting.value = false
isLogin.value[1] = false;
}
})
}
@@ -350,6 +400,8 @@ function getloginStatusCopy() {
if (data.code == 1) {
clearInterval(statusTimer);
statusTimer = null;
submitting.value = false
isLogin.value[0] = false;
}
})
}
@@ -357,13 +409,114 @@ function getloginStatusCopy() {
function tkaccountuse(id, index) {
let num = 0;
tkaccountuseinfo({ userId: id }).then((res) => {
num = res
tkData.value[index].num = num
console.log('账号使用次数', tkData.value[index].num)
tkaccountuseinfo(id).then((res) => {
if (res) {
num = res
tkData.value[index].num = num
console.log('账号使用次数', tkData.value[index].num)
}
}).catch((err) => {
console.log('账号使用次数', err)
})
}
const isRunning = ref(false);
const totalSeconds = ref(0);
//定时器
let timerCrawl = null;
const startTimedata = ref(null);
//清空时间 并开始运行
const startTimer = () => {
resetTimer();
if (isRunning.value) return;
isRunning.value = true;
startTimedata.value = Date.now();
timerCrawl = setInterval(() => {
totalSeconds.value = Math.floor((Date.now() - startTimedata.value) / 1000);
}, 1000);
};
//结束运行 暂停
const pauseTimer = () => {
isRunning.value = false;
clearInterval(timerCrawl);
};
//清空时间
const resetTimer = () => {
isRunning.value = false;
clearInterval(timerCrawl);
totalSeconds.value = 0;
};
// 格式化时间为 HH:MM:SS
const formattedTime = computed(() => {
const hours = Math.floor(totalSeconds.value / 3600);
const minutes = Math.floor((totalSeconds.value % 3600) / 60);
const seconds = totalSeconds.value % 60;
return [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
seconds.toString().padStart(2, '0')
].join(':');
});
function handleInputHour(value) {
console.log(value)
// 替换非数字字符为空字符串
let num = value.replace(/[^\d]/g, '');
// 如果值小于等于0则设置为0
if (Number(num) <= 0) {
num = 0;
}
if ((tkData.value[0].code == 1) && (tkData.value[1].code == 1)) {
if (Number(num) > maxCount.value[1].hourMax) {
num = maxCount.value[1].hourMax;
}
} else if ((tkData.value[0].code == 1) || (tkData.value[1].code == 1)) {
// 如果值大于最大值,则设置为最大值
if (Number(num) > maxCount.value[0].hourMax) {
num = maxCount.value[0].hourMax;
}
} else {
ElMessage.error('请先登录tk后台');
num = 0;
}
// 更新模型
pyData.value.frequency.hour = num;
}
function handleInputDay(value) {
console.log(value)
// 替换非数字字符为空字符串
let num = value.replace(/[^\d]/g, '');
// 如果值小于等于0则设置为0
if (Number(num) <= 0) {
num = 0;
}
if ((tkData.value[0].code == 1) && (tkData.value[1].code == 1)) {
if (Number(num) > maxCount.value[1].dayMax) {
num = maxCount.value[1].dayMax;
}
} else if ((tkData.value[0].code == 1) || (tkData.value[1].code == 1)) {
// 如果值大于最大值,则设置为最大值
if (Number(num) > maxCount.value[0].dayMax) {
num = maxCount.value[0].dayMax;
}
} else {
ElMessage.error('请先登录tk后台');
num = 0;
}
// 更新模型
pyData.value.frequency.day = num;
}
</script>
<style scoped lang="less">
@@ -398,7 +551,7 @@ function tkaccountuse(id, index) {
background: #FFFFFF;
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
border-radius: 24px;
padding-top: 60px;
// padding-top: 60px;
box-sizing: border-box;
div {
@@ -498,7 +651,7 @@ label {
.open-login {
width: 100px;
height: 47px;
background: #E7CA92;
background: @btn-bg-color;
border-radius: 10px;
border: none;
}
@@ -506,7 +659,7 @@ label {
.reset-button {
width: 132px;
height: 47px;
background: #E7CA92;
background: @btn-bg-color;
border-radius: 10px;
font-family: Source Han Sans SC;
@@ -520,7 +673,7 @@ label {
.submit-button {
width: 160px;
height: 47px;
background: #338F6A;
background: @bg-color;
border-radius: 10px;
}
@@ -551,9 +704,9 @@ label {
</style>
<style scoped>
<style scoped lang="less">
::v-deep(.el-input-group__prepend) {
background: #84CEB2;
background: @bg-color-light;
border-radius: 10px 0px 0px 10px;
border: 1px solid #B7CEC5;
font-family: Source Han Sans SC;
@@ -564,7 +717,7 @@ label {
}
::v-deep(.el-input-group__append) {
background: #84CEB2;
background: @bg-color-light;
border-radius: 0px 10px 10px 0px;
border: 1px solid #B7CEC5;
font-family: Source Han Sans SC;

View File

@@ -1,58 +1,79 @@
<template>
<div class="app-container">
<Sidebar class="noneText" @activeIndex="activeIndexFn" />
<!-- <Sidebar class="noneText" @activeIndex="activeIndexFn" /> -->
<div class="content ">
<div v-show="activeIndex == 1">
<workbenches v-if="openWerk" />
<!-- <div v-show="activeIndexA == 1">
<workbenches />
</div> -->
<div>
<hostsList />
</div>
<div v-show="activeIndex == 2">
<hostsList v-if="openList" />
</div>
<!-- <div style="position: absolute; bottom: 0; right: 0;">{{ version }}</div> -->
</div>
<div class="footer">
{{ $t('common.expirationtime') }}{{ time }}
</div>
</div>
</template>
<script setup>
import Sidebar from '../components/Sidebar.vue';
import { useI18n } from 'vue-i18n';
import { ref, reactive, onMounted } from "vue";
// import Sidebar from '../components/Sidebar.vue';
import { RouterLink, RouterView } from 'vue-router'
import hostsList from '@/views/hosts/hostsList.vue'
import workbenches from '@/views/hosts/workbenches.vue'
import { ref } from 'vue'
import { getUser } from '@/utils/storage'
let userType = ref(getUser().userType)
let activeIndex = ref(userType.value == 3 ? 1 : 2)
let openWerk = ref(userType.value == 3 ? true : false)
let openList = ref(userType.value == 3 ? false : true)
console.log("用户等级", getUser().userType)
function activeIndexFn(data) {
activeIndex.value = data
openWerk.value = true
openList.value = true
console.log(data)
import { ElMessage } from 'element-plus';
import { getUser } from "@/utils/storage";
// import workbenches from '@/views/hosts/workbenches.vue'
import { tokenStore,UserStore } from '@/stores/notice'
const userCache = UserStore()
const { t } = useI18n();
const time = ref(formatTimestamp(userCache.user.brotherExpireTime))
// 时间格式化方法 - 将12位时间戳转为YYYY-MM-DD HH:mm:ss格式
function formatTimestamp(timestamp) {
try {
// 转换为数字
const ts = Number(timestamp);
if (isNaN(ts)) {
return '--';
}
// 处理13位时间戳毫秒级
const date = new Date(ts > 999999999999 ? ts : ts * 1000);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} catch (e) {
console.error('时间格式化错误:', e);
return '--';
}
}
</script>
<style>
<style lang="less">
body,
html {
margin: 0;
padding: 0;
height: 100%;
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.app-container {
display: flex;
width: 1600px;
height: 900px;
background-color: #338F6A;
background-color: @bg-color;
position: relative;
}
@@ -67,15 +88,17 @@ html {
.sidebar {
width: 200px;
background-color: #338F6A;
background-color: @bg-color;
padding: 20px;
/* box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); */
}
.content {
margin-left: 280px;
width: 1304px;
height: 868px;
// margin-left: 280px;
margin-left: 25px;
margin-right: 25px;
width: 1540px;
height: 848px;
background: #FFFFFF;
border-radius: 36px;
margin-top: 16px;
@@ -86,4 +109,11 @@ html {
justify-content: space-around;
align-items: center;
}
.footer{
position: absolute;
bottom: 10px;
left: calc(50% - 150px);
color: aqua;
font-size: 16px;
}
</style>

Binary file not shown.

View File

@@ -18,6 +18,9 @@ module.exports = defineConfig({
})
]
}
},
less: {
additionalData: `@import "@/static/css/app.less";` // 注入全局变量文件
}
}
}