优化代码

This commit is contained in:
pengxiaolong
2025-07-31 22:07:21 +08:00
parent 60be63e2d2
commit d9b2d496be
44 changed files with 1608 additions and 2587 deletions

Binary file not shown.

9
package-lock.json generated
View File

@@ -8,10 +8,11 @@
"name": "tk-page",
"version": "0.1.0",
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.8.4",
"core-js": "^3.8.3",
"echarts": "^5.6.0",
"element-plus": "^2.9.7",
"element-plus": "^2.10.4",
"pinia": "^3.0.3",
"qwebchannel": "^6.2.0",
"vue": "^3.2.13",
@@ -6538,9 +6539,9 @@
"license": "ISC"
},
"node_modules/element-plus": {
"version": "2.9.7",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.9.7.tgz",
"integrity": "sha512-6vjZh5SXBncLhUwJGTVKS5oDljfgGMh6J4zVTeAZK3YdMUN76FgpvHkwwFXocpJpMbii6rDYU3sgie64FyPerQ==",
"version": "2.10.4",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.10.4.tgz",
"integrity": "sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",

View File

@@ -7,10 +7,11 @@
"build": "vue-cli-service build"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.8.4",
"core-js": "^3.8.3",
"echarts": "^5.6.0",
"element-plus": "^2.9.7",
"element-plus": "^2.10.4",
"pinia": "^3.0.3",
"qwebchannel": "^6.2.0",
"vue": "^3.2.13",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -9,7 +9,6 @@
<title>
<%= webpackConfig.name %>
</title>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<body>

View File

@@ -26,8 +26,6 @@ window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
}
</script>
<!-- <style lang="less">
/*每个页面公共css */
<style lang="less">
@import "@/static/css/app.less";
</style> -->
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

BIN
src/assets/push.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

173
src/components/Appaside.vue Normal file
View File

@@ -0,0 +1,173 @@
<template>
<div class="app-aside">
<div class="logo">
<img class="logo-img" src="@/assets/logo.png" alt="logo"></img>
</div>
<div class="Navigation">
<div class="Navigation-card" v-for="(item, index) in NavigationModule" :key="index" @click="handleClick(item.path, item.id)" :style="{backgroundColor: item.id === activeId|| item.id === 5? '#ffffff' : '' ,boxShadow: item.id === activeId|| item.id === 5? '5px 5px 15px rgba(0, 0, 0, 0.3)' : ''}">
<img class="Navigation-card-icon-img" :src="item.icon" alt="">
<div class="Navigation-card-name">{{ item.name }}</div>
</div>
</div>
<div class="Avatar">
<img class="Avatar-img" src="@/assets/logo.png" alt="logo"></img>
</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
import { setStorage , getStorage } from '@/utils/storage.js';
const router = useRouter();
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onBeforeMount, // 组件挂载前执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const NavigationModule = [
{
name: 'PK',
id: 1,
path: '/nav/PK',
icon: 'https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/Pk.png'
},
{
name: '站内信',
id: 2,
path: '/nav/Forum',
icon: 'https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/Forum.png'
},
{
name: '消息',
id: 3,
path: '/nav/Message',
icon: 'https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/Message.png'
},
{
name: '我的',
id: 4,
path: '/nav/Mine',
icon: 'https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/Mine.png'
},
{
id: 5,
icon: 'https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/push.png'
}
]
const activeId = ref(1);
function handleClick(path, id) {
if (id === 5) {
}else{
activeId.value = id;
router.push(path);
}
}
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
try{
activeId.value = getStorage('activeId');
0 }catch(e){
activeId.value = 1
}
});
onBeforeMount(()=>{
// 组件挂载前执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped lang="less">
.app-aside{
width: 100%;
height: 95%;
display: flex;
flex-direction: column;
align-items: center;
justify-content:space-between;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.logo{
width: 60px;
height: 60px;
margin-top: 20px;
border-radius: 10px;
}
.logo-img{
width: 60px;
height: 60px;
border-radius: 10px;
}
.Avatar{
width: 60px;
height: 60px;
border-radius: 50px;
}
.Avatar-img{
width: 60px;
height: 60px;
border-radius: 50px;
transition: all 0.3s ease;
}
.Avatar-img:hover{
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.3);
transform: scale(1.1);
opacity: 0.8;
}
.Navigation{
width: 100%;
height: 75%;
display: flex;
flex-direction: column;
align-items: center;
}
.Navigation-card{
width: 60px;
height: 60px;
border-radius: 10px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 30px;
transition: all 0.3s ease;
}
.Navigation-card-icon{
width: 20px;
height: 20px;
}
.Navigation-card-icon-img{
width: 20px;
height: 20px;
}
.Navigation-card-name{
font-size: 12px;
color:@Prompt-text-color;
margin-top: 5px;
}
.Navigation-card:hover{
transform: scale(1.1);
opacity: 0.8;
background-color: #ffffff;
}
</style>

View File

@@ -1,160 +0,0 @@
<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>

View File

@@ -1,142 +0,0 @@
<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">工作台</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">主播列表</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;">
退出登录
</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>

View File

@@ -0,0 +1,298 @@
<template>
<!-- 主播库 -->
<div class="anchor-library">
<el-splitter>
<el-splitter-panel size="70%">
<!-- 主播列表 -->
<div class="demo-panel">
<div
class="anchor-library-list"
style="overflow: auto"
v-infinite-scroll="load"
>
<div class="anchor-library-card" v-for="(item, index) in list" :key="index">
<div class="card-content">
<div class="card-avatar"></div>
<div class="personalInformation">
<div class="name">来自世界上最长名的国家的某个人</div>
<div class="GenderAndCountry">
<div class="Gender"></div>
<div class="Country">来自世界上最长名的国家的名称很长很长哦</div>
</div>
</div>
<div class="card-Operation">
<div class="modify">
<img
class="modify-icon"
src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/recompose.png"
alt=""
/>
</div>
<div class="delete">
<img
class="delete-icon"
src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/expurgate.png"
alt=""
/>
</div>
</div>
</div>
</div>
</div>
</div>
</el-splitter-panel>
<el-splitter-panel size="25%" :collapsible="true" :resizable="false">
<!-- 添加或修改主播 -->
<div class="demo-panel">
<div class="add-anchor-library">
<div class="title">添加我的主播</div>
<!-- <div class="title">
修改主播信息
</div> -->
<div class="add-anchor-library-content">
<div class="input-name">
<!-- 主播名称 -->
<el-input v-model="refname" size="large" placeholder="请输入主播名称" />
</div>
<div class="country">
<!-- 国家 -->
<el-select-v2
v-model="countryvalue"
filterable
:options="country"
placeholder="请选择国家"
size="large"
style="vertical-align: middle"
class="select"
/>
</div>
<div class="gender">
<!-- 性别 -->
<el-select-v2
v-model="gendervalue"
filterable
:options="genderOptions"
size="large"
placeholder="请选择性别"
style="vertical-align: middle"
class="select"
/>
</div>
<div class="Confirm">确认</div>
<div class="Reset">重置</div>
</div>
</div>
</div>
</el-splitter-panel>
</el-splitter>
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
import { getCountryNamesArray } from "../../utils/countryUtil";
const refname = ref("");
const country = ref([]);
country.value = getCountryNamesArray(); //国家条目
const genderOptions = [
{ value: 1, label: "男" },
{ value: 2, label: "女" },
]; // 性别选项
const list = ref([{}, {}, {}, {}, {}, {}]);
function load() {}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped lang="less">
.anchor-library {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.anchor-library-list {
width: 100%;
height: 100%;
}
.anchor-library-card {
width: 100%;
height: 150px;
margin-bottom: 15px;
margin-top: 15px;
display: flex;
justify-content: center;
align-items: center;
}
.card-content {
width: 90%;
height: 100%;
border-radius: 20px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
background-image: linear-gradient(45deg, @bg-Sidebar-color-bottom, @bg-color);
transition: all 0.4s ease;
display: flex;
align-items: center;
}
.card-content:hover {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.8);
transform: scale(1.1);
opacity: 0.8;
}
.card-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
background-color: #ffffff;
margin-left: 20px;
}
.personalInformation {
width: calc(100% - 340px);
height: 100px;
margin-left: 20px;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.name {
font-size: 20px;
font-weight: bold;
color: #333333;
}
.GenderAndCountry {
display: flex;
align-items: center;
}
.Gender {
font-size: 16px;
color: #666666;
border-radius: 50px;
background-color: #ffffff;
padding: 2px 20px 2px 20px;
margin-right: 20px;
}
.Country {
font-size: 16px;
color: #666666;
border-radius: 50px;
background-color: #ffffff;
padding: 2px 20px 2px 20px;
}
.card-Operation {
width: 200px;
height: 100px;
margin-left: 20px;
margin-right: 20px;
display: flex;
align-items: center;
justify-content: space-around;
}
.modify-icon {
width: 30px;
height: 30px;
}
.delete-icon {
width: 30px;
height: 30px;
}
.demo-panel {
width: 100%;
height: 100%;
}
.add-anchor-library {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.title {
width: 100%;
height: 70px;
font-size: 24px;
font-weight: bold;
color: #333333;
text-align: center;
line-height: 70px;
}
.add-anchor-library-content {
width: 100%;
height: calc(100% - 70px);
display: flex;
flex-direction: column;
align-items: center;
}
.input-name {
width: 80%;
height: 50px;
margin-top: 20px;
}
.country {
width: 80%;
height: 50px;
margin-top: 20px;
}
.gender {
width: 80%;
height: 80px;
margin-top: 20px;
}
.select {
width: 100%;
}
.Confirm {
width: 80%;
height: 50px;
margin-top: 200px;
text-align: center;
line-height: 50px;
background-color: #ffffff;
color: @Prompt-text-color;
font-size: 20px;
transition: all 0.4s ease;
border-radius: 20px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
}
.Confirm:hover {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.8);
transform: scale(1.1);
opacity: 0.8;
}
.Reset {
width: 80%;
height: 50px;
margin-top: 30px;
text-align: center;
line-height: 50px;
background-color: #ffffff;
color: @Prompt-text-color;
font-size: 20px;
transition: all 0.4s ease;
border-radius: 20px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
}
.Reset:hover {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.8);
transform: scale(1.1);
opacity: 0.8;
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<!-- 我的PK记录 -->
<div>我的PK记录</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
/* 样式定义 */
</style>

View File

@@ -0,0 +1,31 @@
<template>
<!-- PK信息 -->
<div>PK信息</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
/* 样式定义 */
</style>

View File

@@ -0,0 +1,31 @@
<template>
<!-- 积分列表 -->
<div>积分列表</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
/* 样式定义 */
</style>

View File

@@ -6,12 +6,15 @@ 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 * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App);
app.use(ElementPlus, {
locale: zhCn, // 配置中文
});
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus) // 注册 ElementPlus
app.use(createPinia()); // 注册 Pinia
app.use(store); // 注册 store

