Compare commits
24 Commits
05cad57358
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a1cb9afa93 | |||
| 7bd4a92da6 | |||
| b030675ee3 | |||
| d0d2f260df | |||
|
|
812fade238 | ||
|
|
84bab8b8c8 | ||
|
|
bf61508796 | ||
|
|
73d8b38f99 | ||
|
|
6959fd2d7a | ||
|
|
ea50c14175 | ||
|
|
8cabf98bd9 | ||
|
|
51ce853bde | ||
|
|
a46cbf79f6 | ||
|
|
52415dee0b | ||
|
|
01e9c26821 | ||
| 560581f1a4 | |||
| 17d2251a70 | |||
| dcd677bbab | |||
| 60f6fc4873 | |||
| 5e34aaf402 | |||
| c5fb3ea0b2 | |||
| 666e27246f | |||
| 91ee2dd7cb | |||
| 7df29e741c |
73
package-lock.json
generated
@@ -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"
|
||||
},
|
||||
@@ -2871,6 +2872,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",
|
||||
@@ -9709,9 +9754,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"
|
||||
@@ -13292,6 +13337,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",
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
29
src/App.vue
@@ -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> -->
|
||||
|
||||
@@ -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
|
After Width: | Height: | Size: 6.9 KiB |
BIN
src/assets/ai.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/icon.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 88 KiB |
BIN
src/assets/logo1.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/logo2.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 1.3 MiB |
BIN
src/assets/logoBg1.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
src/assets/logoBg2.png
Normal file
|
After Width: | Height: | Size: 363 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 106 KiB |
BIN
src/assets/logotext1.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/logotext12.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/assets/plus.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/plus2.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/wifi.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
355
src/i18n/lang/en-US.js
Normal file
@@ -0,0 +1,355 @@
|
||||
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',
|
||||
assistant: 'TK high-value fan assistant',
|
||||
},
|
||||
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"
|
||||
}
|
||||
}
|
||||
349
src/i18n/lang/zh-CN.js
Normal file
@@ -0,0 +1,349 @@
|
||||
export default {
|
||||
common: {
|
||||
networkSettings: '网络设置',
|
||||
language: '语言',
|
||||
simplifiedChinese: '简体中文',
|
||||
english: 'English',
|
||||
accountLogin: '账号登录',
|
||||
tenantName: '租户名称',
|
||||
account: '账号',
|
||||
password: '密码',
|
||||
login: '登录',
|
||||
version: '版本号',
|
||||
expirationtime: '过期时间',
|
||||
assistant: 'TK高价值粉丝助手',
|
||||
|
||||
},
|
||||
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: "津巴布韦"
|
||||
}
|
||||
|
||||
}
|
||||
17
src/main.js
@@ -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');
|
||||
@@ -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
@@ -0,0 +1,4 @@
|
||||
@bg-color: #022b4e; // 主色
|
||||
@bg-color-light: #022b4eaf; // 浅主色
|
||||
@bg-color-light-light: #022b4e1c; // 浅浅主色
|
||||
@btn-bg-color: #045dac; // 黄色按钮主色
|
||||
653
src/static/css/cyber-design.less
Normal file
@@ -0,0 +1,653 @@
|
||||
/**
|
||||
* Cyber Design System - 现代科技感设计系统
|
||||
* 基于 test.html 的设计规范
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 设计变量 Design Tokens
|
||||
// ============================================
|
||||
|
||||
// 主色调
|
||||
@primary-color: #0052CC;
|
||||
@cyber-blue: #00D2FF;
|
||||
@cyber-blue-dark: #0052CC;
|
||||
|
||||
// 灰度色阶
|
||||
@tech-gray-50: #F8FAFC;
|
||||
@tech-gray-100: #F1F5F9;
|
||||
@tech-gray-200: #E2E8F0;
|
||||
@tech-gray-300: #CBD5E1;
|
||||
@tech-gray-400: #94A3B8;
|
||||
@tech-gray-500: #64748B;
|
||||
@tech-gray-600: #475569;
|
||||
@tech-gray-700: #334155;
|
||||
@tech-gray-800: #1E293B;
|
||||
@tech-gray-900: #0F172A;
|
||||
|
||||
// 功能色
|
||||
@success-color: #10B981;
|
||||
@warning-color: #F59E0B;
|
||||
@error-color: #EF4444;
|
||||
|
||||
// 渐变
|
||||
@cyber-gradient: linear-gradient(135deg, @cyber-blue 0%, @cyber-blue-dark 100%);
|
||||
@page-gradient: radial-gradient(circle at top right, #f8fafc, #eff6ff);
|
||||
|
||||
// 圆角
|
||||
@radius-sm: 8px;
|
||||
@radius-md: 12px;
|
||||
@radius-lg: 16px;
|
||||
@radius-xl: 20px;
|
||||
@radius-2xl: 24px;
|
||||
@radius-3xl: 32px;
|
||||
|
||||
// 阴影
|
||||
@shadow-soft-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.03);
|
||||
@shadow-cyber-glow: 0 0 20px rgba(0, 210, 255, 0.3);
|
||||
@shadow-premium: 0 10px 25px -5px rgba(0, 0, 0, 0.04), 0 8px 10px -6px rgba(0, 0, 0, 0.04);
|
||||
@shadow-glass: 0 8px 32px 0 rgba(31, 38, 135, 0.07);
|
||||
@shadow-button: 0 4px 12px rgba(0, 82, 204, 0.25);
|
||||
|
||||
// ============================================
|
||||
// 全局基础样式
|
||||
// ============================================
|
||||
|
||||
// 页面背景
|
||||
.cyber-page-bg {
|
||||
background: @page-gradient;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// 内容容器
|
||||
.cyber-container {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 玻璃态组件 Glass Components
|
||||
// ============================================
|
||||
|
||||
// 玻璃态头部卡片
|
||||
.glass-header {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.9);
|
||||
border-radius: @radius-3xl;
|
||||
padding: 28px 32px;
|
||||
box-shadow: @shadow-glass;
|
||||
}
|
||||
|
||||
// 玻璃态内容卡片
|
||||
.glass-card {
|
||||
background: #ffffff;
|
||||
border-radius: @radius-3xl;
|
||||
box-shadow: @shadow-premium;
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 按钮系统 Button System
|
||||
// ============================================
|
||||
|
||||
// 主渐变按钮
|
||||
.cyber-button {
|
||||
background: @cyber-gradient;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: @radius-xl;
|
||||
padding: 12px 24px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: @shadow-button;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.1);
|
||||
box-shadow: @shadow-cyber-glow;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
filter: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 幽灵按钮
|
||||
.ghost-button {
|
||||
background: #ffffff;
|
||||
color: @tech-gray-600;
|
||||
border: 1px solid @tech-gray-200;
|
||||
border-radius: @radius-xl;
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
&:hover {
|
||||
background: @tech-gray-50;
|
||||
border-color: @tech-gray-300;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 输入框系统 Input System
|
||||
// ============================================
|
||||
|
||||
.input-tech {
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
border-radius: @radius-xl;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
color: @tech-gray-800;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: @shadow-soft-inner;
|
||||
outline: none;
|
||||
|
||||
&::placeholder {
|
||||
color: @tech-gray-400;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: rgba(0, 82, 204, 0.3);
|
||||
box-shadow: 0 0 0 3px rgba(0, 82, 204, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 标签系统 Label System
|
||||
// ============================================
|
||||
|
||||
.label-tech {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: @tech-gray-400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-left: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
// 徽章标签
|
||||
.badge-level {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: #D97706;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
background: rgba(0, 82, 204, 0.08);
|
||||
color: @primary-color;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 表格系统 Table System
|
||||
// ============================================
|
||||
|
||||
.table-tech {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
|
||||
thead tr {
|
||||
th {
|
||||
padding: 16px 16px 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: @tech-gray-400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid @tech-gray-100;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
transition: background 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
background: @tech-gray-50;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 18px 16px;
|
||||
font-size: 14px;
|
||||
color: @tech-gray-600;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 分页系统 Pagination System
|
||||
// ============================================
|
||||
|
||||
.pagination-tech {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.page-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: @radius-xl;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: @tech-gray-600;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: @tech-gray-900;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 信息区块 Info Block
|
||||
// ============================================
|
||||
|
||||
.info-pill {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
padding: 8px 16px;
|
||||
border-radius: 9999px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
.info-label {
|
||||
color: @tech-gray-400;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: @tech-gray-700;
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
|
||||
.info-divider {
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
background: @tech-gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 页脚区块 Footer Section
|
||||
// ============================================
|
||||
|
||||
.footer-section {
|
||||
padding: 20px 32px;
|
||||
background: rgba(248, 250, 252, 0.5);
|
||||
border-top: 1px solid @tech-gray-100;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.stat-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: @tech-gray-400;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: @tech-gray-900;
|
||||
|
||||
&.primary {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 滚动条美化 Scrollbar
|
||||
// ============================================
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: @tech-gray-300;
|
||||
border-radius: 10px;
|
||||
|
||||
&:hover {
|
||||
background: @tech-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Element Plus 组件覆盖 - 输入框
|
||||
// ============================================
|
||||
|
||||
.cyber-form {
|
||||
// 输入框
|
||||
.el-input__wrapper {
|
||||
background: #ffffff !important;
|
||||
border: 1px solid rgba(226, 232, 240, 0.6) !important;
|
||||
border-radius: @radius-xl !important;
|
||||
box-shadow: @shadow-soft-inner !important;
|
||||
padding: 4px 16px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
|
||||
&:hover {
|
||||
border-color: @tech-gray-300 !important;
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
border-color: rgba(0, 82, 204, 0.3) !important;
|
||||
box-shadow: 0 0 0 3px rgba(0, 82, 204, 0.08) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--large .el-input__wrapper {
|
||||
height: 44px !important;
|
||||
}
|
||||
|
||||
// 选择器
|
||||
.el-select__wrapper {
|
||||
background: #ffffff !important;
|
||||
border: 1px solid rgba(226, 232, 240, 0.6) !important;
|
||||
border-radius: @radius-xl !important;
|
||||
box-shadow: @shadow-soft-inner !important;
|
||||
padding: 4px 16px !important;
|
||||
min-height: 44px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
|
||||
&:hover {
|
||||
border-color: @tech-gray-300 !important;
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
border-color: rgba(0, 82, 204, 0.3) !important;
|
||||
box-shadow: 0 0 0 3px rgba(0, 82, 204, 0.08) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 复选框
|
||||
.el-checkbox {
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
border-radius: @radius-xl;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: @tech-gray-300;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: @primary-color;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
.el-checkbox__label {
|
||||
font-weight: 600;
|
||||
color: @tech-gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮 - 主要
|
||||
.el-button--primary {
|
||||
background: @cyber-gradient !important;
|
||||
border: none !important;
|
||||
border-radius: @radius-xl !important;
|
||||
font-weight: 600 !important;
|
||||
box-shadow: @shadow-button !important;
|
||||
transition: all 0.3s ease !important;
|
||||
height: 44px !important;
|
||||
padding: 0 24px !important;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.1);
|
||||
box-shadow: @shadow-cyber-glow !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&.is-disabled {
|
||||
opacity: 0.6 !important;
|
||||
filter: none !important;
|
||||
transform: none !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮 - 默认
|
||||
.el-button--default {
|
||||
background: #ffffff !important;
|
||||
border: 1px solid @tech-gray-200 !important;
|
||||
border-radius: @radius-xl !important;
|
||||
color: @tech-gray-600 !important;
|
||||
font-weight: 600 !important;
|
||||
transition: all 0.2s ease !important;
|
||||
height: 44px !important;
|
||||
padding: 0 20px !important;
|
||||
|
||||
&:hover {
|
||||
background: @tech-gray-50 !important;
|
||||
border-color: @tech-gray-300 !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Element Plus 组件覆盖 - 表格
|
||||
// ============================================
|
||||
|
||||
.cyber-table {
|
||||
.el-table {
|
||||
background: transparent !important;
|
||||
--el-table-border-color: transparent;
|
||||
--el-table-header-bg-color: transparent;
|
||||
--el-table-row-hover-bg-color: rgba(248, 250, 252, 0.8);
|
||||
|
||||
th.el-table__cell {
|
||||
background: transparent !important;
|
||||
font-size: 11px !important;
|
||||
font-weight: 700 !important;
|
||||
color: @tech-gray-400 !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.05em !important;
|
||||
padding: 16px 12px 20px !important;
|
||||
border-bottom: 1px solid @tech-gray-100 !important;
|
||||
}
|
||||
|
||||
td.el-table__cell {
|
||||
padding: 16px 12px !important;
|
||||
font-size: 14px !important;
|
||||
color: @tech-gray-600 !important;
|
||||
border-bottom: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
.el-table__row:hover > td.el-table__cell {
|
||||
background: @tech-gray-50 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Element Plus 组件覆盖 - 分页
|
||||
// ============================================
|
||||
|
||||
.cyber-pagination {
|
||||
.el-pagination {
|
||||
--el-pagination-bg-color: transparent;
|
||||
--el-pagination-hover-color: @primary-color;
|
||||
|
||||
.el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: @radius-xl;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
margin: 0 2px;
|
||||
background: transparent;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: @tech-gray-900 !important;
|
||||
color: #ffffff !important;
|
||||
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-prev,
|
||||
.btn-next {
|
||||
background: transparent;
|
||||
border-radius: @radius-xl;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: #ffffff;
|
||||
color: @primary-color;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
}
|
||||
|
||||
.el-select__wrapper {
|
||||
background: #ffffff !important;
|
||||
border: 1px solid @tech-gray-200 !important;
|
||||
border-radius: @radius-xl !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 布局辅助类 Layout Utilities
|
||||
// ============================================
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-start {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.gap-2 { gap: 8px; }
|
||||
.gap-3 { gap: 12px; }
|
||||
.gap-4 { gap: 16px; }
|
||||
.gap-6 { gap: 24px; }
|
||||
.gap-8 { gap: 32px; }
|
||||
|
||||
.mt-4 { margin-top: 16px; }
|
||||
.mt-6 { margin-top: 24px; }
|
||||
.mt-8 { margin-top: 32px; }
|
||||
|
||||
.mb-4 { margin-bottom: 16px; }
|
||||
.mb-6 { margin-bottom: 24px; }
|
||||
.mb-8 { margin-bottom: 32px; }
|
||||
|
||||
.p-4 { padding: 16px; }
|
||||
.p-6 { padding: 24px; }
|
||||
.p-8 { padding: 32px; }
|
||||
|
||||
// 分割线
|
||||
.divider-line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, transparent, @tech-gray-200, transparent);
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.divider-vertical {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: @tech-gray-200;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,295 +1,527 @@
|
||||
<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="auth-shell">
|
||||
<!-- 顶部操作 -->
|
||||
<div class="top-actions">
|
||||
<div class="setup-item center-justify">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<span class="setup-text">{{ $t('common.networkSettings') }}</span>
|
||||
</span>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
<!-- From -->
|
||||
<div class="from">
|
||||
<div class="from-title center-justify">
|
||||
<div>账号登陆</div>
|
||||
</div>
|
||||
<!-- 语言选择:用 el-select,但外观套进 setup-item -->
|
||||
<div class="setup-item setup-item--dropdown">
|
||||
<el-dropdown trigger="click" @command="changeLanguage">
|
||||
<span class="lang-dropdown">
|
||||
<span class="lang-dropdown__label">{{ currentLanguageLabel }}</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="zh-CN">
|
||||
{{ $t('common.simplifiedChinese') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="en-US">
|
||||
{{ $t('common.english') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="from-input">
|
||||
<el-form label-position="left" label-width="100px" :model="formData">
|
||||
<!-- 主卡片 -->
|
||||
<div class="auth-card">
|
||||
<!-- 左侧登录 -->
|
||||
<div class="auth-left">
|
||||
<div class="logo center-justify">
|
||||
<!-- 你原本 logo 是空的,这里给你留一个位 -->
|
||||
<!-- 想放logo就取消注释 -->
|
||||
<img class="logo-image" src="@/assets/logo2.png" alt="" />
|
||||
<span class="logo-title">{{ $t('common.assistant') }}</span>
|
||||
</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="welcome">{{ $t('common.accountLogin') }}</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">
|
||||
<div class="from-input">
|
||||
<el-form label-position="left" label-width="100px" :model="formData">
|
||||
<div class="field-label">{{ $t('common.tenantName') }}</div>
|
||||
<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="field-label">{{ $t('common.account') }}</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="field-label">{{ $t('common.password') }}</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-item">
|
||||
<el-button class="loginButton" type="primary" :loading="isSubmitting" @click="onSubmit">
|
||||
{{ $t('common.login') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version">{{ $t('common.version') }}:{{ version }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧插画 -->
|
||||
<div class="auth-right">
|
||||
<img src="../assets/logoBg2.png" class="illustration" alt="" />
|
||||
</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, onMounted, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { login, rentgetloginID } from "@/api/account";
|
||||
import { setToken, setUser, setUserPass, getUserPass } from "@/utils/storage";
|
||||
import { usePythonBridge } from "@/utils/pythonBridge";
|
||||
import { tokenStore, UserStore } from "@/stores/notice";
|
||||
|
||||
const router = useRouter();
|
||||
const { locale, t } = useI18n();
|
||||
|
||||
const tokenCache = tokenStore();
|
||||
const userCache = UserStore();
|
||||
|
||||
const { getVersion } = usePythonBridge();
|
||||
let version = ref("0.0.0");
|
||||
|
||||
// 当前语言
|
||||
const currentLanguage = ref(localStorage.getItem("language") || "zh-CN");
|
||||
const currentLanguageLabel = computed(() =>
|
||||
currentLanguage.value === "zh-CN"
|
||||
? t("common.simplifiedChinese")
|
||||
: t("common.english")
|
||||
);
|
||||
|
||||
const formData = ref({
|
||||
userId: getUserPass() == null ? '' : getUserPass().userId,
|
||||
password: getUserPass() == null ? '' : getUserPass().password,
|
||||
tenantName: "",
|
||||
userId: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
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);
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
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('账号未启用');
|
||||
}
|
||||
if (isSubmitting.value) return;
|
||||
isSubmitting.value = true;
|
||||
|
||||
} else {
|
||||
alert('账号或密码错误');
|
||||
}
|
||||
|
||||
}).catch((err) => {
|
||||
loading.close();
|
||||
});
|
||||
rentgetloginID({ name: formData.value.tenantName })
|
||||
.then((tenantId) => {
|
||||
login({
|
||||
username: formData.value.userId,
|
||||
tenantId: tenantId,
|
||||
password: formData.value.password,
|
||||
})
|
||||
.then((res) => {
|
||||
isSubmitting.value = false;
|
||||
setToken(res.tokenValue);
|
||||
tokenCache.setToken(res.tokenValue);
|
||||
userCache.setUser(res);
|
||||
setUser(res);
|
||||
setUserPass(formData.value);
|
||||
router.push("/nav");
|
||||
})
|
||||
.catch(() => {
|
||||
isSubmitting.value = false;
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
isSubmitting.value = false;
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
/* ✅ 全屏自适应 */
|
||||
.main {
|
||||
width: 1600px;
|
||||
height: 900px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(180deg, #f3f5f9 0%, #eef1f5 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Source Han Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
|
||||
/* 页面无法选中 */
|
||||
-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;
|
||||
|
||||
.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;
|
||||
/* 确保视频在内容之下 */
|
||||
}
|
||||
|
||||
.setup {
|
||||
display: flex;
|
||||
color: #fff;
|
||||
|
||||
.setup-item {
|
||||
padding: 10px 6px;
|
||||
display: flex;
|
||||
|
||||
div {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(255, 255, 255);
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.from {
|
||||
width: 420px;
|
||||
height: 320px;
|
||||
color: #107A4E;
|
||||
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: #107A4E;
|
||||
line-height: 37px;
|
||||
|
||||
|
||||
div {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
// border-bottom: 4px solid #1db97d;
|
||||
}
|
||||
}
|
||||
|
||||
.from-input {
|
||||
width: 100%;
|
||||
padding: 15px 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;
|
||||
}
|
||||
|
||||
.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: #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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.center-line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.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;
|
||||
/* 外壳:自适应 */
|
||||
.auth-shell {
|
||||
width: min(1320px, 92vw);
|
||||
height: min(760px, 86vh);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.center-flex {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* 背景柔光 */
|
||||
.auth-shell::before,
|
||||
.auth-shell::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
.auth-shell::before {
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
background: radial-gradient(circle, rgba(214, 226, 255, 0.7), rgba(214, 226, 255, 0));
|
||||
left: -80px;
|
||||
top: -40px;
|
||||
}
|
||||
|
||||
.auth-shell::after {
|
||||
width: 360px;
|
||||
height: 360px;
|
||||
background: radial-gradient(circle, rgba(255, 236, 214, 0.6), rgba(255, 236, 214, 0));
|
||||
right: -40px;
|
||||
bottom: -60px;
|
||||
}
|
||||
|
||||
/* 顶部操作 */
|
||||
.top-actions {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 80px;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.setup-item {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid #e1e6f2;
|
||||
border-radius: 16px;
|
||||
padding: 8px 12px 8px 14px;
|
||||
box-shadow: 0 8px 18px rgba(40, 57, 108, 0.12);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.setup-item::after {
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-right: 2px solid #9aa1b3;
|
||||
border-bottom: 2px solid #9aa1b3;
|
||||
transform: rotate(45deg);
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.setup-item:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: #c9d2f4;
|
||||
box-shadow: 0 12px 24px rgba(40, 57, 108, 0.16);
|
||||
}
|
||||
|
||||
.setup-text {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2b3347;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
/* el-select 这块不要小箭头重复:setup-item 自带 after 箭头,所以把 after 去掉 */
|
||||
.setup-item--select::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* 主卡片 */
|
||||
.auth-card {
|
||||
width: min(1200px, 92vw);
|
||||
height: min(680px, 80vh);
|
||||
display: flex;
|
||||
background: #ffffff;
|
||||
border-radius: 28px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 24px 60px rgba(40, 57, 108, 0.16);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 左侧 */
|
||||
.auth-left {
|
||||
width: 42%;
|
||||
padding: clamp(20px, 3vw, 56px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
height: 200px;
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: #2b3347;
|
||||
letter-spacing: 2px;
|
||||
position: relative;
|
||||
padding-left: 14px;
|
||||
margin-bottom: -30px;
|
||||
}
|
||||
|
||||
.logo-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
width: 4px;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(180deg, #4f6ef7, #5b7bff);
|
||||
}
|
||||
|
||||
.welcome {
|
||||
margin-top: 14px;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #2b3347;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.from {
|
||||
width: 100%;
|
||||
color: #2b3347;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.from-input {
|
||||
width: 100%;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
margin: 12px 0 6px;
|
||||
}
|
||||
|
||||
.from-input-item {
|
||||
display: flex;
|
||||
padding: 16px 0 0 0;
|
||||
}
|
||||
|
||||
/* 输入框胶囊 */
|
||||
.from-input-item1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: #f6f8fc;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e6eaf2;
|
||||
padding: 10px 14px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 4px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.from-input-item1 img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 按钮同款 */
|
||||
.loginButton {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #4f6ef7, #5b7bff);
|
||||
border-radius: 14px;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 12px 24px rgba(79, 110, 247, 0.28);
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-top: 24px;
|
||||
font-size: 12px;
|
||||
color: #9aa1b3;
|
||||
}
|
||||
|
||||
/* 右侧插画 */
|
||||
.auth-right {
|
||||
width: 58%;
|
||||
background: linear-gradient(160deg, #f8f9fc 0%, #f0f3f9 100%);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.illustration {
|
||||
width: 90%;
|
||||
height: auto;
|
||||
max-height: 88%;
|
||||
object-fit: contain;
|
||||
filter: drop-shadow(0 30px 60px rgba(40, 57, 108, 0.25));
|
||||
}
|
||||
|
||||
/* 小屏:上下 */
|
||||
@media (max-width: 900px) {
|
||||
.auth-shell {
|
||||
width: 94vw;
|
||||
height: auto;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.auth-left,
|
||||
.auth-right {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.auth-right {
|
||||
min-height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
/* element-plus 变量 */
|
||||
.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: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 输入文字/placeholder 同款深色 */
|
||||
::v-deep(.el-input__inner) {
|
||||
color: #107A4E;
|
||||
|
||||
color: #2b3347;
|
||||
}
|
||||
|
||||
::v-deep(.el-input__inner::placeholder) {
|
||||
color: #107A4E;
|
||||
color: #9aa1b3;
|
||||
}
|
||||
</style>
|
||||
|
||||
/* 语言下拉:去边框、透明底,融入 setup-item */
|
||||
::v-deep(.lang-select .el-select__wrapper) {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
padding: 0 8px;
|
||||
min-height: 24px;
|
||||
/* 或者 height: 24px */
|
||||
}
|
||||
|
||||
::v-deep(.lang-select .el-select__selected-item) {
|
||||
color: #2b3347;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
::v-deep(.lang-select .el-select__caret) {
|
||||
color: #9aa1b3;
|
||||
}
|
||||
|
||||
.lang-dropdown {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
min-height: 24px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.lang-dropdown__label {
|
||||
color: #2b3347;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
|
||||
77
src/views/hosts/Home.vue
Normal 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>
|
||||
288
src/views/hosts/ai/LanguageManagement.vue
Normal 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>
|
||||
416
src/views/hosts/ai/scriptManagement.vue
Normal 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 + ". " + 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>
|
||||
47
src/views/hosts/mini/miniAM.vue
Normal 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>
|
||||
315
src/views/hosts/mini/miniIntegral.vue
Normal 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>
|
||||
313
src/views/hosts/test.html
Normal file
@@ -0,0 +1,313 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<title>Yolo Assistant - Data Console</title>
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect" />
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
|
||||
rel="stylesheet" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
||||
rel="stylesheet" />
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#0052CC",
|
||||
"cyber-blue": "#00D2FF",
|
||||
"cyber-blue-dark": "#0052CC",
|
||||
"tech-gray-50": "#F8FAFC",
|
||||
"tech-gray-100": "#F1F5F9",
|
||||
"tech-gray-200": "#E2E8F0",
|
||||
},
|
||||
fontFamily: {
|
||||
display: ["Inter", "system-ui", "sans-serif"],
|
||||
},
|
||||
borderRadius: {
|
||||
DEFAULT: "12px",
|
||||
'xl': '20px',
|
||||
'2xl': '24px',
|
||||
'3xl': '32px',
|
||||
},
|
||||
boxShadow: {
|
||||
'soft-inner': 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.03)',
|
||||
'cyber-glow': '0 0 20px rgba(0, 210, 255, 0.3)',
|
||||
'premium': '0 10px 25px -5px rgba(0, 0, 0, 0.04), 0 8px 10px -6px rgba(0, 0, 0, 0.04)',
|
||||
'glass': '0 8px 32px 0 rgba(31, 38, 135, 0.07)',
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
:root {
|
||||
--primary-color: #0052CC;
|
||||
--cyber-gradient: linear-gradient(135deg, #00D2FF 0%, #0052CC 100%);
|
||||
}
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: radial-gradient(circle at top right, #f8fafc, #eff6ff);
|
||||
}
|
||||
.cyber-button {
|
||||
background: var(--cyber-gradient);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.cyber-button:hover {
|
||||
filter: brightness(1.1);
|
||||
box-shadow: 0 0 15px rgba(0, 210, 255, 0.4);
|
||||
}
|
||||
.ghost-button {
|
||||
border: 1px solid #E2E8F0;
|
||||
background: white;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.ghost-button:hover {
|
||||
background: #F8FAFC;
|
||||
border-color: #CBD5E1;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
::-webkit-scrollbar { width: 6px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: #CBD5E1; border-radius: 10px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: #94A3B8; }
|
||||
.input-tech {
|
||||
@apply bg-white border border-tech-gray-200/60 rounded-xl px-4 py-2.5 text-sm transition-all focus:ring-2 focus:ring-primary/10 focus:border-primary/30 outline-none shadow-soft-inner;
|
||||
}
|
||||
.glass-header {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="font-display text-slate-900 antialiased min-h-screen">
|
||||
<div class="max-w-[1440px] mx-auto px-6 py-8">
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center text-white font-bold text-xl shadow-lg shadow-primary/20">
|
||||
y</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xl font-bold tracking-tight text-slate-900 leading-none">yolo</span>
|
||||
<span class="text-[10px] font-semibold tracking-widest text-primary uppercase">Assistant</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-6 text-xs font-medium bg-white/50 px-4 py-2 rounded-full border border-white/50 shadow-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-400">当前网络:</span>
|
||||
<span class="text-primary bg-blue-50 px-2 py-0.5 rounded-full">日本 (Japan)</span>
|
||||
</div>
|
||||
<div class="h-3 w-px bg-slate-200"></div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-400">运行时间:</span>
|
||||
<span class="text-slate-700 font-mono">00:00:00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main class="space-y-6">
|
||||
<div class="glass-header rounded-3xl p-8 shadow-glass">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-end">
|
||||
<div class="lg:col-span-8 grid grid-cols-1 md:grid-cols-12 gap-6">
|
||||
<div class="md:col-span-3 flex items-center h-full">
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<div class="relative">
|
||||
<input class="peer hidden" type="checkbox" />
|
||||
<div
|
||||
class="w-5 h-5 border-2 border-slate-200 rounded-lg peer-checked:bg-primary peer-checked:border-primary transition-all">
|
||||
</div>
|
||||
<span
|
||||
class="material-symbols-outlined absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-sm opacity-0 peer-checked:opacity-100">check</span>
|
||||
</div>
|
||||
<span
|
||||
class="text-sm font-semibold text-slate-600 group-hover:text-primary transition-colors">过滤隐私用户</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="md:col-span-4 space-y-2">
|
||||
<label
|
||||
class="text-[11px] font-bold text-slate-400 uppercase tracking-wider flex items-center gap-2 px-1">
|
||||
<span class="material-symbols-outlined text-xs">payments</span> 金币范围
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="input-tech w-full" placeholder="最小金币" type="text" />
|
||||
<span class="text-slate-300">/</span>
|
||||
<input class="input-tech w-full" placeholder="最大金币" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:col-span-5 space-y-2">
|
||||
<label
|
||||
class="text-[11px] font-bold text-slate-400 uppercase tracking-wider flex items-center gap-2 px-1">
|
||||
<span class="material-symbols-outlined text-xs">trending_up</span> 等级范围
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="input-tech w-full" placeholder="最小等级" type="text" />
|
||||
<span class="text-slate-300">/</span>
|
||||
<input class="input-tech w-full" placeholder="最大等级" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-4 flex gap-3">
|
||||
<button
|
||||
class="cyber-button flex-[2] text-white px-4 py-3 rounded-2xl font-bold text-sm flex items-center justify-center gap-2 shadow-lg shadow-blue-500/20">
|
||||
<span class="material-symbols-outlined text-lg">person_pin_circle</span>
|
||||
选择直播间
|
||||
</button>
|
||||
<button
|
||||
class="cyber-button flex-1 py-3 rounded-2xl text-white text-sm font-bold shadow-cyber-glow">
|
||||
开始执行
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-4 mt-8 pt-8 border-t border-slate-200/50">
|
||||
<div class="flex-1 min-w-[300px] flex items-center gap-4">
|
||||
<div class="relative w-48">
|
||||
<select class="input-tech w-full appearance-none pr-10 bg-slate-50/50">
|
||||
<option>全部国家</option>
|
||||
<option>美国</option>
|
||||
<option>日本</option>
|
||||
</select>
|
||||
<span
|
||||
class="material-symbols-outlined absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">expand_more</span>
|
||||
</div>
|
||||
<div class="relative flex-1">
|
||||
<span
|
||||
class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-lg">alternate_email</span>
|
||||
<input class="input-tech w-full pl-10" placeholder="请输入大哥 ID..." type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-xl">search</span>查询
|
||||
</button>
|
||||
<button
|
||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-xl">restart_alt</span>重置
|
||||
</button>
|
||||
<button
|
||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-xl">file_download</span>导出Excel
|
||||
</button>
|
||||
<button
|
||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-xl">tune</span>更多筛选
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white rounded-3xl shadow-premium border border-white flex flex-col min-h-[500px] overflow-hidden">
|
||||
<div class="flex-1 p-8">
|
||||
<div class="w-full overflow-x-auto">
|
||||
<table class="w-full text-left border-separate border-spacing-y-0">
|
||||
<thead>
|
||||
<tr class="text-slate-400 text-[11px] font-bold uppercase tracking-widest">
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">ID</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">所在直播间</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">用户ID</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">等级</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">打赏金币</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">地区</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">粉丝数</th>
|
||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold text-right">创建时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="group hover:bg-slate-50 transition-colors">
|
||||
<td class="py-5 px-4 text-sm text-slate-500">1</td>
|
||||
<td
|
||||
class="py-5 px-4 text-sm font-semibold text-primary underline decoration-primary/20 underline-offset-4 cursor-pointer">
|
||||
toxi_robert</td>
|
||||
<td class="py-5 px-4 text-sm font-mono text-slate-600">706350628...</td>
|
||||
<td class="py-5 px-4">
|
||||
<span
|
||||
class="px-2 py-1 bg-amber-50 text-amber-600 text-[10px] font-bold rounded-lg border border-amber-100">LV
|
||||
29</span>
|
||||
</td>
|
||||
<td class="py-5 px-4 text-sm font-bold text-slate-700">37,420</td>
|
||||
<td class="py-5 px-4 text-sm text-slate-600">意大利</td>
|
||||
<td class="py-5 px-4 text-sm text-slate-500">562</td>
|
||||
<td class="py-5 px-4 text-sm text-slate-400 font-mono text-right">2026-01-16
|
||||
15:15:07</td>
|
||||
</tr>
|
||||
<tr class="hidden">
|
||||
<td class="py-32" colspan="8">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div
|
||||
class="w-16 h-16 bg-slate-50 rounded-full flex items-center justify-center mb-4">
|
||||
<span
|
||||
class="material-symbols-outlined text-3xl text-slate-300">database_off</span>
|
||||
</div>
|
||||
<p class="text-slate-400 text-sm">暂无匹配数据</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<footer
|
||||
class="px-8 py-6 bg-slate-50/50 border-t border-slate-100 flex flex-wrap items-center justify-between gap-6">
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="relative">
|
||||
<select
|
||||
class="bg-white border border-slate-200 rounded-xl py-2 pl-4 pr-10 text-xs font-bold text-slate-600 appearance-none focus:ring-2 focus:ring-primary/10 cursor-pointer">
|
||||
<option>10条/页</option>
|
||||
<option>20条/页</option>
|
||||
<option>50条/页</option>
|
||||
</select>
|
||||
<span
|
||||
class="material-symbols-outlined absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 text-lg pointer-events-none">expand_more</span>
|
||||
</div>
|
||||
<div class="h-6 w-[1px] bg-slate-200"></div>
|
||||
<div class="flex items-center gap-8 text-xs">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-slate-400 font-medium">总数据</span>
|
||||
<span class="font-bold text-slate-900 text-sm">1,248</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-slate-400 font-medium">有效数</span>
|
||||
<span class="font-bold text-primary text-sm">842</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-slate-400 uppercase tracking-tighter font-medium">服务到期</span>
|
||||
<span class="text-slate-900 font-bold text-sm">2036-08-01</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="p-2 text-slate-400 hover:bg-white hover:text-primary hover:shadow-sm rounded-xl transition-all">
|
||||
<span class="material-symbols-outlined text-xl">chevron_left</span>
|
||||
</button>
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
class="w-9 h-9 rounded-xl bg-slate-900 text-white text-xs font-bold transition-all shadow-lg shadow-slate-900/20">1</button>
|
||||
<button
|
||||
class="w-9 h-9 rounded-xl hover:bg-white text-slate-600 text-xs font-bold transition-all">2</button>
|
||||
<button
|
||||
class="w-9 h-9 rounded-xl hover:bg-white text-slate-600 text-xs font-bold transition-all">3</button>
|
||||
<span class="px-2 text-slate-300">...</span>
|
||||
<button
|
||||
class="w-9 h-9 rounded-xl hover:bg-white text-slate-600 text-xs font-bold transition-all">12</button>
|
||||
</div>
|
||||
<button
|
||||
class="p-2 text-slate-400 hover:bg-white hover:text-primary hover:shadow-sm rounded-xl transition-all">
|
||||
<span class="material-symbols-outlined text-xl">chevron_right</span>
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -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;
|
||||
|
||||
@@ -1,84 +1,171 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<Sidebar class="noneText" @activeIndex="activeIndexFn" />
|
||||
<div class="content ">
|
||||
<div v-show="activeIndex == 1">
|
||||
<workbenches v-if="openWerk" />
|
||||
<div class="app-container cyber-page">
|
||||
<div class="cyber-main-container">
|
||||
<!-- 主内容区域 -->
|
||||
<div class="content-wrapper glass-content">
|
||||
<hostsList />
|
||||
</div>
|
||||
<div v-show="activeIndex == 2">
|
||||
<hostsList v-if="openList" />
|
||||
|
||||
<!-- 底部状态栏 -->
|
||||
<div class="footer-bar">
|
||||
<div class="footer-content">
|
||||
<span class="footer-icon">⏱️</span>
|
||||
<span class="footer-label">{{ $t('common.expirationtime') }}</span>
|
||||
<span class="footer-value">{{ time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Sidebar from '../components/Sidebar.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref, reactive, onMounted } from "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'
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getUser } from "@/utils/storage";
|
||||
import { tokenStore, UserStore } from '@/stores/notice'
|
||||
|
||||
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)
|
||||
const userCache = UserStore()
|
||||
const { t } = useI18n();
|
||||
const time = ref(formatTimestamp(userCache.user.brotherExpireTime))
|
||||
|
||||
|
||||
|
||||
function activeIndexFn(data) {
|
||||
activeIndex.value = data
|
||||
openWerk.value = true
|
||||
openList.value = true
|
||||
console.log(data)
|
||||
// 时间格式化方法 - 将12位时间戳转为YYYY-MM-DD HH:mm:ss格式
|
||||
function formatTimestamp(timestamp) {
|
||||
try {
|
||||
const ts = Number(timestamp);
|
||||
if (isNaN(ts)) {
|
||||
return '--';
|
||||
}
|
||||
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">
|
||||
@import "@/static/css/cyber-design.less";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
display: flex;
|
||||
width: 1600px;
|
||||
height: 900px;
|
||||
background-color: #338F6A;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.noneText {
|
||||
/* 页面无法选中 */
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 200px;
|
||||
background-color: #338F6A;
|
||||
padding: 20px;
|
||||
/* box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); */
|
||||
.app-container.cyber-page {
|
||||
width: 1600px;
|
||||
height: 900px;
|
||||
background: radial-gradient(ellipse at top right, #f0f9ff 0%, #e0f2fe 30%, #f1f5f9 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
// 背景装饰
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
background: radial-gradient(circle, rgba(0, 210, 255, 0.08) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
left: -10%;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: radial-gradient(circle, rgba(0, 82, 204, 0.06) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: 280px;
|
||||
width: 1304px;
|
||||
height: 868px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 36px;
|
||||
margin-top: 16px;
|
||||
.cyber-main-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
}
|
||||
|
||||
.content-wrapper.glass-content {
|
||||
flex: 1;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: 28px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.95);
|
||||
box-shadow:
|
||||
0 20px 60px -10px rgba(0, 0, 0, 0.08),
|
||||
0 10px 30px -5px rgba(0, 0, 0, 0.04),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 24px;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 24px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
|
||||
|
||||
.footer-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.footer-label {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.footer-value {
|
||||
font-size: 13px;
|
||||
color: #0052CC;
|
||||
font-weight: 600;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
}
|
||||
}
|
||||
|
||||
.center-justify {
|
||||
|
||||
BIN
tk-page.rar
@@ -18,6 +18,9 @@ module.exports = defineConfig({
|
||||
})
|
||||
]
|
||||
}
|
||||
},
|
||||
less: {
|
||||
additionalData: `@import "@/static/css/app.less";` // 注入全局变量文件
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||