初始化仓库
5
.env.development
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# .env.development
|
||||||
|
|
||||||
|
VUE_APP_API_URL="123123123"
|
||||||
|
|
||||||
|
# VUE_APP_FEATURE_FLAG=true
|
||||||
5
.env.production
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# .env.production
|
||||||
|
|
||||||
|
VUE_APP_API_URL="434534534"
|
||||||
|
|
||||||
|
# VUE_APP_FEATURE_FLAG=true
|
||||||
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
5
babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
||||||
19
jsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
14092
package-lock.json
generated
Normal file
39
package.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "tk-page",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.8.4",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"echarts": "^5.6.0",
|
||||||
|
"element-plus": "^2.9.7",
|
||||||
|
"pinia": "^3.0.1",
|
||||||
|
"qwebchannel": "^6.2.0",
|
||||||
|
"vue": "^3.2.13",
|
||||||
|
"vue-i18n": "^11.1.7",
|
||||||
|
"vue-router": "^4.0.3",
|
||||||
|
"vuex": "^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-router": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||||
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"less": "^4.2.2",
|
||||||
|
"less-loader": "^12.2.0",
|
||||||
|
"postcss-preset-env": "^10.1.5",
|
||||||
|
"postcss-px-to-viewport": "^1.1.1",
|
||||||
|
"postcss-px-viewport": "^0.0.4",
|
||||||
|
"postcss-viewport-units": "^0.1.6"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead",
|
||||||
|
"not ie 11"
|
||||||
|
]
|
||||||
|
}
|
||||||
13
postcss.config.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
'postcss-px-to-viewport': {
|
||||||
|
viewportWidth: 1600, // 视窗的宽度,对应设计稿宽度
|
||||||
|
viewportHeight: 900, // 视窗的高度,对应设计稿高度
|
||||||
|
unitPrecision: 3, // 指定 px 转换为视窗单位值的小数位数
|
||||||
|
viewportUnit: 'vw', // 指定需要转换成的视窗单位,vw 或者 vh
|
||||||
|
selectorBlackList: ['.ignore', '.hairlines'], // 指定不需要转换的类
|
||||||
|
minPixelValue: 1, // 小于或等于 1 px 不转换为视窗单位
|
||||||
|
mediaQuery: false // 允许在媒体查询中转换 px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
32
public/index.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title>
|
||||||
|
<%= webpackConfig.name %>
|
||||||
|
</title>
|
||||||
|
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it
|
||||||
|
to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
/* width: 1600px;
|
||||||
|
height: 900px; */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</html>
|
||||||
35
src/App.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
const { locale } = useI18n()
|
||||||
|
locale.value = localStorage.getItem('lang') || 'zh'
|
||||||
|
const debounce = (fn, delay) => {
|
||||||
|
let timer
|
||||||
|
return (...args) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
fn(...args)
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const _ResizeObserver = window.ResizeObserver
|
||||||
|
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
|
||||||
|
constructor(callback) {
|
||||||
|
callback = debounce(callback, 200)
|
||||||
|
super(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- <style lang="less">
|
||||||
|
/*每个页面公共css */
|
||||||
|
|
||||||
|
@import "@/static/css/app.less";
|
||||||
|
</style> -->
|
||||||
72
src/api/account.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { getAxios, postAxios, downFile } from '@/utils/axios.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function getIdByName(name) {
|
||||||
|
return getAxios({ url: `/api/tenant/get-id-by-name?name=${name}` })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function login(data) {
|
||||||
|
return postAxios({ url: '/api/user/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 tkhostdata(data) {
|
||||||
|
return postAxios({ url: '/api/save_data/hosts_info', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function apiGetCart() {
|
||||||
|
return getAxios({ url: '/cgi-bin/cart/latest' })
|
||||||
|
}
|
||||||
|
// export function login(data) {
|
||||||
|
// return postAxios({ url: 'api/account/login', data })
|
||||||
|
// }
|
||||||
|
export function cheekalive(data) {
|
||||||
|
return postAxios({ url: 'api/account/cheekalive', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dicts(data) {
|
||||||
|
return postAxios({ url: 'api/param/dicts', data })
|
||||||
|
}
|
||||||
|
export function tkhostdetail(data) {
|
||||||
|
return postAxios({ url: 'api/tkinfo/tkhostdetail', data })
|
||||||
|
}
|
||||||
|
//导出表格
|
||||||
|
export function exporthosts(data) {
|
||||||
|
return postAxios({ url: 'api/export/hostsinfo', data })
|
||||||
|
}
|
||||||
|
export function downList(url, data) {
|
||||||
|
return downFile(url, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询员工
|
||||||
|
export function getStaffList(data) {
|
||||||
|
return postAxios({ url: 'api/account/list', data })
|
||||||
|
}
|
||||||
|
//分配主播
|
||||||
|
export function managerhosts(data) {
|
||||||
|
return postAxios({ url: 'api/account/managerhosts', data })
|
||||||
|
}
|
||||||
|
//编辑主播
|
||||||
|
export function upholdinfo(data) {
|
||||||
|
return postAxios({ url: 'api/tkinfo/upholdinfo', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
//查看名字
|
||||||
|
export function accountName(str) {
|
||||||
|
return postAxios({ url: 'api/account/accountName?accounts=' + str })
|
||||||
|
}
|
||||||
|
|
||||||
BIN
src/assets/filter.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/list.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/assets/listAction.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
src/assets/logo1.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/logoBg.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
src/assets/logoBg1.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
src/assets/logotext.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
src/assets/logotext1.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/navAction.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/password.png
Normal file
|
After Width: | Height: | Size: 806 B |
BIN
src/assets/username.png
Normal file
|
After Width: | Height: | Size: 945 B |
BIN
src/assets/work.png
Normal file
|
After Width: | Height: | Size: 993 B |
BIN
src/assets/workAction.png
Normal file
|
After Width: | Height: | Size: 993 B |
BIN
src/assets/worklogo.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
160
src/components/EChartsComponent.vue
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="chart" style="width: 500px; height: 300px;"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { tkhostdata, dicts, tkhostdetail } from '@/api/account';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EChartsComponent',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
dataType: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
seriesData: [],
|
||||||
|
num: 0,
|
||||||
|
inputTime: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
this.inputTime = this.time
|
||||||
|
this.getTkhostdetail();
|
||||||
|
console.log(this.time)
|
||||||
|
|
||||||
|
console.log(this.getPrevious7Days(this.inputTime))
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initChart() {
|
||||||
|
if (!this.$refs.chart) {
|
||||||
|
console.error('DOM element not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const myChart = echarts.init(this.$refs.chart);
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
text: this.title
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
legend: {
|
||||||
|
data: [this.title]
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: [this.getPrevious7Days(this.inputTime)[0].slice(4), this.getPrevious7Days(this.inputTime)[1].slice(4), this.getPrevious7Days(this.inputTime)[2].slice(4), this.getPrevious7Days(this.inputTime)[3].slice(4), this.getPrevious7Days(this.inputTime)[4].slice(4), this.getPrevious7Days(this.inputTime)[5].slice(4), this.getPrevious7Days(this.inputTime)[6].slice(4)]
|
||||||
|
},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.title,
|
||||||
|
type: 'line',
|
||||||
|
data: [this.seriesData[0], this.seriesData[1], this.seriesData[2], this.seriesData[3], this.seriesData[4], this.seriesData[5], this.seriesData[6]],
|
||||||
|
smooth: true
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
myChart.setOption(option);
|
||||||
|
console.log(this.dataTime)
|
||||||
|
|
||||||
|
},
|
||||||
|
getTkhostdetail() {
|
||||||
|
tkhostdetail({
|
||||||
|
hostId: this.id,
|
||||||
|
dataType: this.dataType,
|
||||||
|
searchTimeStart: this.getPrevious7Days(this.inputTime)[0],
|
||||||
|
searchTimeEnd: this.getPrevious7Days(this.inputTime)[6]
|
||||||
|
}).then(res => {
|
||||||
|
// console.log("返回数据", res[0][this.getPrevious7Days(this.inputTime)[2]])
|
||||||
|
//echarts 数据初始化
|
||||||
|
this.seriesData = [
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[0]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[0]][this.dataType]),
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[1]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[1]][this.dataType]),
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[2]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[2]][this.dataType]),
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[3]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[3]][this.dataType]),
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[4]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[4]][this.dataType]),
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[5]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[5]][this.dataType]),
|
||||||
|
res[0][this.getPrevious7Days(this.inputTime)[6]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[6]][this.dataType]),
|
||||||
|
]
|
||||||
|
|
||||||
|
this.initChart();
|
||||||
|
this.num++
|
||||||
|
console.log("返回数据", this.seriesData)
|
||||||
|
console.log("返回数据", this.num)
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// getCurrentDate() {
|
||||||
|
// const dates = [];
|
||||||
|
// const today = new Date();
|
||||||
|
|
||||||
|
// for (let i = 6; i >= 0; i--) {
|
||||||
|
// const date = new Date(today);
|
||||||
|
// date.setDate(today.getDate() - i);
|
||||||
|
|
||||||
|
// const year = date.getFullYear();
|
||||||
|
// const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
// const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
// dates.push(`${year}${month}${day}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return dates;
|
||||||
|
// },
|
||||||
|
getPrevious7Days(dateStr) {
|
||||||
|
// 验证输入格式是否正确
|
||||||
|
if (!/^\d{8}$/.test(dateStr)) {
|
||||||
|
console.error('输入的格式不是YYYYMMDD.');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析输入的日期字符串
|
||||||
|
const year = parseInt(dateStr.substring(0, 4));
|
||||||
|
const month = parseInt(dateStr.substring(4, 6)) - 1; // 月份从 0 开始
|
||||||
|
const day = parseInt(dateStr.substring(6, 8));
|
||||||
|
|
||||||
|
// 创建日期对象
|
||||||
|
const date = new Date(year, month, day);
|
||||||
|
|
||||||
|
// 存储结果的数组
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
// 计算前 7 天的日期
|
||||||
|
for (let i = 0; i <= 6; i++) {
|
||||||
|
const currentDate = new Date(date);
|
||||||
|
currentDate.setDate(date.getDate() - i); // 减去 i 天
|
||||||
|
|
||||||
|
// 格式化为 YYYYMMDD
|
||||||
|
const formattedDate = `${currentDate.getFullYear()}${(currentDate.getMonth() + 1).toString().padStart(2, '0')}${currentDate.getDate().toString().padStart(2, '0')}`;
|
||||||
|
result.push(formattedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按时间顺序排序(从最早到最晚)
|
||||||
|
result.sort((a, b) => a.localeCompare(b));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
148
src/components/Sidebar.vue
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="logo">
|
||||||
|
<!-- <img style="margin-right: 10px;" src="@/assets/logo.png"> -->
|
||||||
|
<img src="@/assets/logotext.png">
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li @click="updateActiveIndex(1)">
|
||||||
|
<div>
|
||||||
|
<img v-show="activeIndex == 1" src="@/assets/navAction.png" autoplay loop muted class="background-img">
|
||||||
|
<div style="display: flex;">
|
||||||
|
<img v-show="activeIndex == 1" src="@/assets/workAction.png" style="margin-right: 10px;">
|
||||||
|
<img v-show="activeIndex == 2" src="@/assets/workAction.png" style="margin-right: 10px;">
|
||||||
|
<div :style="activeIndex == 1 ? 'color: #000' : 'color: #fff'" class="center-justify">{{
|
||||||
|
$t('menu.workbenches')
|
||||||
|
}}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li @click="updateActiveIndex(2)">
|
||||||
|
<div>
|
||||||
|
<img v-show="activeIndex == 2" src="@/assets/navAction.png" autoplay loop muted class="background-img">
|
||||||
|
|
||||||
|
<div style="display: flex;">
|
||||||
|
<img v-show="activeIndex == 2" src="@/assets/listAction.png" style="margin-right: 10px;">
|
||||||
|
<img v-show="activeIndex == 1" src="@/assets/listAction.png" style="margin-right: 10px;">
|
||||||
|
<div :style="activeIndex == 2 ? 'color: #000' : 'color: #fff'" class="center-justify">{{
|
||||||
|
$t('menu.hostList')
|
||||||
|
}}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a @click="$router.push('/')" href="javascript:void(0);"
|
||||||
|
style="position: absolute; bottom: 30px; color: aliceblue; font-size: 20px; font-weight: 500;">
|
||||||
|
{{
|
||||||
|
$t('menu.logout')
|
||||||
|
}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import { getUser } from '@/utils/storage'
|
||||||
|
import { defineEmits } from 'vue';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const userInfo = ref(getUser())
|
||||||
|
|
||||||
|
let activeIndex = ref(1);
|
||||||
|
|
||||||
|
const emit = defineEmits(['activeIndex']);
|
||||||
|
|
||||||
|
|
||||||
|
const updateActiveIndex = (index) => {
|
||||||
|
activeIndex.value = index;
|
||||||
|
emit('activeIndex', index);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 900px;
|
||||||
|
width: 280px;
|
||||||
|
background-color: @bg-color;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
border-bottom: 1px solid #fff;
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
img:nth-of-type(1) {
|
||||||
|
height: 66px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img:nth-of-type(2) {
|
||||||
|
height: 29px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li {
|
||||||
|
margin-top: 50px;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-left: 30px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
height: 64px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000000;
|
||||||
|
display: block;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 22px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .sidebar a:hover {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
.background-img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 13px;
|
||||||
|
width: 247px;
|
||||||
|
height: 126px;
|
||||||
|
object-fit: cover;
|
||||||
|
z-index: -1;
|
||||||
|
/* 确保视频在内容之下 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-justify {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
16
src/i18n/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import en from '@/locales/en'
|
||||||
|
import zh from '@/locales/zh'
|
||||||
|
|
||||||
|
const messages = {
|
||||||
|
en,
|
||||||
|
zh
|
||||||
|
}
|
||||||
|
|
||||||
|
const i18n = createI18n({
|
||||||
|
locale: 'zh', // 默认语言
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
messages
|
||||||
|
})
|
||||||
|
|
||||||
|
export default i18n
|
||||||
78
src/locales/en.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
export default {
|
||||||
|
login: {
|
||||||
|
title: 'Account login',
|
||||||
|
version: 'VERSION',
|
||||||
|
login: 'Login',
|
||||||
|
tenantName: 'Tenant Name',
|
||||||
|
account: 'account',
|
||||||
|
password: 'password',
|
||||||
|
Language: 'Language',
|
||||||
|
network: 'NetWork',
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
workbenches: 'Work Benches',
|
||||||
|
hostList: 'Streamer List',
|
||||||
|
logout: 'Logout',
|
||||||
|
},
|
||||||
|
workbenches: {
|
||||||
|
openTK: 'OpenTK',
|
||||||
|
totalnumber: 'Total Number',
|
||||||
|
createHost: 'Create Streamer',
|
||||||
|
query: 'Can query',
|
||||||
|
invite: 'Can invited',
|
||||||
|
runTime: 'Run Time',
|
||||||
|
guildAccount: 'Guild account',
|
||||||
|
guildPass: 'Guild password',
|
||||||
|
guildAccountPlace: 'Please enter your login account',
|
||||||
|
guildPassPlace: 'Please enter your login password',
|
||||||
|
queriedNum: 'Today queried times',
|
||||||
|
loginBackend: 'Login backend',
|
||||||
|
workbenches: 'Work Benches',
|
||||||
|
},
|
||||||
|
workbenchesSetup: {
|
||||||
|
workbenches: 'Work Benches',
|
||||||
|
network: 'Current Network',
|
||||||
|
setCoinsNum: 'Set Coins Number',
|
||||||
|
setFansNum: 'Set Fans Number',
|
||||||
|
setQuery: 'Set Query Frequency',
|
||||||
|
minCoinsNum: 'Min Coins Number',
|
||||||
|
maxCoinsNum: 'Max Coins Number',
|
||||||
|
minFansNum: 'Min Fans Number',
|
||||||
|
maxFansNum: 'Max Fans Number',
|
||||||
|
hour: 'times/hour',
|
||||||
|
hour24: 'times/24hour',
|
||||||
|
start: 'Start Obtaining Data',
|
||||||
|
stop: 'Stop',
|
||||||
|
},
|
||||||
|
hostList: {
|
||||||
|
placeCountry: 'Select country',
|
||||||
|
placeSeletTime: 'Select query time',
|
||||||
|
placeHostId: 'Please enter the anchor ID',
|
||||||
|
selectAll: 'All',
|
||||||
|
query: 'Query',
|
||||||
|
export: 'Export Excel data',
|
||||||
|
hostId: 'Streamer ID',
|
||||||
|
grade: 'Grade',
|
||||||
|
country: 'Country',
|
||||||
|
creationTime: 'Creation Time',
|
||||||
|
anchorcoins: 'Anchor Coins',
|
||||||
|
yesterdayGoldCoins: 'Yesterday Gold Coins',
|
||||||
|
fansNum: 'Number Fans',
|
||||||
|
followersNum: 'Number Followers',
|
||||||
|
onlineFans: 'Online Fans',
|
||||||
|
anchorType: 'Anchor Type',
|
||||||
|
min: 'min',
|
||||||
|
max: 'max',
|
||||||
|
placeMin: 'Please enter the minimum value',
|
||||||
|
placeMax: 'Please enter the maximum value',
|
||||||
|
sort: 'sort',
|
||||||
|
sortType: 'sort Type',
|
||||||
|
ascending: 'ascending',
|
||||||
|
descending: 'descending',
|
||||||
|
reset: 'reset',
|
||||||
|
sure: 'sure',
|
||||||
|
invitationType: 'invitationType',
|
||||||
|
invitationType1: 'Regular',
|
||||||
|
invitationType2: 'Golden',
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/locales/zh.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
export default {
|
||||||
|
login: {
|
||||||
|
title: '账号登陆',
|
||||||
|
version: '版本号',
|
||||||
|
login: '登录',
|
||||||
|
tenantName: '租户名称',
|
||||||
|
account: '账户',
|
||||||
|
password: '密码',
|
||||||
|
Language: '语言设置',
|
||||||
|
network: '网络设置',
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
workbenches: '工作台',
|
||||||
|
hostList: '主播列表',
|
||||||
|
logout: '退出登录',
|
||||||
|
},
|
||||||
|
workbenches: {
|
||||||
|
openTK: '开启TK',
|
||||||
|
totalnumber: '总数量',
|
||||||
|
createHost: '新建主播',
|
||||||
|
query: '查询',
|
||||||
|
invite: '邀请',
|
||||||
|
runTime: '运行时间',
|
||||||
|
guildAccount: '公会账号',
|
||||||
|
guildPass: '公会密码',
|
||||||
|
guildAccountPlace: '请输入登录账号',
|
||||||
|
guildPassPlace: '请输入登录密码',
|
||||||
|
queriedNum: '今日已查询次数',
|
||||||
|
loginBackend: '登录后台',
|
||||||
|
},
|
||||||
|
workbenchesSetup: {
|
||||||
|
workbenches: '工作台',
|
||||||
|
network: '当前网络',
|
||||||
|
setCoinsNum: '设置金币数量',
|
||||||
|
setFansNum: '设置粉丝数量',
|
||||||
|
setQuery: '后台查询频率',
|
||||||
|
minCoinsNum: '最小金币数量',
|
||||||
|
maxCoinsNum: '最大金币数量',
|
||||||
|
minFansNum: '最小粉丝数量',
|
||||||
|
maxFansNum: '最大粉丝数量',
|
||||||
|
hour: '次/小时',
|
||||||
|
hour24: '次/24小时',
|
||||||
|
start: '开始获取数据',
|
||||||
|
stop: '停止',
|
||||||
|
},
|
||||||
|
hostList: {
|
||||||
|
placeCountry: '选择国家',
|
||||||
|
placeSeletTime: '选择查询时间',
|
||||||
|
placeHostId: '请输入主播id',
|
||||||
|
selectAll: '全部',
|
||||||
|
query: '查询',
|
||||||
|
export: '导出Excel数据',
|
||||||
|
hostId: '主播id',
|
||||||
|
grade: '等级',
|
||||||
|
country: '国家',
|
||||||
|
creationTime: '创建时间',
|
||||||
|
anchorcoins: '主播金币',
|
||||||
|
yesterdayGoldCoins: '昨日金币',
|
||||||
|
fansNum: '粉丝数',
|
||||||
|
followersNum: '关注数',
|
||||||
|
onlineFans: '在线粉丝',
|
||||||
|
anchorType: '主播类型',
|
||||||
|
min: '最小值',
|
||||||
|
max: '最大值',
|
||||||
|
placeMin: '请输入最小值',
|
||||||
|
placeMax: '请输入最大值',
|
||||||
|
sort: '排序',
|
||||||
|
sortType: '排序方式',
|
||||||
|
ascending: '升序',
|
||||||
|
descending: '降序',
|
||||||
|
reset: '重置',
|
||||||
|
sure: '确定',
|
||||||
|
invitationType: '邀请类型',
|
||||||
|
invitationType1: '普票',
|
||||||
|
invitationType2: '金票',
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/main.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'; // 引入中文语言包
|
||||||
|
import i18n from './i18n'
|
||||||
|
|
||||||
|
// createApp(App).use(store).use(router).mount('#app')
|
||||||
|
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
// app.use(ElementPlus, {
|
||||||
|
// locale: zhCn, // 配置中文
|
||||||
|
// });
|
||||||
|
app.use(i18n) // 注册 i18n
|
||||||
|
app.use(ElementPlus) // 注册 ElementPlus
|
||||||
|
app.use(createPinia()); // 注册 Pinia
|
||||||
|
app.use(store); // 注册 store
|
||||||
|
app.use(router); // 注册 router
|
||||||
|
app.mount('#app');
|
||||||
|
|
||||||
34
src/router/index.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
||||||
|
import HomeView from '../views/HomeView.vue'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
|
component: HomeView
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/nav',
|
||||||
|
name: 'nav',
|
||||||
|
// redirect: '/nav/hostsList', // 默认跳转
|
||||||
|
component: () => import(/* webpackChunkName: "hostsList" */ '../views/nav.vue'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'hostsList',
|
||||||
|
name: 'hostsList',
|
||||||
|
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/hostsList.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'workBenches',
|
||||||
|
name: 'workBenches',
|
||||||
|
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/workbenches.vue')
|
||||||
|
},]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
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; // 黄色按钮主色
|
||||||
14
src/store/index.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { createStore } from 'vuex'
|
||||||
|
|
||||||
|
export default createStore({
|
||||||
|
state: {
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
}
|
||||||
|
})
|
||||||
12
src/stores/notice.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const noticeStore = defineStore('noticeNum', {
|
||||||
|
state: () => {
|
||||||
|
return { data: { num: 0 } };
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
increment() {
|
||||||
|
this.data.num++;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
187
src/utils/axios.js
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/**
|
||||||
|
* axios请求封装
|
||||||
|
* https://rudon.blog.csdn.net/
|
||||||
|
*/
|
||||||
|
import axios from 'axios'
|
||||||
|
import { getToken, getUser } from '@/utils/storage'
|
||||||
|
import router from '@/router'
|
||||||
|
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { usePythonBridge, } from '@/utils/pythonBridge'
|
||||||
|
|
||||||
|
const { stopScript } = usePythonBridge();
|
||||||
|
|
||||||
|
|
||||||
|
// 请求地址前缀
|
||||||
|
let baseURL = ''
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// 生产环境
|
||||||
|
// baseURL = "https://api.tkpage.yolozs.com"
|
||||||
|
baseURL = "http://192.168.1.174:8101"
|
||||||
|
// baseURL = "http://47.79.98.113:8101"
|
||||||
|
} else {
|
||||||
|
// 测试环境
|
||||||
|
// baseURL = "http://120.26.251.180:8085/"
|
||||||
|
// 开发环境
|
||||||
|
baseURL = "http://47.79.98.113:8101"
|
||||||
|
// baseURL = "http://api.tkpage.vvtiktok.cn"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
axios.interceptors.request.use((config) => {
|
||||||
|
console.log("config", config)
|
||||||
|
const url = sliceUrl(config.url)
|
||||||
|
console.log("url", url)
|
||||||
|
if (!(config.url == 'doLogin' || config.url == 'get-id-by-name')) {
|
||||||
|
config.headers['vvtoken'] = getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求超时时间 - 毫秒
|
||||||
|
config.timeout = 60000
|
||||||
|
config.baseURL = baseURL
|
||||||
|
// 自定义Content-type
|
||||||
|
config.headers['Content-type'] = 'application/json'
|
||||||
|
return config;
|
||||||
|
}, (error) => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
axios.interceptors.response.use((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) {
|
||||||
|
stopScript();
|
||||||
|
router.push('/')
|
||||||
|
ElMessage.error(response.data.code + '' + response.data.message);
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.data.code + '' + response.data.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, (error) => {
|
||||||
|
// 可添加请求失败后的处理逻辑
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// axios的get请求
|
||||||
|
export function getAxios({ url, params }) {
|
||||||
|
// 使用axios发送GET请求
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.get(url, {
|
||||||
|
params
|
||||||
|
// 请求成功,将返回的数据传递给resolve函数
|
||||||
|
}).then(res => {
|
||||||
|
resolve(res)
|
||||||
|
// 请求失败,将错误信息传递给reject函数
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// axios的post请求
|
||||||
|
export function postAxios({ url, data }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.post(
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then(res => {
|
||||||
|
resolve(res)
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const downFile = async (urlstr, data) => {
|
||||||
|
|
||||||
|
// 发送请求,获取文件流
|
||||||
|
const response = await axios.post(urlstr, data, { responseType: 'blob' });
|
||||||
|
|
||||||
|
// 获取文件名(如果后端设置了 Content-Disposition)
|
||||||
|
const contentDisposition = response.headers['content-disposition'];
|
||||||
|
let fileName = 'default-file-name'; // 默认文件名
|
||||||
|
console.log(contentDisposition)
|
||||||
|
console.log(response)
|
||||||
|
if (contentDisposition) {
|
||||||
|
// 从响应头中提取文件名
|
||||||
|
const fileNameMatch = contentDisposition.match(/filename="(.+)"/);
|
||||||
|
if (fileNameMatch && fileNameMatch.length > 1) {
|
||||||
|
fileName = fileNameMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个临时的下载链接
|
||||||
|
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName; // 设置下载的文件名
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// 释放 URL 对象
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
//请求前验证
|
||||||
|
// function cheekalive() {
|
||||||
|
// axios.post('api/account/cheekalive', {
|
||||||
|
// userId: getUser().userId,
|
||||||
|
// currcode: getToken(),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ).then(res => {
|
||||||
|
// console.log(res.data)
|
||||||
|
// if (res.data) {
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// stopScript();
|
||||||
|
// alert("账号在其他地方登录!")
|
||||||
|
// window.location.href = '/';
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//节流函数
|
||||||
|
function throttle(func, limit) {
|
||||||
|
let inThrottle;
|
||||||
|
return function () {
|
||||||
|
const args = arguments;
|
||||||
|
const context = this;
|
||||||
|
if (!inThrottle) {
|
||||||
|
func.apply(context, args);
|
||||||
|
inThrottle = true;
|
||||||
|
setTimeout(() => inThrottle = false, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
261
src/utils/countryUtil.js
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
// country-utils.js
|
||||||
|
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: "津巴布韦"
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getCountryName(code) {
|
||||||
|
return CountryCode[code] || null;
|
||||||
|
}
|
||||||
134
src/utils/pythonBridge.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// pythonBridge.js
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
const bridge = ref(null);
|
||||||
|
// 初始化 QWebChannel
|
||||||
|
const initBridge = () => {
|
||||||
|
if (/localhost/.test(window.location.href)) return
|
||||||
|
new QWebChannel(qt.webChannelTransport, (channel) => {
|
||||||
|
bridge.value = channel.objects.bridge;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export function usePythonBridge() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 调用 Python 方法
|
||||||
|
const fetchDataConfig = (data) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (bridge.value) {
|
||||||
|
bridge.value.fetchDataConfig(data, function (result) {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询获取主播的数据
|
||||||
|
const fetchDataCount = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (bridge.value) {
|
||||||
|
bridge.value.fetchDataCount(function (result) {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开tk后台
|
||||||
|
const loginTikTok = () => {
|
||||||
|
if (bridge.value) {
|
||||||
|
bridge.value.loginTikTok(function (result) {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// 登录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 givePyAnchorId = (id) => {
|
||||||
|
|
||||||
|
if (bridge.value) {
|
||||||
|
bridge.value.givePyAnchorId(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));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
const stopScript = () => {
|
||||||
|
if (bridge.value) {
|
||||||
|
bridge.value.stopScript();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//获取版本号
|
||||||
|
const getVersion = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (bridge.value) {
|
||||||
|
bridge.value.currentVersion(function (result) {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 在组件挂载时初始化桥接
|
||||||
|
onMounted(initBridge);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetchDataConfig,
|
||||||
|
fetchDataCount,
|
||||||
|
loginBackStage,
|
||||||
|
loginTikTok,
|
||||||
|
givePyAnchorId,
|
||||||
|
backStageloginStatus,
|
||||||
|
backStageloginStatusCopy,
|
||||||
|
exportToExcel,
|
||||||
|
stopScript,
|
||||||
|
getVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
54
src/utils/storage.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
export function setToken(token) {
|
||||||
|
localStorage.setItem('token', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getToken() {
|
||||||
|
return localStorage.getItem('token');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeToken() {
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setUser(user) {
|
||||||
|
|
||||||
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUser() {
|
||||||
|
return JSON.parse(localStorage.getItem('user'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setNumData(numData) {
|
||||||
|
localStorage.setItem('num', JSON.stringify(numData));
|
||||||
|
}
|
||||||
|
export function getNumData() {
|
||||||
|
return JSON.parse(localStorage.getItem('num'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出一个函数,用于设置用户密码
|
||||||
|
export function setUserPass(userdata) {
|
||||||
|
localStorage.setItem('userPass', JSON.stringify(userdata));
|
||||||
|
}
|
||||||
|
// 导出一个函数,用于获取用户密码
|
||||||
|
export function getUserPass() {
|
||||||
|
return JSON.parse(localStorage.getItem('userPass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于设置tk账户密码
|
||||||
|
export function setTkUser(userdata) {
|
||||||
|
localStorage.setItem('tkuser', JSON.stringify(userdata));
|
||||||
|
}
|
||||||
|
// 用于获取tk账户密码
|
||||||
|
export function getTkUser() {
|
||||||
|
return JSON.parse(localStorage.getItem('tkuser'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于列表筛选条件
|
||||||
|
export function setSerch(data) {
|
||||||
|
localStorage.setItem('Serch', JSON.stringify(data));
|
||||||
|
}
|
||||||
|
// 用于获取列表筛选条件
|
||||||
|
export function getSerch() {
|
||||||
|
return JSON.parse(localStorage.getItem('Serch'));
|
||||||
|
}
|
||||||
366
src/views/HomeView.vue
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
<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>
|
||||||
|
<el-dropdown>
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<span style="font-size:20px; color: #fff;">
|
||||||
|
{{ $t('login.network') }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="setup-item center-justify">
|
||||||
|
<div></div>
|
||||||
|
<el-dropdown>
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<span style="font-size:20px; color: #fff;">
|
||||||
|
{{ $t('login.Language') }}
|
||||||
|
</span>
|
||||||
|
<!-- <el-icon class="el-icon--right">
|
||||||
|
<arrow-down />
|
||||||
|
</el-icon> -->
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="switchLanguage('zh')">
|
||||||
|
中文
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="switchLanguage('en')">
|
||||||
|
English
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<!-- <el-radio-group v-model="localeData" size="large">
|
||||||
|
<el-radio-button @click="switchLanguage('zh')" label="中文" />
|
||||||
|
<el-radio-button @click="switchLanguage('en')" label="English" />
|
||||||
|
</el-radio-group> -->
|
||||||
|
|
||||||
|
</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;height: 100%;" src="@/assets/logo.png"> -->
|
||||||
|
<img style="height: 100%;" src="@/assets/logotext.png">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- From -->
|
||||||
|
<div class="from">
|
||||||
|
<div class="from-title center-justify">
|
||||||
|
<div>{{ $t('login.title') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="from-input">
|
||||||
|
<el-form label-position="left" label-width="100px" :model="formData">
|
||||||
|
<div class="from-input-item1">
|
||||||
|
<img src="@/assets/username.png" alt="">
|
||||||
|
<el-input style="height: 25px;" v-model="formData.tenantName"
|
||||||
|
:placeholder="$t('login.tenantName')" clearable @keyup.enter="onSubmit" />
|
||||||
|
</div>
|
||||||
|
<div class="from-input-item1">
|
||||||
|
<img src="@/assets/username.png" alt="">
|
||||||
|
<el-input style="height: 25px;" v-model="formData.userId"
|
||||||
|
:placeholder="$t('login.account')" clearable @keyup.enter="onSubmit" />
|
||||||
|
</div>
|
||||||
|
<div class="from-input-item1">
|
||||||
|
<img src="@/assets/password.png" alt="">
|
||||||
|
<el-input style="height: 25px; " v-model="formData.password" type="password"
|
||||||
|
:placeholder="$t('login.password')" show-password @keyup.enter="onSubmit" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="from-input-item">
|
||||||
|
<el-button class="loginButton" color="#8f7ee7" type="primary" @click="onSubmit">{{
|
||||||
|
$t('login.login') }}</el-button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="version center-justify ">{{ $t('login.version') }}:{{ version }}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { login, getIdByName } from '@/api/account';
|
||||||
|
import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/utils/storage';
|
||||||
|
import { ElLoading } from 'element-plus';
|
||||||
|
import { usePythonBridge } from '@/utils/pythonBridge'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
|
||||||
|
const { locale } = useI18n()
|
||||||
|
let localeData = ref(locale.value == 'zh' ? '中文' : 'English')
|
||||||
|
function switchLanguage(lang) {
|
||||||
|
console.log(localeData.value)
|
||||||
|
locale.value = lang
|
||||||
|
localStorage.setItem('lang', lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { getVersion, stopScript } = usePythonBridge();
|
||||||
|
let version = ref('0.0.0');
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
stopScript();
|
||||||
|
setTimeout(() => {
|
||||||
|
getVersion().then((res) => {
|
||||||
|
version.value = res;
|
||||||
|
})
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const formData = ref({
|
||||||
|
tenantName: getUserPass() == null ? '' : getUserPass().tenantName,
|
||||||
|
userId: getUserPass() == null ? '' : getUserPass().userId,
|
||||||
|
password: getUserPass() == null ? '' : getUserPass().password,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
const loading = ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text: 'Loading',
|
||||||
|
background: 'rgba(0, 0, 0, 0.7)',
|
||||||
|
});
|
||||||
|
setUserPass(formData.value);
|
||||||
|
getIdByName(formData.value.tenantName).then((tenantId) => {
|
||||||
|
console.log(tenantId)
|
||||||
|
login({
|
||||||
|
tenantId: Number(tenantId),
|
||||||
|
username: formData.value.userId,
|
||||||
|
password: formData.value.password,
|
||||||
|
}).then((res) => {
|
||||||
|
loading.close();
|
||||||
|
console.log(res)
|
||||||
|
setToken(res.tokenValue);
|
||||||
|
setUser(res);
|
||||||
|
router.push('/nav');
|
||||||
|
}).catch((err) => {
|
||||||
|
loading.close();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.main {
|
||||||
|
width: 1600px;
|
||||||
|
height: 900px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
/* 页面无法选中 */
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.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;
|
||||||
|
/* 防止内容溢出 */
|
||||||
|
|
||||||
|
.version {
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
left: calc(50% - 50px);
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// width: 1600px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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:nth-child(1) {
|
||||||
|
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: @bg-color;
|
||||||
|
background-color: #ffffff44;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
padding: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.from-title {
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 24px;
|
||||||
|
color: @bg-color;
|
||||||
|
line-height: 37px;
|
||||||
|
|
||||||
|
|
||||||
|
div {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
// border-bottom: 4px solid #1db97d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.from-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
|
||||||
|
.from-input-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
.from-input-item-title {
|
||||||
|
color: @bg-color;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 80px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginButton {
|
||||||
|
width: 359px;
|
||||||
|
height: 50px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 24px;
|
||||||
|
border: 1px solid #FFFFFF;
|
||||||
|
|
||||||
|
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 18px;
|
||||||
|
color: @bg-color;
|
||||||
|
line-height: 37px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.from-input-item1 {
|
||||||
|
display: flex;
|
||||||
|
width: 359px;
|
||||||
|
height: 50px;
|
||||||
|
background: @bg-color-light-light;
|
||||||
|
border-radius: 24px;
|
||||||
|
border: 1px solid #FFFFFF;
|
||||||
|
padding: 12px 25px 13px 25px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-justify {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-align {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper {
|
||||||
|
--el-input-focus-border-color: rgba(255, 255, 0, 0);
|
||||||
|
--el-menu-hover-bg-color: rgba(255, 255, 0, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
::v-deep(.el-input__wrapper) {
|
||||||
|
background-color: rgba(255, 0, 0, 0);
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-input__inner) {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-input__inner::placeholder) {
|
||||||
|
color: @bg-color;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
595
src/views/hosts/hostsList.vue
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
<template>
|
||||||
|
<div class="hostList">
|
||||||
|
<div>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<el-select v-model="searchForm.country" filterable :placeholder="$t('hostList.placeCountry')" size="large"
|
||||||
|
style="width: 160px">
|
||||||
|
<el-option :label="$t('hostList.selectAll')" :value="''" />
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
<el-date-picker v-model="searchForm.createTime" type="date" value-format="YYYY-MM-DD"
|
||||||
|
:placeholder="$t('hostList.placeSeletTime')" size="large" style="margin-left: 50px;width: 160px;" />
|
||||||
|
|
||||||
|
<el-input v-model="searchForm.hostsId" :placeholder="$t('hostList.placeHostId')" size="large"
|
||||||
|
style="width: 160px; margin-left: 50px;" clearable />
|
||||||
|
|
||||||
|
<el-button class="serch-button" style="margin-left: 50px;" type="primary" @click="serch">{{
|
||||||
|
$t('hostList.query') }}</el-button>
|
||||||
|
<el-button class="put-button" :disabled="tableData.length == 0" type="primary" @click="exportList">{{
|
||||||
|
$t('hostList.export') }}</el-button>
|
||||||
|
<!-- <el-button class="put-button" type="primary" @click="dialogFormVisible = true">分配给指定员工</el-button> -->
|
||||||
|
<el-button @click="filterdialogVisible = true" style="width: 50px;" class="put-button" type="primary"><img
|
||||||
|
style="height: 30px;" src="@/assets/filter.png"></el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hostTable center-justify">
|
||||||
|
<el-table ref="multipleTableRef" :data="tableData" stripe v-loading="loading" height="500"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="35" />
|
||||||
|
|
||||||
|
<el-table-column fixed prop="hostId" :label="$t('hostList.hostId')" width="160">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="hostIdText" @click="openHTML(scope.row.hostId)"> {{
|
||||||
|
scope.row.hostId }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
|
||||||
|
<el-table-column prop="hostlevel" :label="$t('hostList.grade')" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.hostlevel }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="invitationType" :label="$t('hostList.invitationType')" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
|
||||||
|
<el-tag :type="scope.row.invitationType == 1 ? 'success' : 'warning'">
|
||||||
|
{{ scope.row.invitationType == 1 ? $t('hostList.invitationType1') : $t('hostList.invitationType2') }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column v-for="label in labelList" :key="label.paramCode" :prop="label.paramCode"
|
||||||
|
:label="label.paramCodeMeaning" width="120">
|
||||||
|
<template v-if="label.paramCode != 'createDt'" #default="scope">
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <el-table-column label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<el-button type="primary" @click="getTkhostdetail(scope.row.hostId)">查看</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="center-justify" style="margin-top: 30px;">
|
||||||
|
<el-pagination v-model:current-page="page" v-model:page-size="pageSize" background
|
||||||
|
layout="sizes, prev, pager, next" :total="total" :page-sizes="[10, 20, 50, 100]"
|
||||||
|
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<el-dialog v-model="filterdialogVisible" width="800px" :before-close="handleClose">
|
||||||
|
<el-row v-for="(field, index) in fields" :key="index" :gutter="20" style="margin-bottom: 10px">
|
||||||
|
<el-col :span="4">
|
||||||
|
<div style="height: 100%; padding-top: 10px" class="center-justify">
|
||||||
|
{{ field.label }}
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<div><label>{{ $t('hostList.min') }}</label></div>
|
||||||
|
<el-input type="number" :oninput="'if(value.length>9)value=value.slice(0,9)'"
|
||||||
|
v-model.number="searchForm[field.minModel]" :placeholder="$t('hostList.placeMin')" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<div><label>{{ $t('hostList.max') }}</label></div>
|
||||||
|
<el-input type="number" :oninput="'if(value.length>9)value=value.slice(0,9)'"
|
||||||
|
v-model.number="searchForm[field.maxModel]" :placeholder="$t('hostList.placeMax')" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="4">
|
||||||
|
<!-- <label>选择筛选条件</label> -->
|
||||||
|
<div style="height: 100%;padding-top: 10px;" class="center-justify">
|
||||||
|
{{ $t('hostList.sort') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<div><label>{{ $t('hostList.sortType') }}</label></div>
|
||||||
|
|
||||||
|
|
||||||
|
<el-select v-model="sortData.sortType" filterable placeholder="请选择" style="width: 240px">
|
||||||
|
<el-option v-for="item in sortNameOptions" :key="item.type" :label="item.label" :value="item.type" />
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<div><label>{{ $t('hostList.ascending') }}/{{ $t('hostList.descending') }}</label></div>
|
||||||
|
|
||||||
|
<el-select v-model="sortData.sortForm" filterable placeholder="请选择" style="width: 240px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in [{ label: $t('hostList.ascending'), value: 'asc' }, { label: $t('hostList.descending'), value: 'desc' }]"
|
||||||
|
:key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="reset">
|
||||||
|
{{ $t('hostList.reset') }}
|
||||||
|
</el-button>
|
||||||
|
<!-- <el-button @click="filterdialogVisible = false">取消</el-button> -->
|
||||||
|
<el-button type="primary" @click="handelClick">
|
||||||
|
{{ $t('hostList.sure') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// import { getToken, setToken, removeToken } from '@/utils/storage'
|
||||||
|
import { tkhostdata, dicts, tkhostdetail, downList, getStaffList, managerhosts, upholdinfo, getCountryinfo, accountName } from '@/api/account';
|
||||||
|
import { usePythonBridge, } from '@/utils/pythonBridge'
|
||||||
|
import { getUser, setSerch, getSerch } from '@/utils/storage'
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
// import EChartsComponent from '@/components/EChartsComponent.vue';
|
||||||
|
// import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
// import { color } from 'echarts';
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const loading = ref(false)
|
||||||
|
//py方法
|
||||||
|
const { givePyAnchorId, exportToExcel } = usePythonBridge();
|
||||||
|
|
||||||
|
let num = ref(0)
|
||||||
|
//账号信息
|
||||||
|
const userInfo = ref(getUser())
|
||||||
|
//主播列表DOM
|
||||||
|
const multipleTableRef = ref(null)
|
||||||
|
|
||||||
|
let labelList = ref([
|
||||||
|
{ paramCode: 'country', paramCodeMeaning: t('hostList.country') },
|
||||||
|
{ paramCode: 'createTime', paramCodeMeaning: t('hostList.creationTime') },
|
||||||
|
{ paramCode: 'hostsCoins', paramCodeMeaning: t('hostList.anchorcoins') },
|
||||||
|
{ paramCode: 'yesterdayCoins', paramCodeMeaning: t('hostList.yesterdayGoldCoins') },
|
||||||
|
{ paramCode: 'fans', paramCodeMeaning: t('hostList.fansNum') },
|
||||||
|
{ paramCode: 'fllowernum', paramCodeMeaning: t('hostList.followersNum') },
|
||||||
|
{ paramCode: 'onlineFans', paramCodeMeaning: t('hostList.onlineFans') },
|
||||||
|
{ paramCode: 'hostsKind', paramCodeMeaning: t('hostList.anchorType') },
|
||||||
|
// { paramCode: 'invitationType', paramCodeMeaning: t('hostList.invitationType') },
|
||||||
|
|
||||||
|
]);
|
||||||
|
const tableData = ref([])
|
||||||
|
//主播列表传参
|
||||||
|
const searchForm = ref({})
|
||||||
|
const fields = [
|
||||||
|
{ label: t('hostList.fansNum'), minModel: 'fansMin', maxModel: 'fansMax' },
|
||||||
|
{ label: t('hostList.onlineFans'), minModel: 'onlineFansMin', maxModel: 'onlineFansMax' },
|
||||||
|
{ label: t('hostList.anchorcoins'), minModel: 'hostsCoinsMin', maxModel: 'hostsCoinsMax' },
|
||||||
|
{ label: t('hostList.followersNum'), minModel: 'fllowernumMin', maxModel: 'fllowernumMax' },
|
||||||
|
]
|
||||||
|
//排序
|
||||||
|
let sortData = ref({ sortForm: 'desc', sortType: "createTime" })
|
||||||
|
|
||||||
|
//排序类型
|
||||||
|
let sortNameOptions = ref([
|
||||||
|
{ label: t('hostList.creationTime'), type: 'createTime' },
|
||||||
|
{ label: t('hostList.anchorcoins'), type: 'hostsCoins' },
|
||||||
|
{ label: t('hostList.fansNum'), type: 'fans' },
|
||||||
|
{ label: t('hostList.yesterdayGoldCoins'), type: 'yesterdayCoins' },
|
||||||
|
{ label: t('hostList.onlineFans'), type: 'onlineFans' },
|
||||||
|
{ label: t('hostList.followersNum'), type: 'fllowernum' },
|
||||||
|
|
||||||
|
])
|
||||||
|
//员工选择列表
|
||||||
|
let staffOptions = ref([])
|
||||||
|
//筛选条件选择列表
|
||||||
|
|
||||||
|
//选择的员工
|
||||||
|
let staffValue = ref('')
|
||||||
|
//选择的主播列表
|
||||||
|
let selectHostList = ref([])
|
||||||
|
//分配弹窗是否弹出
|
||||||
|
let dialogFormVisible = ref(false)
|
||||||
|
//分配情况弹窗是否弹出
|
||||||
|
let hostNameVisible = ref(false)
|
||||||
|
|
||||||
|
//备注弹窗是否弹出
|
||||||
|
let commentVisible = ref(false)
|
||||||
|
//筛选弹窗是否弹出
|
||||||
|
let filterdialogVisible = ref(false)
|
||||||
|
//分配的员工
|
||||||
|
let staffId = ref({})
|
||||||
|
//备注信息
|
||||||
|
let commentInfo = ref('')
|
||||||
|
//备注信息主播
|
||||||
|
let commentHost = ref('')
|
||||||
|
//分页
|
||||||
|
let pageSize = ref(10)
|
||||||
|
let page = ref(1)
|
||||||
|
let total = ref(0)
|
||||||
|
//是否渲染
|
||||||
|
const isPopoverVisible = reactive({})
|
||||||
|
|
||||||
|
|
||||||
|
let options = ref([])
|
||||||
|
|
||||||
|
let version = ref('0.0.0');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getCountry(); //获取国家
|
||||||
|
|
||||||
|
// getSerchStorage();//获取搜索条件
|
||||||
|
|
||||||
|
getlist();//获取主播列表
|
||||||
|
})
|
||||||
|
|
||||||
|
function serch() {
|
||||||
|
page.value = 1
|
||||||
|
console.log(t('hostList.yesterdayGoldCoins'))
|
||||||
|
getlist();
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportList() {
|
||||||
|
if (searchForm.value.dataType == 'InvitationType') {
|
||||||
|
searchForm.value.dataEnd = searchForm.value.dataStart
|
||||||
|
}
|
||||||
|
exportToExcel({})
|
||||||
|
|
||||||
|
|
||||||
|
// //浏览器导出方法
|
||||||
|
// downList('export/hostsinfo',
|
||||||
|
// {
|
||||||
|
// searchTime: searchForm.value.time,
|
||||||
|
// region: searchForm.value.country,
|
||||||
|
// pageSize: pageSize.value,
|
||||||
|
// page: page.value,
|
||||||
|
// userId: userInfo.value.userId,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
}
|
||||||
|
//分页每页条数
|
||||||
|
function handleSizeChange(val) {
|
||||||
|
console.log(`${val} items per page`)
|
||||||
|
getlist();
|
||||||
|
|
||||||
|
}
|
||||||
|
//分页页数
|
||||||
|
function handleCurrentChange(val) {
|
||||||
|
console.log(`current page: ${val}`)
|
||||||
|
getlist();
|
||||||
|
|
||||||
|
}
|
||||||
|
//选择行
|
||||||
|
function handleSelectionChange(data) {
|
||||||
|
console.log(data)
|
||||||
|
selectHostList.value = []
|
||||||
|
data.forEach(item => {
|
||||||
|
selectHostList.value.push(item.hostId)
|
||||||
|
})
|
||||||
|
// multipleTableRef.value = data
|
||||||
|
// console.log(multipleTableRef.value)
|
||||||
|
|
||||||
|
}
|
||||||
|
//获取主播列表
|
||||||
|
const getlist = () => {
|
||||||
|
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
console.log(searchForm.value)
|
||||||
|
tkhostdata({
|
||||||
|
tenantId: Number(userInfo.value.tenantId),
|
||||||
|
sort: sortData.value.sortForm,//正序倒序
|
||||||
|
sortName: sortData.value.sortType,//排序类型
|
||||||
|
"current": page.value,
|
||||||
|
"pageSize": pageSize.value,
|
||||||
|
...searchForm.value,//筛选条件
|
||||||
|
}).then(res => {
|
||||||
|
loading.value = false
|
||||||
|
if (res) {
|
||||||
|
console.log('主播列表', res)
|
||||||
|
total.value = Number(res.total)
|
||||||
|
tableData.value = res.records.map(item => ({
|
||||||
|
hostId: item.hostsId, // 注意:原字段是 hostId,你的数据是 hostsId,需手动映射
|
||||||
|
hostlevel: item.hostsLevel, // 原字段 hostlevel 对应你的数据 hostsLevel
|
||||||
|
country: item.country,
|
||||||
|
createTime: item.createTime,
|
||||||
|
fans: item.fans,
|
||||||
|
fllowernum: item.fllowernum,
|
||||||
|
hostsCoins: item.hostsCoins,
|
||||||
|
hostsKind: item.hostsKind,
|
||||||
|
onlineFans: item.onlineFans,
|
||||||
|
yesterdayCoins: item.yesterdayCoins,
|
||||||
|
// 保留原有字段(如 belongBy、useable 等)
|
||||||
|
belongBy: item.belongBy,
|
||||||
|
useable: item.useable,
|
||||||
|
invitationType: item.invitationType,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
function handelClick() {
|
||||||
|
filterdialogVisible.value = false
|
||||||
|
}
|
||||||
|
function reset() {
|
||||||
|
searchForm.value.fansMin = null
|
||||||
|
searchForm.value.fansMax = null
|
||||||
|
searchForm.value.onlineFansMin = null
|
||||||
|
searchForm.value.onlineFansMax = null
|
||||||
|
searchForm.value.hostsCoinsMin = null
|
||||||
|
searchForm.value.hostsCoinsMax = null
|
||||||
|
searchForm.value.fllowernumMin = null
|
||||||
|
searchForm.value.fllowernumMax = null
|
||||||
|
}
|
||||||
|
function handleClose(done) {
|
||||||
|
console.log('关闭')
|
||||||
|
// searchForm.value = {
|
||||||
|
// dataType: '',
|
||||||
|
// dataStart: '',
|
||||||
|
// dataEnd: '',
|
||||||
|
// }
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//修改主播维护状态
|
||||||
|
// function handleSelectChange(event, data) {
|
||||||
|
|
||||||
|
// upholdinfo({
|
||||||
|
// "hostId": data.hostId,
|
||||||
|
// "userId": userInfo.value.userId,
|
||||||
|
// "tenantId": userInfo.value.tenantId,
|
||||||
|
// // "comment": "我已经尽力维护,但是失败了",
|
||||||
|
// "useable": event.target.value
|
||||||
|
// }).then(res => {
|
||||||
|
// console.log(res)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
//更改主播维护备注
|
||||||
|
|
||||||
|
// function uphostcomment() {
|
||||||
|
// upholdinfo({
|
||||||
|
// "hostId": commentHost.value,
|
||||||
|
// "userId": userInfo.value.userId,
|
||||||
|
// "tenantId": userInfo.value.tenantId,
|
||||||
|
// "comment": commentInfo.value,
|
||||||
|
// }).then(res => {
|
||||||
|
// console.log(res)
|
||||||
|
// serch()
|
||||||
|
// commentVisible.value = false
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
function filterTag(value, row) {
|
||||||
|
console.log(row.useable, value)
|
||||||
|
return row.useable === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取国家
|
||||||
|
function getCountry() {
|
||||||
|
getCountryinfo({}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
res.forEach(item => {
|
||||||
|
if (item.countryGroupName) {
|
||||||
|
options.value.push({ value: item.countryGroupName, label: item.countryGroupName })
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log(options.value)
|
||||||
|
}).catch(err => {
|
||||||
|
console.log('getCountry', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取下级员工
|
||||||
|
// const getStaff = () => {
|
||||||
|
// getStaffList({
|
||||||
|
// userId: userInfo.value.userId,
|
||||||
|
// activeYn: userInfo.value.activeYn,
|
||||||
|
// tenantId: userInfo.value.tenantId,
|
||||||
|
// }).then(res => {
|
||||||
|
// console.log(res)
|
||||||
|
// res.forEach(item => {
|
||||||
|
// staffOptions.value.push({
|
||||||
|
// value: item.userId,
|
||||||
|
// label: item.userName
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// //分配主播给员工
|
||||||
|
// function allocation() {
|
||||||
|
// managerhosts({
|
||||||
|
// "manaId": userInfo.value.userId,
|
||||||
|
// "userId": staffValue.value,
|
||||||
|
// "tenantId": userInfo.value.tenantId,
|
||||||
|
// "hostIds": selectHostList.value
|
||||||
|
// }).then(res => {
|
||||||
|
// if (res) {
|
||||||
|
// dialogFormVisible.value = false
|
||||||
|
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
//获取主播信息
|
||||||
|
// const getTkhostdetail = (id) => {
|
||||||
|
// tkhostdetail({
|
||||||
|
// hostId: id,
|
||||||
|
// // page: 1,
|
||||||
|
// searchTimeStart: '20250401',
|
||||||
|
// searchTimeEnd: '20250403'
|
||||||
|
// }).then(res => {
|
||||||
|
|
||||||
|
// console.log(labelList.value)
|
||||||
|
|
||||||
|
// })
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
function openPopover(hostId, paramCode) {
|
||||||
|
|
||||||
|
isPopoverVisible[`${hostId}-${paramCode}`] = true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePopover(hostId, paramCode) {
|
||||||
|
|
||||||
|
// isPopoverVisible[`${hostId}-${paramCode}`] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function openHTML(id) {
|
||||||
|
givePyAnchorId(id)
|
||||||
|
|
||||||
|
// upholdinfo({
|
||||||
|
// "hostId": id,
|
||||||
|
// "userId": userInfo.value.userId,
|
||||||
|
// "tenantId": userInfo.value.tenantId,
|
||||||
|
// // "comment": "我已经尽力维护,但是失败了",
|
||||||
|
// "useable": "Y"
|
||||||
|
// }).then(res => {
|
||||||
|
|
||||||
|
// getlist();
|
||||||
|
// })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// function openAccountName(idStr) {
|
||||||
|
// if (idStr) {
|
||||||
|
// hostNameVisible.value = true
|
||||||
|
// accountName(idStr).then(res => {
|
||||||
|
// staffId.value = JSON.stringify(res).replace(/[{}"]/g, '') // 移除所有 {} 和 "
|
||||||
|
// .split(',') // 按逗号分割成数组
|
||||||
|
|
||||||
|
// console.log(res)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.hostList {
|
||||||
|
box-sizing: border-box;
|
||||||
|
// height: 100vh;
|
||||||
|
padding: 40px;
|
||||||
|
|
||||||
|
/* 页面无法选中 */
|
||||||
|
// -webkit-user-select: none;
|
||||||
|
// -moz-user-select: none;
|
||||||
|
// -ms-user-select: none;
|
||||||
|
// user-select: none;
|
||||||
|
|
||||||
|
.hostTable {
|
||||||
|
width: 100%;
|
||||||
|
padding: 40px 0;
|
||||||
|
|
||||||
|
.hostIdText {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.serch-button {
|
||||||
|
width: 80px;
|
||||||
|
height: 47px;
|
||||||
|
background: @btn-bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.put-button {
|
||||||
|
width: 132px;
|
||||||
|
height: 47px;
|
||||||
|
background: @btn-bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog {
|
||||||
|
|
||||||
|
--el-dialog-font-line-height: 50px;
|
||||||
|
--el-dialog-width: 600px;
|
||||||
|
--el-dialog-border-radius: 8px;
|
||||||
|
|
||||||
|
// border: 10px solid @bg-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-justify {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-align {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
::v-deep(.el-input__wrapper) {
|
||||||
|
background-color: #F2FAF9;
|
||||||
|
border: 1px solid @bg-color;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-select__wrapper) {
|
||||||
|
background-color: #F2FAF9;
|
||||||
|
border: 1px solid @bg-color;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-pagination.is-background .el-pager li.is-active) {
|
||||||
|
background-color: @bg-color;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
765
src/views/hosts/workbenches.vue
Normal file
@@ -0,0 +1,765 @@
|
|||||||
|
<template>
|
||||||
|
<div class="center-line workbenches">
|
||||||
|
<div class="center-align" style="width: 100%; margin: 0 20px;">
|
||||||
|
<div class="box-card-num1 center-line">
|
||||||
|
<div>{{ $t('workbenches.totalnumber') }}: <span>{{ hostData.totalCount }}</span></div>
|
||||||
|
<div>{{ $t('workbenches.createHost') }}: <span>{{ hostData.validAnchorsCount }}</span></div>
|
||||||
|
<div> {{ $t('workbenches.query') }}: <span>{{ hostData.checkedDataCount }}</span></div>
|
||||||
|
<div>{{ $t('workbenches.invite') }}: <span>{{ hostData.canInvitationCount }}</span></div>
|
||||||
|
<div>{{ $t('workbenches.runTime') }}: <span>{{ formattedTime }}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="center-line" style="padding-top: 15vh;">
|
||||||
|
<el-button class="open-login" type="primary" @click="openTK">{{ $t('workbenches.openTK') }}</el-button>
|
||||||
|
<!-- <el-button class="open-login" type="primary" @click="startTimer">计时开始</el-button> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<el-card class="box-card-num" v-for="(item, index) in 2" :key="index">
|
||||||
|
<div class="center-justify">
|
||||||
|
<div class="from-input-item">
|
||||||
|
<div class="from-input-item-title center-justify">
|
||||||
|
{{ $t('workbenches.guildAccount') }}:
|
||||||
|
</div>
|
||||||
|
<el-input :disabled="!(tkData[index].code == 0 && !isLogin[index])"
|
||||||
|
v-model="tkData[index].account" :placeholder="$t('workbenches.guildAccountPlace')"
|
||||||
|
clearable />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="from-input-item">
|
||||||
|
<div class="from-input-item-title center-justify">
|
||||||
|
{{ $t('workbenches.guildPass') }}:
|
||||||
|
</div>
|
||||||
|
<el-input :disabled="!(tkData[index].code == 0 && !isLogin[index])"
|
||||||
|
v-model="tkData[index].password" type="password"
|
||||||
|
:placeholder="$t('workbenches.guildPassPlace')" show-password />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-button class="open-login" style="margin-left: 60px;"
|
||||||
|
:disabled="!(tkData[index].code == 0 && !isLogin[index])" type="primary"
|
||||||
|
@click="loginTK(index)">{{ $t('workbenches.loginBackend') }}</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>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="todayCount"> {{ $t('workbenches.queriedNum') }}:{{ tkData[index].num }}</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container ">
|
||||||
|
|
||||||
|
<el-card class="box-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="center-justify"><img src="@/assets/worklogo.png">
|
||||||
|
{{ $t('workbenchesSetup.workbenches') }} </span>
|
||||||
|
<div style="margin-right: 120px;">{{ $t('workbenchesSetup.workbenches') }}:{{
|
||||||
|
locale == 'zh' ? countryData : countryDataEN }}
|
||||||
|
<!-- <el-button class="reset-button" @click="reset">重置数据</el-button> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="input-group">
|
||||||
|
<label>{{ $t('workbenchesSetup.setCoinsNum') }}</label>
|
||||||
|
<el-input type='number' v-model="pyData.gold.min" :min="0" :max="pyData.gold.max - 1"
|
||||||
|
:placeholder="$t('workbenchesSetup.minCoinsNum')" style="width: 100%"
|
||||||
|
:disabled="!pyData.isStart">
|
||||||
|
<template #prepend>{{ $t('workbenchesSetup.minCoinsNum') }}</template>
|
||||||
|
</el-input>
|
||||||
|
<el-input type='number' v-model="pyData.gold.max" :min="pyData.gold.min + 1" :max="100"
|
||||||
|
:placeholder="$t('workbenchesSetup.maxCoinsNum')" style="width: 100%; margin-top: 10px"
|
||||||
|
:disabled="!pyData.isStart">
|
||||||
|
<template #prepend>{{ $t('workbenchesSetup.maxCoinsNum') }}</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="input-group">
|
||||||
|
<label>{{ $t('workbenchesSetup.setFansNum') }}</label>
|
||||||
|
<el-input type='number' v-model="pyData.fans.min" :min="0" :max="pyData.fans.max - 1"
|
||||||
|
:placeholder="$t('workbenchesSetup.minFansNum')" style="width: 100%"
|
||||||
|
:disabled="!pyData.isStart">
|
||||||
|
<template #prepend>{{ $t('workbenchesSetup.minFansNum') }}</template>
|
||||||
|
</el-input>
|
||||||
|
<el-input type='number' v-model="pyData.fans.max" :min="pyData.fans.min + 1" :max="100"
|
||||||
|
:placeholder="$t('workbenchesSetup.maxFansNum')" style="width: 100%; margin-top: 10px"
|
||||||
|
:disabled="!pyData.isStart">
|
||||||
|
<template #prepend>{{ $t('workbenchesSetup.maxFansNum') }}</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="input-group">
|
||||||
|
<label>{{ $t('workbenchesSetup.setQuery') }}</label>
|
||||||
|
<!-- <el-input type='number' v-model="pyData.frequency.hour" @input="handleInputHour" -->
|
||||||
|
<el-input type='number' v-model="pyData.frequency.hour"
|
||||||
|
:placeholder="$t('workbenchesSetup.hour')" style="width: 100%"
|
||||||
|
:disabled="!pyData.isStart">
|
||||||
|
<template #append>{{ $t('workbenchesSetup.hour') }}</template>
|
||||||
|
</el-input>
|
||||||
|
<!-- <el-input type='number' v-model="pyData.frequency.day" @input="handleInputDay" -->
|
||||||
|
<el-input type='number' v-model="pyData.frequency.day"
|
||||||
|
:placeholder="$t('workbenchesSetup.hour24')" style="width: 100%; margin-top: 10px"
|
||||||
|
:disabled="!pyData.isStart">
|
||||||
|
<template #append>{{ $t('workbenchesSetup.hour24') }}</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div style="margin-top: 20px; text-align: center">
|
||||||
|
|
||||||
|
<el-button class="submit-button" :disabled="submitting" v-show="pyData.isStart" type="primary"
|
||||||
|
@click="submit">{{
|
||||||
|
$t('workbenchesSetup.start') }}</el-button>
|
||||||
|
<el-button v-show="!pyData.isStart" type="danger" @click="unsubmit">{{
|
||||||
|
$t('workbenchesSetup.stop') }}</el-button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
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'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
const { locale } = useI18n()
|
||||||
|
//导入python交互方法
|
||||||
|
const { fetchDataConfig, fetchDataCount, loginBackStage, loginTikTok, backStageloginStatus, backStageloginStatusCopy } = usePythonBridge();
|
||||||
|
|
||||||
|
|
||||||
|
//ip国家
|
||||||
|
let countryData = ref('');
|
||||||
|
//英文国家
|
||||||
|
let countryDataEN = ref('');
|
||||||
|
|
||||||
|
//获取主播数量的定时器
|
||||||
|
let getHostTimer = ref(null);
|
||||||
|
//获取查询次数定时器
|
||||||
|
let getNumTimer = ref(null);
|
||||||
|
//获取的主播信息
|
||||||
|
let hostData = ref({
|
||||||
|
totalCount: 0,
|
||||||
|
validAnchorsCount: 0,
|
||||||
|
canInvitationCount: 0,
|
||||||
|
checkedDataCount: 0,
|
||||||
|
});
|
||||||
|
//是否开启tk
|
||||||
|
// 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([
|
||||||
|
{
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
index: 1,
|
||||||
|
code: 0,
|
||||||
|
num: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
index: 2,
|
||||||
|
code: 0,
|
||||||
|
num: 0
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
//python需要的数据
|
||||||
|
let pyData = ref({
|
||||||
|
gold: { min: 0, max: 0 },
|
||||||
|
fans: { min: 0, max: 0 },
|
||||||
|
frequency: { hour: 0, day: 0 },
|
||||||
|
isStart: true,
|
||||||
|
country: countryData.value,
|
||||||
|
test: '123',
|
||||||
|
test1: { test: 123, test12: 123 },
|
||||||
|
tenantId: getUser().tenantId,
|
||||||
|
userId: getUser().userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
//按钮提交状态
|
||||||
|
let submitting = ref(true);
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
//从缓存获取数据
|
||||||
|
if (getNumData()) {
|
||||||
|
pyData.value = getNumData();
|
||||||
|
}
|
||||||
|
if (getTkUser()) {
|
||||||
|
tkData.value = getTkUser();
|
||||||
|
tkData.value[0].code = 0;
|
||||||
|
tkData.value[1].code = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tkaccountuse(tkData.value[0].account, 0)
|
||||||
|
tkaccountuse(tkData.value[1].account, 1)
|
||||||
|
|
||||||
|
getIpInfo()
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const getIpInfo = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://ipapi.co/json/');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('请求失败');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('IP信息:', data.country);
|
||||||
|
countryDataEN.value = data.country_name
|
||||||
|
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',
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//提交数据到py
|
||||||
|
const submit = () => {
|
||||||
|
pyData.value.country = countryData.value;
|
||||||
|
console.log('提交的区间值:', pyData.value);
|
||||||
|
// 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 ((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(
|
||||||
|
'确认开始爬取数据?',
|
||||||
|
'开始',
|
||||||
|
{
|
||||||
|
confirmButtonText: '开始',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'success',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
// console.log('提交的区间值:', pyData.value.gold, pyData.value.fans, pyData.value.frequency);
|
||||||
|
//开始按钮的状态 改为禁用
|
||||||
|
submitting.value = true;
|
||||||
|
setNumData(pyData.value);
|
||||||
|
console.error('提交的区间值:', JSON.stringify(pyData.value));
|
||||||
|
|
||||||
|
fetchDataConfig(JSON.stringify({
|
||||||
|
gold: pyData.value.gold,
|
||||||
|
fans: pyData.value.fans,
|
||||||
|
frequency: pyData.value.frequency,
|
||||||
|
isStart: true,
|
||||||
|
country: countryData.value,
|
||||||
|
tenantId: getUser().tenantId,
|
||||||
|
userId: getUser().userId,
|
||||||
|
})).then((res) => {
|
||||||
|
//开始计时器
|
||||||
|
startTimer();
|
||||||
|
//开启查询次数
|
||||||
|
getHostTimer.value = setInterval(() => {
|
||||||
|
fetchDataCount().then((res) => {
|
||||||
|
hostData.value = JSON.parse(res);
|
||||||
|
})
|
||||||
|
}, 1000);
|
||||||
|
getNumTimer.value = setInterval(() => {
|
||||||
|
tkaccountuse(tkData.value[0].account, 0)
|
||||||
|
tkaccountuse(tkData.value[1].account, 1)
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
|
||||||
|
}).finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
pyData.value.isStart = false;
|
||||||
|
submitting.value = false;
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//停止
|
||||||
|
const unsubmit = () => {
|
||||||
|
fetchDataConfig(JSON.stringify({
|
||||||
|
gold: pyData.value.gold,
|
||||||
|
fans: pyData.value.fans,
|
||||||
|
frequency: pyData.value.frequency,
|
||||||
|
isStart: false,
|
||||||
|
country: countryData.value,
|
||||||
|
tenantId: getUser().tenantId,
|
||||||
|
userId: getUser().userId,
|
||||||
|
})).then((res) => {
|
||||||
|
pauseTimer();
|
||||||
|
pyData.value.isStart = true;
|
||||||
|
clearInterval(getHostTimer.value);
|
||||||
|
getHostTimer.value = null;
|
||||||
|
clearInterval(getNumTimer.value);
|
||||||
|
getNumTimer.value = null;
|
||||||
|
|
||||||
|
// ElMessage.sussec('已停止')
|
||||||
|
}).catch((err) => {
|
||||||
|
// ElMessage.error('停止失败')
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//重置
|
||||||
|
const reset = () => {
|
||||||
|
pyData.value.gold = { min: 0, max: 0 };
|
||||||
|
pyData.value.fans = { min: 0, max: 0 };
|
||||||
|
pyData.value.frequency = { hour: 0, day: 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const loginTK = (index) => {
|
||||||
|
setTkUser(tkData.value)
|
||||||
|
loginBackStage({
|
||||||
|
account: tkData.value[index].account,
|
||||||
|
password: tkData.value[index].password,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const openTK = () => {
|
||||||
|
// isTk.value = true;
|
||||||
|
// console.log(isTk.value)
|
||||||
|
loginTikTok();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getloginStatus() {
|
||||||
|
backStageloginStatus().then((res) => {
|
||||||
|
const data = JSON.parse(res);
|
||||||
|
tkData.value[data.index].code = data.code
|
||||||
|
|
||||||
|
if (data.code == 1) {
|
||||||
|
clearInterval(statusTimer);
|
||||||
|
statusTimer = null;
|
||||||
|
submitting.value = false
|
||||||
|
isLogin.value[1] = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function getloginStatusCopy() {
|
||||||
|
backStageloginStatusCopy().then((res) => {
|
||||||
|
const data = JSON.parse(res);
|
||||||
|
tkData.value[data.index].code = data.code
|
||||||
|
|
||||||
|
if (data.code == 1) {
|
||||||
|
clearInterval(statusTimer);
|
||||||
|
statusTimer = null;
|
||||||
|
submitting.value = false
|
||||||
|
isLogin.value[0] = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function tkaccountuse(id, index) {
|
||||||
|
let num = 0;
|
||||||
|
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">
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workbenches {
|
||||||
|
padding: 45px 29px 22px 27px;
|
||||||
|
|
||||||
|
/* 页面无法选中 */
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-card {
|
||||||
|
// width: 1240px;
|
||||||
|
height: 436px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-card-num1 {
|
||||||
|
|
||||||
|
width: 197px;
|
||||||
|
height: 321px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
|
||||||
|
border-radius: 24px;
|
||||||
|
// padding-top: 60px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
div {
|
||||||
|
height: 20%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
color: #8D8E8E;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #000;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-card-num {
|
||||||
|
width: 897px;
|
||||||
|
height: 145px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
|
||||||
|
border-radius: 24px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
padding-top: 18px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.todayCount {
|
||||||
|
padding: 15px 21px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.from-input-item {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.from-input-item-title {
|
||||||
|
color: #000000;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 100px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginButton {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.loginState {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #b90000;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #2D2727;
|
||||||
|
line-height: 37px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
margin: 22px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-login {
|
||||||
|
width: 100px;
|
||||||
|
height: 47px;
|
||||||
|
background: @btn-bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-button {
|
||||||
|
width: 132px;
|
||||||
|
height: 47px;
|
||||||
|
background: @btn-bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
margin-left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
width: 160px;
|
||||||
|
height: 47px;
|
||||||
|
background: @bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-justify {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-align {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
::v-deep(.el-input-group__prepend) {
|
||||||
|
background: @bg-color-light;
|
||||||
|
border-radius: 10px 0px 0px 10px;
|
||||||
|
border: 1px solid #B7CEC5;
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-input-group__append) {
|
||||||
|
background: @bg-color-light;
|
||||||
|
border-radius: 0px 10px 10px 0px;
|
||||||
|
border: 1px solid #B7CEC5;
|
||||||
|
font-family: Source Han Sans SC;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-input__wrapper) {
|
||||||
|
width: 218px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
width: 200px;
|
||||||
|
height: 48px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #B7CEC5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
89
src/views/nav.vue
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<Sidebar class="noneText" @activeIndex="activeIndexFn" />
|
||||||
|
<div class="content ">
|
||||||
|
<div v-show="activeIndexA == 1">
|
||||||
|
<workbenches />
|
||||||
|
</div>
|
||||||
|
<div v-show="activeIndexA == 2">
|
||||||
|
<hostsList />
|
||||||
|
</div>
|
||||||
|
<div style="position: absolute; bottom: 0; right: 0;">{{ version }}</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Sidebar from '../components/Sidebar.vue';
|
||||||
|
import { RouterLink, RouterView } from 'vue-router'
|
||||||
|
import hostsList from '@/views/hosts/hostsList.vue'
|
||||||
|
import workbenches from '@/views/hosts/workbenches.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { getUser } from '@/utils/storage'
|
||||||
|
// import { usePythonBridge } from '@/utils/pythonBridge'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let activeIndexA = ref(1)
|
||||||
|
|
||||||
|
function activeIndexFn(data) {
|
||||||
|
activeIndexA.value = data
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-container {
|
||||||
|
display: flex;
|
||||||
|
width: 1600px;
|
||||||
|
height: 900px;
|
||||||
|
background-color: @bg-color;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.noneText {
|
||||||
|
/* 页面无法选中 */
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 200px;
|
||||||
|
background-color: @bg-color;
|
||||||
|
padding: 20px;
|
||||||
|
/* box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin-left: 280px;
|
||||||
|
width: 1304px;
|
||||||
|
height: 868px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 36px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-justify {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
27
vue.config.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service');
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true,
|
||||||
|
css: {
|
||||||
|
loaderOptions: {
|
||||||
|
postcss: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: [
|
||||||
|
require('postcss-px-to-viewport')({
|
||||||
|
viewportWidth: 1600, // 视窗的宽度,对应设计稿宽度
|
||||||
|
viewportHeight: 900, // 视窗的高度,对应设计稿高度
|
||||||
|
unitPrecision: 3, // 指定 px 转换为视窗单位值的小数位数
|
||||||
|
viewportUnit: 'vw', // 指定需要转换成的视窗单位,vw 或者 vh
|
||||||
|
selectorBlackList: ['.ignore', '.hairlines'], // 指定不需要转换的类
|
||||||
|
minPixelValue: 1, // 小于或等于 1 px 不转换为视窗单位
|
||||||
|
mediaQuery: false // 允许在媒体查询中转换 px
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
less: {
|
||||||
|
additionalData: `@import "@/static/css/app.less";` // 注入全局变量文件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||