View File

@@ -11,19 +11,29 @@ const routes = [
{
path: '/nav',
name: 'nav',
// redirect: '/nav/hostsList', // 默认跳转
component: () => import(/* webpackChunkName: "hostsList" */ '../views/nav.vue'),
redirect: '/nav/PK', // 默认跳转
component: () => import('../views/nav.vue'),
children: [
{
path: 'hostsList',
name: 'hostsList',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/hostsList.vue')
path: 'PK',
name: 'pk',
component: () => import('../views/hosts/pk.vue')
},
{
path: 'Forum',
name: 'Forum',
component: () => import('../views/hosts/Forum.vue')
},
{
path: 'Message',
name: 'Message',
component: () => import('../views/hosts/Message.vue')
},
{
path: 'Mine',
name: 'Mine',
component: () => import('../views/hosts/Mine.vue')
},
// {
// path: 'workBenches',
// name: 'workBenches',
// component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/workbenches.vue')
// },
]
}
]

View File

@@ -1,4 +1,12 @@
@bg-color: #022b4e; // 主色
@bg-color-light: #022b4eaf; // 浅主
@bg-color-light-light: #022b4e1c; // 浅浅主
@btn-bg-color: #045dac; // 黄色按钮主
@bg-color: #F8F9FA;// 主色
@font-color: #212529; // 字体
@border-color: #E9ECEF; // 边框
@bg-Sidebar-color-top:#F8F9FA; // 侧边栏背景
@bg-Sidebar-color-bottom: #E9ECEF; // 侧边栏底色
@Supplementary-text-color: #495057; // 补充文字色
@Prompt-text-color: #6c757d; // 提示文字色
@bg-night-color: #1A1A1A; // 夜间模式色
@font-color-night: #E9ECEF; // 夜间模式字体色

View File

@@ -255,7 +255,6 @@ export const CountryCode = {
ZM: "赞比亚",
ZW: "津巴布韦"
};
export function getCountryName(code) {
return CountryCode[code] || null;
export function getCountryNamesArray() {
return Object.entries(CountryCode).map(([code, nickname]) => ({ value: nickname, label: nickname }));
}

View File

@@ -1,198 +0,0 @@
// pythonBridge.js
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
const pyBridge = ref(null);
// 初始化 QWebChannel
const initBridge = () => {
if (/localhost/.test(window.location.href)) return
new QWebChannel(qt.webChannelTransport, (channel) => {
pyBridge.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);
// });
// }
// });
// };
//删除前端储存数据
const deleteStorageData = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.deleteAccountInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//获取前端储存数据
const getStorageData = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.readAccountInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//设置前端储存数据
const setStorageData = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.storageAccountInfo(JSON.stringify(data),function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
// 查询获取大哥的数据
const controlTask = (data) => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.control_task(data,function (result) {
resolve(result);
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
//总数有效数
const getBrotherInfo = () => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.getBrotherInfo(function (result) {
resolve(JSON.parse(result));
});
}else{
console.log("pyBridge is null未连接上")
}
});
};
// 打开tk后台
const loginTikTok = () => {
if (pyBridge.value) {
pyBridge.value.loginTikTok(function (result) {
});
}else{
console.log("pyBridge is null未连接上")
}
};
// // 登录tk后台
// const loginBackStage = (data) => {
// if (bridge.value) {
// if (data.index == 0) {
// bridge.value.loginBackStage(JSON.stringify(data));
// } else if (data.index == 1) {
// bridge.value.loginBackStageCopy(JSON.stringify(data));
// }
// }
// };
//跳转到主播页面
const givePyAnchorId = (id) => {
console.log("id",id);
if (pyBridge.value) {
pyBridge.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 (pyBridge .value) {
pyBridge .value.exportToExcel(JSON.stringify(data));
}
};
const stopScript = () => {
if (pyBridge .value) {
pyBridge .value.stopScript();
}
};
//获取版本号
const getVersion = () => {
return new Promise((resolve, reject) => {
if (pyBridge.value) {
pyBridge.value.currentVersion(function (result) {
resolve(result);
});
}
});
};
// 在组件挂载时初始化桥接
onMounted(initBridge);
return {
loginTikTok,
exportToExcel,
stopScript,
controlTask,
getBrotherInfo,
getVersion,
givePyAnchorId,
getStorageData,
setStorageData,
deleteStorageData,
};
}

View File

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

View File

@@ -1,351 +1,30 @@
<template>
<div class="main">
<div class="container">
<div class="right">
<img src="../assets/logoBg.png" class="background-video" alt="" />
<!-- 设置 -->
<div class="center-align">
<div></div>
<div class="setup">
<div class="setup-item center-justify">
<div></div>
<span> 网络设置 </span>
</div>
<div class="setup-item center-justify">
<div></div>
<span> 简体中文 </span>
</div>
</div>
</div>
<div class="center-line" style="margin-top: 40px">
<!-- logo -->
<div class="logo">
<!-- <div class="center-justify" style="height: 80px; width: 300px;">
<img style="height: 100%;" src="@/assets/logotext.png">
</div> -->
</div>
<!-- From -->
<div class="from">
<div class="from-title center-justify">
<div>账号登陆</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="租户名称"
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="账号"
clearable
@keyup.enter="onSubmit"
/>
</div>
<div class="from-input-item1">
<img src="@/assets/password.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.password"
type="password"
placeholder="密码"
show-password
@keyup.enter="onSubmit"
/>
</div>
<div class="from-input-item">
<el-button
class="loginButton"
color="#8f7ee7"
type="primary"
@click="onSubmit"
>登录</el-button
>
</div>
</el-form>
</div>
</div>
</div>
<div class="version center-justify">版本号{{ version }}</div>
</div>
</div>
</div>
<div></div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import { login, rentgetloginID } from "@/api/account";
import { getToken, setToken, setUser, setUserPass, getUserPass } from "@/utils/storage";
import { ElLoading } from "element-plus";
import { usePythonBridge } from "@/utils/pythonBridge";
import { ElMessage } from 'element-plus';
import { tokenStore,UserStore } from '@/stores/notice'
const tokenCache = tokenStore()
const userCache = UserStore()
const { getVersion } = usePythonBridge();
let version = ref("0.0.0");
onMounted(() => {
setTimeout(() => {
getVersion().then((res) => {
version.value = res;
});
getpassword();
}, 500);
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
async function getpassword(){
const res = await getUserPass();
formData.value = {
tenantName: res == null ? "" : res.tenantName,
userId: res == null ? "" : res.userId,
password: res == null ? "" : res.password,
};
}
const router = useRouter();
const formData = ref({});
const onSubmit = () => {
const loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
});
rentgetloginID({
name: formData.value.tenantName,
})
.then((res) => {
console.log(res);
login({
username: formData.value.userId,
tenantId: res,
password: formData.value.password,
})
.then((res) => {
loading.close();
console.log(res);
setToken(res.tokenValue);
tokenCache.setToken(res.tokenValue)
userCache.setUser(res)
setUser(res);
setUserPass(formData.value);
router.push("/nav");
})
.catch((err) => {
loading.close();
});
})
.catch((err) => {
loading.close();
console.log(err);
});
};
</script>
<style lang="less">
.main {
width: 1600px;
height: 900px;
overflow: hidden;
box-sizing: border-box;
/* 页面无法选中 */
-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 {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rgb(255, 255, 255);
margin-right: 5px;
}
}
}
.logo {
padding: 20px 0;
height: 80px;
}
.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 scoped>
/* 样式定义 */
</style>

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

@@ -0,0 +1,77 @@
<template>
<div class="forum">
<div v-infinite-scroll="load" class="infinite-list" style="overflow: auto">
<el-card style="width: 90%">
<template #header>
<div class="card-header">
<span>Card name</span>
</div>
</template>
<p v-for="o in 2" :key="o" class="text item">{{ "List item " + o }}</p>
<template #footer>
<div class="card-footer">
<span>Card footer</span>
</div>
</template>
</el-card>
</div>
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
function load() {
// 加载更多数据
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped lang="less">
.forum {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.infinite-list {
width: 90%;
height: 90%;
display: flex;
flex-direction: column;
align-items: center;
}
.card-header{
font-size: 18px;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
}
.card-footer{
font-size: 14px;
display: flex;
justify-content:flex-end;
align-items: center;
}
</style>

218
src/views/hosts/Message.vue Normal file
View File

@@ -0,0 +1,218 @@
<template>
<div class="message">
<div style="height: 100%">
<el-splitter>
<el-splitter-panel size="17%" min="17%" max="25%">
<!-- 会话列表 -->
<div class="demo-panel">
<div
v-infinite-scroll="load"
infinite-scroll-distance="100px"
class="chatList"
style="overflow: auto"
>
<div class="chatItem" v-for="(item, index) in chatList" :key="index">
<div class="Avatar"></div>
<div class="chatContent">
<div class="PersonalInfo">
<div class="name">名称</div>
<div class="time">07-29 14:00</div>
</div>
<div class="chatText">最后一条消息</div>
</div>
</div>
</div>
</div>
</el-splitter-panel>
<el-splitter-panel>
<el-splitter layout="vertical">
<el-splitter-panel>
<!-- 消息列表 -->
<div class="demo-panel"></div>
</el-splitter-panel>
<el-splitter-panel size="20%" min="20%" max="40%">
<!-- 输入框 -->
<div class="demo-panel">
<div class="inputBox">
<div class="Console">
<div class="Console-content">
</div>
</div>
<div class="input">
<textarea
v-model="textarea"
class="textarea"
style="width: 100%; height: 100%"
/>
</div>
</div>
</div>
</el-splitter-panel>
</el-splitter>
</el-splitter-panel>
</el-splitter>
</div>
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const textarea = ref(""); // 输入框内容
const chatList = ref([
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
]);
function load() {
console.log("加载更多",textarea.value);
} // 加载更多
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped lang="less">
.message {
width: 100%;
height: 100%;
}
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.chatList {
width: 100%;
height: 100%;
background-color: #ffffff;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.chatItem {
width: 100%;
height: 70px;
background-color: #ffffff;
display: flex;
align-items: center;
}
.chatItem:hover {
background-color: #f5f5f5;
}
.Avatar {
width: 50px;
height: 50px;
border-radius: 50%;
margin-left: 10px;
background-color: #f5f5f5;
}
.chatContent {
margin-left: 10px;
width: calc(100% - 80px);
height: 60%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.PersonalInfo {
display: flex;
align-items: center;
justify-content: space-between;
}
.name {
width: 50%;
font-size: 14px;
color: @font-color;
font-weight: bold;
white-space: nowrap; /* 单行显示 */
overflow: hidden;
text-overflow: ellipsis; /* 显示省略号 */
}
.time {
font-size: 12px;
color: @Prompt-text-color;
}
.chatText {
font-size: 10px;
color: @font-color;
width: 100%;
white-space: nowrap; /* 单行显示 */
overflow: hidden;
text-overflow: ellipsis; /* 显示省略号 */
}
.inputBox {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.Console {
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.Console-content{
width: 98%;
height: 100%;
}
.input {
width: 98%;
height: calc(100% - 70px);
}
.textarea{
width: 100%;
height: 100%;
border: none;
outline: none;
overflow: hidden;
background-color:#ffffff00;
resize:none;
}
</style>

105
src/views/hosts/Mine.vue Normal file
View File

@@ -0,0 +1,105 @@
<template>
<div class="mine">
<div class="custom-style">
<el-segmented v-model="segmentedvalue" :options="options" block>
<template #default="scope">
<div class="flex flex-col items-center gap-2 p-2">
<el-icon size="1.5vw">
<component :is="scope.item.icon" />
</el-icon>
<div>{{ scope.item.label }}</div>
</div>
</template>
</el-segmented>
</div>
<div class="mine-content">
<PointsList v-if="segmentedvalue === 4" />
<PKRecord v-if="segmentedvalue === 3" />
<PKmessage v-if="segmentedvalue === 2" />
<AnchorLibrary v-if="segmentedvalue === 1" />
</div>
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
import {
Document,
Mic,
Coin,
List
} from '@element-plus/icons-vue';
import PointsList from "@/components/mineSubComponent/PointsList";
import PKRecord from "@/components/mineSubComponent/PKRecord";
import PKmessage from "@/components/mineSubComponent/PKmessage";
import AnchorLibrary from "@/components/mineSubComponent/AnchorLibrary";
const options = [
{
label: '主播库',
value: 1,
icon: Coin,
},
{
label: 'PK信息',
value: 2,
icon: Mic,
},
{
label: '我的PK记录',
value: 3,
icon: List,
},
{
label: '积分列表',
value: 4,
icon: Document,
},
]
const segmentedvalue = ref(1);
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped lang="less">
.mine{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.custom-style .el-segmented {
height: 70px;
--el-segmented-item-selected-color: var(--el-text-color-primary);
--el-segmented-item-selected-bg-color: @border-color;
--el-border-radius-base: 20px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.mine-content{
width: 100%;
height: calc(100% - 70px);
}
</style>

View File

@@ -1,834 +0,0 @@
<template>
<div class="hostList">
<div>
<!-- -->
<div style="display: flex">
<div></div>
<el-input
v-model="queryFormData.coinMin"
placeholder="最小金币"
size="large"
style="width: 180px"
type="number"
/>
<el-input
v-model="queryFormData.coinMax"
placeholder="最大金币"
size="large"
style="width: 180px;"
class="right-input"
type="number"
/>
<el-input
v-model="queryFormData.levelMin"
placeholder="最小等级"
size="large"
style="width: 180px;"
class="right-input"
type="number"
/>
<el-input
v-model="queryFormData.levelMax"
placeholder="最大等级"
size="large"
style="width: 180px;"
class="right-input"
type="number"
/>
<div class="right-input right-text" style="width: 250px; height: 50px; line-height: 50px">
当前网络{{ countryData }}
</div>
<div class="right-input right-text" style="width: 160px; height: 50px; line-height: 50px">
总数{{ getBrotherInfodata.total }}
</div>
<div class="right-input right-text" style="width: 160px;height: 50px; line-height: 50px">
有效数{{ getBrotherInfodata.valid }}
</div>
<el-button
class="serch-button right-input"
type="primary"
@click="Resetss"
>重置</el-button
>
<el-button
class="serch-button right-input"
style="width: 150px"
type="primary"
@click="openTikTok"
>打开 TikTok 登录</el-button
>
<el-button
v-show="queryFormData.isRunning"
class="serch-button right-input"
type="primary"
@click="getBigBrother"
>开始</el-button
>
<el-button
v-show="!queryFormData.isRunning"
class="serch-button right-input"
type="primary"
@click="BigBrotherstop"
>结束</el-button
>
</div>
<!-- `````````````````````````````````````````````````````````````````````````````````````````````````` -->
<div style="width: 100%; border-bottom: 1px solid #e9e9e9; margin-top: 30px"></div>
<!-- ···································································································· -->
<div style="display: flex; margin-top: 30px; flex-wrap: wrap">
<el-select
v-model="searchForm.region"
filterable
placeholder="选择国家"
size="large"
style="width: 160px"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input
v-model="searchForm.displayId"
placeholder="大哥id"
size="large"
style="width: 160px;"
class="right-input"
clearable
/>
<el-button
class="serch-button right-input"
type="primary"
@click="serch"
>查询</el-button
>
<el-button class="serch-button" type="primary" @click="reset"> 重置 </el-button>
<el-button
class="put-button"
:disabled="tableData.length == 0"
type="primary"
@click="exportList"
>导出Excel数据</el-button
>
<el-button
@click="filterdialogVisible = true"
class="put-button buttoMore-filters"
type="primary"
><img class="filters-img" src="@/assets/filter.png"
/><div style="margin-left: 10px">
更多筛选
</div></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"
max-height="500"
@selection-change="handleSelectionChange"
>
<!-- <el-table-column type="selection" width="35" /> -->
<el-table-column fixed prop="displayId" label="Id" :width="screenWidth">
<template #default="scope">
<div class="hostIdText" @click="openHTML(scope.row.displayId)">
{{ scope.row.displayId }}
</div>
</template>
</el-table-column>
<el-table-column
v-for="label in labelList"
:key="label.paramCode"
:prop="label.paramCode"
:label="label.paramCodeMeaning"
:width="screenWidth"
>
<template v-if="label.paramCode != 'createDt'" #default="scope"> </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, 500, 1000]"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- ·······································································弹窗······································································· -->
<el-dialog v-model="filterdialogVisible" width="800px" :before-close="handleClose">
<el-row :gutter="20">
<el-col :span="4">
<!-- <label>选择筛选条件</label> -->
<div style="height: 100%; padding-top: 10px" class="center-justify">时间</div>
</el-col>
<el-col :span="10">
<div><label>开始时间/结束时间</label></div>
<el-date-picker
v-model="createTimes"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择查询时间"
size="large"
style="width: 600px; margin-top: 10px"
/>
</el-col>
</el-row>
<el-row
v-for="(field, index) in fields"
:key="index"
:gutter="20"
style="margin-bottom: 10px"
>
<el-col :span="4">
<div style="height: 100%" class="center-justify">
{{ field.label }}
</div>
</el-col>
<el-col :span="10">
<div><label>最小值</label></div>
<el-input
type="number"
:oninput="'if(value.length>9)value=value.slice(0,9)'"
v-model.number="searchForm[field.minModel]"
placeholder="请输入最小值"
/>
</el-col>
<el-col :span="10">
<div><label>最大值</label></div>
<el-input
type="number"
:oninput="'if(value.length>9)value=value.slice(0,9)'"
v-model.number="searchForm[field.maxModel]"
placeholder="请输入最大值"
/>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="4">
<!-- <label>选择筛选条件</label> -->
<div style="height: 100%; padding-top: 10px" class="center-justify">排序</div>
</el-col>
<el-col :span="10">
<div><label>排序类型</label></div>
<el-select
v-model="sortData.sortName"
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>升序/降序</label></div>
<el-select
v-model="sortData.sort"
filterable
placeholder="请选择"
style="width: 240px"
>
<el-option
v-for="item in [
{ label: '升序', value: 'asc' },
{ label: '降序', 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"> 重置 </el-button>
<!-- <el-button @click="filterdialogVisible = false">取消</el-button> -->
<el-button type="primary" @click="handelClick"> 确认 </el-button>
</span>
</template>
</el-dialog>
</div>
</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,onBeforeUnmount } from "vue";
import EChartsComponent from "@/components/EChartsComponent.vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { color } from "echarts";
import { getCountryName } from "@/utils/countryUtil";
import { ElLoading } from "element-plus";
//ip国家
let countryData = ref("");
//获取屏幕宽度
const screenWidth = ref(window.innerWidth/10);
//获取国家
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);
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',
// })
// })
}
};
//打开TikTok登录
function openTikTok() {
loginTikTok();
}
//重置
function Resetss() {
queryFormData.value = {};
}
//大哥climb
const queryFormData = ref({
coinMin: "",
coinMax: "",
levelMin: "",
levelMax: "",
isRunning: true,
});
//时间
const timerId = ref(null);
const getBrotherInfodata = ref({
total: 0,
valid: 0,
});
function BigBrotherstop() {
queryFormData.value.tenantId = userInfo.value.tenantId;
queryFormData.value.region = countryData.value;
controlTask(JSON.stringify(queryFormData.value)).then((res) => {
queryFormData.value.isRunning = true;
clearInterval(timerId.value);
timerId.value = null;
});
}
function getBigBrother() {
const loading = ElLoading.service({
lock: true,
text: "正在启动...",
background: "rgba(0, 0, 0, 0.7)",
});
queryFormData.value.tenantId = userInfo.value.tenantId;
queryFormData.value.region = countryData.value;
controlTask(JSON.stringify(queryFormData.value)).then((res) => {
queryFormData.value.isRunning = false;
timerId.value = setInterval(() => {
getBrotherInfo().then((res) => {
loading.close();
getBrotherInfodata.value = res;
});
}, 1000);
});
}
const loading = ref(false);
//py方法
const {
givePyAnchorId,
exportToExcel,
loginTikTok,
controlTask,
getBrotherInfo,
} = usePythonBridge();
let num = ref(0);
//账号信息
const userInfo = ref({});
//主播列表DOM
const multipleTableRef = ref(null);
let labelList = ref([
{ paramCode: "userIdStr", paramCodeMeaning: "用户id" },
{ paramCode: "level", paramCodeMeaning: "等级" },
{ paramCode: "hostcoins", paramCodeMeaning: "打赏的金币" },
{ paramCode: "hostDisplayId", paramCodeMeaning: "所在直播间主播id" },
{ paramCode: "region", paramCodeMeaning: "地区" },
{ paramCode: "followerCount", paramCodeMeaning: "粉丝数" },
{ paramCode: "followingCount", paramCodeMeaning: "关注数" },
{ paramCode: "createTime", paramCodeMeaning: "创建时间" },
]);
const tableData = ref([]);
//主播列表传参
const searchForm = ref({});
const createTimes = ref([]);
//分页
const page = ref(1);
const pageSize = ref(10);
const fields = [
// { label: "打赏的金币", minModel: "hostcoinsMin", maxModel: "hostcoinsMax" },
{ label: "等级", minModel: "levelMin", maxModel: "levelMax" },
];
//排序
let sortData = ref({ sortName: "createTime", sort: "desc" });
//排序类型
let sortNameOptions = ref([
{ label: "创建时间", type: "createTime" },
{ label: "打赏的金币", type: "hostsCoins" },
{ label: "等级", type: "level" },
]);
//员工选择列表
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 total = ref(0);
//是否渲染
const isPopoverVisible = reactive({});
let options = ref([]);
let version = ref("0.0.0");
onMounted(() => {
window.addEventListener('resize', handleResize)
setTimeout(() => {
getUserdata();
}, 500);
});
function handleResize() {
screenWidth.value = window.innerWidth/10;
}
async function getUserdata() {
const User = await getUser();
userInfo.value = User;
getCountry(); //获取国家
getlist(); //获取主播列表
getIpInfo();
}
function serch() {
page.value = 1;
getlist();
}
function exportList() {
// if (searchForm.value.dataType == "InvitationType") {
// searchForm.value.dataEnd = searchForm.value.dataStart;
// }
exportToExcel(requestParams.value);
// //浏览器导出方法
// 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)
}
//时间格式化
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从 0开始需要+1
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
//获取主播列表
//记录请求参数
let requestParams = ref({});
const getlist = () => {
loading.value = true;
tkhostdata({
tenantId: Number(userInfo.value.tenantId),
sort: sortData.value.sort, //正序倒序
sortName: sortData.value.sortName, //排序类型
current: page.value,
pageSize: pageSize.value,
createTimeStart: createTimes.value[0],
createTimeEnd: createTimes.value[1],
...searchForm.value, //筛选条件
}).then((res) => {
loading.value = false;
console.log(res);
if (res) {
requestParams.value = res.records;
console.log(res.records);
total.value = Number(res.total);
tableData.value = res.records.map((item) => ({
level: item.level, // 等级
createTime: formatDate(new Date(item.createTime)), // 创建时间
followerCount: item.followerCount, // 粉丝数
followingCount: item.followingCount, // 关注数
hostDisplayId: item.hostDisplayId, // 所在直播间主播id
hostcoins: item.hostcoins, // 打赏的金币
region: item.region, // 地区
totalGiftCoins: item.totalGiftCoins, // 打赏金币总和
userIdStr: item.userIdStr, // 用户id
displayId: item.displayId,
}));
}
});
};
function handelClick() {
filterdialogVisible.value = false;
}
function reset() {
searchForm.value = {};
sortData.value = { sortName: "createTime", sort: "desc" };
createTimes.value = [];
}
function handleClose(done) {
console.log("关闭");
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("````````国家2`````````", 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) {
console.log(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: 30px 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;
}
}
.buttoMore-filters{
width: 150px;
}
.filters-img{
width: 30px;
}
.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;
}
.right-input {
margin-left: 20px;
}
.right-text{
color: #666;
font-size: 16px;
}
</style>

538
src/views/hosts/pk.vue Normal file
View File

@@ -0,0 +1,538 @@
<template>
<div class="pk">
<!-- -->
<div class="el-scrollbar__wrap">
<el-splitter>
<el-splitter-panel collapsible>
<el-splitter layout="vertical">
<el-splitter-panel collapsible size="15 %" :resizable="false">
<!-- 顶部面板 -->
<div class="demo-panel">
<div class="controlPanel">
<div
class="today"
@click="PKistodayorhall = true"
:style="{
boxShadow: PKistodayorhall
? '0px 0px 10px rgba(0, 0, 0, 0.4)'
: 'none',
color: PKistodayorhall ? '#6c757d' : '',
}"
>
PK大厅
</div>
<div
class="hall"
@click="PKistodayorhall = false"
:style="{
boxShadow: PKistodayorhall
? 'none'
: '0px 0px 10px rgba(0, 0, 0, 0.4)',
color: PKistodayorhall ? '' : '#6c757d',
}"
>
今日PK
</div>
<div class="selectbox" :style="{flexDirection: PKistodayorhall? 'column' : 'row'}">
<!-- 国家 -->
<el-select-v2
v-model="countryvalue"
filterable
:options="country"
placeholder="请选择国家"
style="vertical-align: middle"
class="select"
/>
<!-- 性别 -->
<el-select-v2
v-model="gendervalue"
filterable
:options="genderOptions"
placeholder="请选择性别"
style="vertical-align: middle"
class="select"
:style="{marginLeft: PKistodayorhall? '0px' : '30px'}"
/>
</div>
<!-- 最大最小金币 -->
<div class="Goldcoinbox">
<div class="Goldcoinbox-left">
<div class="Goldcoinbox-text">最小金币数单位为K</div>
<el-input-number
v-model="minnum"
controls-position="right"
@change="handleChange"
/>
</div>
<div class="Goldcoinbox-middle"></div>
<div class="Goldcoinbox-right">
<div class="Goldcoinbox-text">最大金币数单位为K</div>
<el-input-number
v-model="maxnum"
controls-position="right"
@change="handleChange"
/>
</div>
</div>
<!-- 时间 -->
<div class="timebox" v-if="PKistodayorhall">
<el-date-picker
v-model="timevalue"
type="datetimerange"
range-separator=""
start-placeholder="最小PK时间"
end-placeholder="最大PK时间"
/>
</div>
<div class="btnbox" :style="{flexDirection: PKistodayorhall? 'column' : 'row',width: PKistodayorhall? '80px' : '190px'}">
<div
class="primary search"
@click="search"
:style="{
backgroundColor: searchStatus ? '#5a6268' : '',
color: searchStatus ? '#ffffff' : '',
}"
>
搜索
</div>
<div class="primary" @click="reset" :style="{marginLeft: PKistodayorhall? '0px' : '30px'}">重置</div>
</div>
</div>
</div>
</el-splitter-panel>
<el-splitter-panel collapsible>
<el-splitter>
<el-splitter-panel collapsible :resizable="false">
<!-- 列表面板 -->
<div class="demo-panel">
<div
v-infinite-scroll="load"
infinite-scroll-distance="100px"
class="infinite-list"
style="overflow: auto"
>
<div
class="infinite-list-item"
v-for="(item, index) in list"
:key="index"
>
<div class="infinite-card">
<!-- 头像 -->
<div class="Avatar"></div>
<div class="Information">
<!-- 个人信息 -->
<div class="Information-Personal">
<div class="Information-name">
来自世界上最长名的国家的某个人
</div>
<div class="Information-gender"></div>
<div class="Information-Country">
来自世界上最长名的国家的某个人
</div>
</div>
<!-- 时间 -->
<div class="time">PK时间北京时间:2025-08-09 01:00</div>
<!--PK信息 -->
<div class="Information-PK">
<img
class="goldimg"
src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/gold.png"
alt=""
/>
<div class="gold">金币9999k</div>
<img
class="sessionimg"
src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/session.png"
alt=""
/>
<div class="Session">场次100</div>
</div>
<!-- 备注信息 -->
<div class="Information-Remarks">
这是一条备注信息他很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很
</div>
</div>
</div>
</div>
</div>
</div>
</el-splitter-panel>
<el-splitter-panel collapsible size="27%" :resizable="false">
<!-- 聊天面板 -->
<div class="demo-panel">
<div class="chat">
<div class="chat-name">来自世界上最长名的国家的某个人</div>
<div class="chat-content"></div>
<div class="chat-input">
<div class="chat-input-Controls"></div>
<div class="chat-input-Textarea">
<el-input
v-model="textarea"
style="width:100%"
:rows="4"
type="textarea"
placeholder="请输入内容"
/>
</div>
</div>
</div>
</div>
</el-splitter-panel>
</el-splitter>
</el-splitter-panel>
</el-splitter>
</el-splitter-panel>
</el-splitter>
</div>
<!-- -->
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
import { getCountryNamesArray } from "../../utils/countryUtil";
import { time } from "echarts";
const country = ref([]);
country.value = getCountryNamesArray(); //国家条目
const PKistodayorhall = ref(true); // 选择今日PK还是PK大厅/false 今日PK/true PK大厅
const genderOptions = [
{ value: 1, label: "男" },
{ value: 2, label: "女" },
]; // 性别选项
const searchStatus = ref(false); // 搜索状态
const countryvalue = ref(null); //选中的国家
const gendervalue = ref(null); // 选中的性别
const minnum = ref(null); // 最小金币
const maxnum = ref(null); // 最大金币
const timevalue = ref(null); // 时间
const textarea = ref(""); // 聊天输入框
const list = ref([{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]); // 列表数据
//搜索按钮
function search() {
searchStatus.value = true;
}
//重置按钮
function reset() {
searchStatus.value = false;
countryvalue.value = null;
gendervalue.value = null;
minnum.value = null;
maxnum.value = null;
timevalue.value = null;
}
function load() {} // 加载更多
const refname = ref(""); //
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped lang="less">
.pk {
width: 100%;
height: 100%;
// background-color: #ffffff;
}
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.el-scrollbar__wrap {
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.controlPanel {
width: 100%;
height: 100%;
background-image: linear-gradient(
30deg,
@bg-Sidebar-color-bottom,
@bg-Sidebar-color-top
);
display: flex;
align-items: center;
justify-content: space-around;
}
.today {
width: 150px;
height: 50px;
background-color: #ffffff;
border-radius: 50px;
transition: all 0.4s ease;
color: #e9e8e8;
font-size: 20px;
font-weight: bold;
text-align: center;
line-height: 50px;
letter-spacing: 3px;
}
.today:hover {
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.3);
transform: scale(1.1);
opacity: 0.8;
}
.today:active {
transform: scale(1.1) rotate(10deg);
}
.hall {
width: 150px;
height: 50px;
background-color: #ffffff;
border-radius: 50px;
transition: all 0.4s ease;
color: #e9e8e8;
font-size: 20px;
font-weight: bold;
text-align: center;
line-height: 50px;
letter-spacing: 3px;
}
.hall:hover {
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.3);
transform: scale(1.1);
opacity: 0.8;
}
.hall:active {
transform: scale(1.1) rotate(10deg);
}
.selectbox {
display: flex;
// flex-direction: column;
align-items: center;
}
.select {
width: 150px;
margin-top: 10px;
}
.Goldcoinbox {
width: 330px;
display: flex;
align-items: center;
justify-content: space-between;
}
.Goldcoinbox-left {
display: flex;
flex-direction: column;
}
.Goldcoinbox-right {
display: flex;
flex-direction: column;
}
.Goldcoinbox-text {
font-size: 10px;
color: @Prompt-text-color;
}
.Goldcoinbox-middle {
border-bottom: 2px solid #b9b9b9;
width: 5px;
height: 15px;
}
.timebox {
width: 410px;
}
.btnbox {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.primary {
width: 80px;
height: 30px;
background-color: #e9e7e7;
border-radius: 10px;
color: @Supplementary-text-color;
font-size: 12px;
font-weight: bold;
text-align: center;
line-height: 30px;
letter-spacing: 2px;
transition: all 0.4s ease;
}
.search {
margin-bottom: 20px;
}
.primary:hover {
background-color: #5a6268;
color: #ffffff;
transform: scale(1.1);
opacity: 0.8;
}
.infinite-list {
width: 100%;
height: 100%;
background-color: #ffffff;
}
.infinite-list-item {
width: 100%;
height: 170px;
margin-bottom: 20px;
margin-top: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.infinite-card {
width: 90%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
background-image: linear-gradient(0deg, @bg-Sidebar-color-top, @bg-color);
border-radius: 20px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
transition: all 0.4s ease;
}
.infinite-card:hover {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.8);
transform: scale(1.1);
opacity: 0.8;
}
.Avatar {
width: 100px;
height: 100px;
border-radius: 50%;
background-color: #ececec;
margin-left: 30px;
}
.Information {
width: calc(100% - 160px);
height: 90%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.Information-Personal {
display: flex;
align-items: center;
}
.Information-name {
width: auto;
font-size: 20px;
font-weight: bold;
color: @font-color;
}
.Information-gender {
width: 50px;
height: 20px;
background-color: #e9e7e7;
border-radius: 10px;
color: @Prompt-text-color;
font-size: 12px;
font-weight: bold;
text-align: center;
line-height: 20px;
letter-spacing: 2px;
margin-left: 20px;
}
.Information-Country {
width: auto;
height: 20px;
background-color: #e9e7e7;
border-radius: 10px;
color: @Prompt-text-color;
font-size: 12px;
font-weight: bold;
text-align: center;
line-height: 20px;
letter-spacing: 2px;
padding-left: 5px;
padding-right: 5px;
margin-left: 20px;
}
.time {
font-size: 16px;
color: @Prompt-text-color;
}
.Information-PK {
display: flex;
align-items: center;
}
.goldimg {
width: 20px;
height: 20px;
margin-right: 10px;
}
.gold {
font-size: 16px;
color: @font-color;
}
.sessionimg {
width: 20px;
height: 20px;
margin-left: 50px;
margin-right: 10px;
}
.Session {
font-size: 16px;
color: @font-color;
}
.Information-Remarks {
font-size: 14px;
color: @Prompt-text-color;
}
.chat {
width: 100%;
height: 100%;
}
.chat-name {
width: 100%;
height: 50px;
background-color: #ffffff;
// border-bottom: 3px solid #b9b9b9;
color: @Prompt-text-color;
font-size: 16px;
font-weight: bold;
text-align: center;
line-height: 50px;
letter-spacing: 3px;
white-space: nowrap; /* 单行显示 */
overflow: hidden;
text-overflow: ellipsis; /* 显示省略号 */
}
.chat-content {
width: 100%;
height: calc(100% - 200px);
}
.chat-input {
width: 100%;
height: 150px;
// border-top: 3px solid #b9b9b9;
}
.chat-input-Controls {
width: 100%;
height: 50px;
background-color: @border-color;
}
.chat-input-Textarea {
width: 100%;
height: 80px;
// background-color: #ffffff;
}
</style>

View File

@@ -1,742 +0,0 @@
<template>
<div class="center-line workbenches">
<div class="center-align" style="width: 100%; margin: 0 20px;">
<div class="box-card-num1 center-line">
<div>总数量: <span>{{ hostData.totalCount }}</span></div>
<div>新建主播: <span>{{ hostData.validAnchorsCount }}</span></div>
<div> 已查询: <span>{{ hostData.checkedDataCount }}</span></div>
<div>可邀请: <span>{{ hostData.canInvitationCount }}</span></div>
<div>运行时间: <span>{{ formattedTime }}</span></div>
</div>
<div class="center-line" style="padding-top: 15vh;">
<el-button class="open-login" type="primary" @click="openTK">开启tk</el-button>
<!-- <el-button class="open-login" type="primary" @click="startTimer">计时开始</el-button> -->
</div>
<div>
<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">
公会账号
</div>
<el-input :disabled="!(tkData[index].code == 0 && !isLogin[index])"
v-model="tkData[index].account" placeholder="请输入登录账号" clearable />
</div>
<div class="from-input-item">
<div class="from-input-item-title center-justify">
公会密码
</div>
<el-input :disabled="!(tkData[index].code == 0 && !isLogin[index])"
v-model="tkData[index].password" type="password" placeholder="请输入登录密码" show-password />
</div>
<el-button class="open-login" style="margin-left: 60px;"
:disabled="!(tkData[index].code == 0 && !isLogin[index])" type="primary"
@click="loginTK(index)">登录后台</el-button>
<div v-if="tkData[index].code == 0" class="loginState"></div>
<div v-if="tkData[index].code == 1" class="loginState" style="background-color: green;"></div>
</div>
<div class="todayCount">今日已查询次数{{ 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">工作台 </span>
<div style="margin-right: 120px;">当前网络:{{ countryData }}
<!-- <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>设置金币数量</label>
<el-input type='number' v-model="pyData.gold.min" :min="0" :max="pyData.gold.max - 1"
placeholder="最小值" style="width: 100%" :disabled="!pyData.isStart">
<template #prepend>最小金币数</template>
</el-input>
<el-input type='number' v-model="pyData.gold.max" :min="pyData.gold.min + 1" :max="100"
placeholder="最大值" style="width: 100%; margin-top: 10px" :disabled="!pyData.isStart">
<template #prepend>最大金币数</template>
</el-input>
</div>
</el-col>
<el-col :span="8">
<div class="input-group">
<label>设置粉丝数量</label>
<el-input type='number' v-model="pyData.fans.min" :min="0" :max="pyData.fans.max - 1"
placeholder="最小值" style="width: 100%" :disabled="!pyData.isStart">
<template #prepend>最小粉丝数</template>
</el-input>
<el-input type='number' v-model="pyData.fans.max" :min="pyData.fans.min + 1" :max="100"
placeholder="最大值" style="width: 100%; margin-top: 10px" :disabled="!pyData.isStart">
<template #prepend>最大粉丝数</template>
</el-input>
</div>
</el-col>
<el-col :span="8">
<div class="input-group">
<label>后台查询频率</label>
<!-- <el-input type='number' v-model="pyData.frequency.hour" @input="handleInputHour" -->
<el-input type='number' v-model="pyData.frequency.hour" placeholder="次/小时"
style="width: 100%" :disabled="!pyData.isStart">
<template #append>/小时</template>
</el-input>
<!-- <el-input type='number' v-model="pyData.frequency.day" @input="handleInputDay" -->
<el-input type='number' v-model="pyData.frequency.day" placeholder="次/24小时"
style="width: 100%; margin-top: 10px" :disabled="!pyData.isStart">
<template #append>/24小时</template>
</el-input>
</div>
</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">开始获取数据</el-button>
<el-button v-show="!pyData.isStart" type="danger" @click="unsubmit">停止获取</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'
//导入python交互方法
const { fetchDataConfig, fetchDataCount, loginBackStage, loginTikTok, backStageloginStatus, backStageloginStatusCopy } = usePythonBridge();
//ip国家
let countryData = ref('');
//获取主播数量的定时器
let getHostTimer = 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);
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);
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;
// 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>

View File

@@ -1,40 +1,20 @@
<template>
<div class="app-container">
<!-- <Sidebar class="noneText" @activeIndex="activeIndexFn" /> -->
<div class="content ">
<!-- <div v-show="activeIndexA == 1">
<workbenches />
</div> -->
<div>
<hostsList />
</div>
<!-- <div style="position: absolute; bottom: 0; right: 0;">{{ version }}</div> -->
</div>
<div class="common-layout">
<el-container>
<!-- 左侧导航栏 -->
<el-aside class="nav-aside">
<Appaside></Appaside>
</el-aside>
<!-- 右侧主体内容 -->
<el-main class="nav-main">
<router-view />
</el-main>
</el-container>
</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)
// }
import Appaside from "/src/components/Appaside.vue";
</script>
@@ -44,48 +24,22 @@ body,
html {
margin: 0;
padding: 0;
height: 100%;
height: 100vh;
width: 100vw;
}
.app-container {
display: flex;
width: 1600px;
height: 900px;
background-color: @bg-color;
position: relative;
.common-layout {
width: 100vw;
height: 100vh;
}
.noneText {
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.nav-main {
width: 95vw;
height: 100vh;
background-color:@bg-color;
}
.sidebar {
width: 200px;
background-color: @bg-color;
padding: 20px;
/* box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); */
}
.content {
// margin-left: 280px;
margin-left: 25px;
margin-right: 25px;
width: 1540px;
height: 868px;
background: #FFFFFF;
border-radius: 36px;
margin-top: 16px;
}
.center-justify {
display: flex;
justify-content: space-around;
align-items: center;
.nav-aside {
width: 5vw;
height: 100vh;
background-image: linear-gradient(0deg, @bg-Sidebar-color-top, @bg-color);
border: 2px solid @border-color;
}
</style>