Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ccd7759669 |
@@ -76,6 +76,9 @@ export default {
|
|||||||
specifyStart: 'Start',
|
specifyStart: 'Start',
|
||||||
enterRoomIds: 'Enter room IDs, separate multiple IDs with Enter key',
|
enterRoomIds: 'Enter room IDs, separate multiple IDs with Enter key',
|
||||||
enterRoomId: 'Please enter room ID',
|
enterRoomId: 'Please enter room ID',
|
||||||
|
currentCount: 'Current',
|
||||||
|
countSeparator: '/',
|
||||||
|
countUnit: 'items',
|
||||||
|
|
||||||
// 网络问题弹窗
|
// 网络问题弹窗
|
||||||
networkFailed:
|
networkFailed:
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ export default {
|
|||||||
specifyStart: '开始',
|
specifyStart: '开始',
|
||||||
networkFailed: '网络连接失败,无法访问网络,请查看网络设置。',
|
networkFailed: '网络连接失败,无法访问网络,请查看网络设置。',
|
||||||
enterRoomIds: '请输入直播间id,多个id用回车键隔开',
|
enterRoomIds: '请输入直播间id,多个id用回车键隔开',
|
||||||
|
currentCount: '当前',
|
||||||
|
countSeparator: '条 / 最大',
|
||||||
|
countUnit: '条',
|
||||||
|
|
||||||
// ==== 新增:表格列、排序使用 ====
|
// ==== 新增:表格列、排序使用 ====
|
||||||
userId: '用户id',
|
userId: '用户id',
|
||||||
|
|||||||
@@ -69,15 +69,13 @@
|
|||||||
// 玻璃态组件 Glass Components
|
// 玻璃态组件 Glass Components
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
// 玻璃态头部卡片
|
// 玻璃态头部卡片 - 简化版(移除 backdrop-filter 提升性能)
|
||||||
.glass-header {
|
.glass-header {
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: #ffffff;
|
||||||
backdrop-filter: blur(12px);
|
border: 1px solid #e8ecf0;
|
||||||
-webkit-backdrop-filter: blur(12px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.9);
|
|
||||||
border-radius: @radius-3xl;
|
border-radius: @radius-3xl;
|
||||||
padding: 28px 32px;
|
padding: 28px 32px;
|
||||||
box-shadow: @shadow-glass;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 玻璃态内容卡片
|
// 玻璃态内容卡片
|
||||||
@@ -111,20 +109,11 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.05);
|
||||||
box-shadow: @shadow-cyber-glow;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
transform: translateY(0);
|
opacity: 0.9;
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: none;
|
|
||||||
transform: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,12 +136,6 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: @tech-gray-50;
|
background: @tech-gray-50;
|
||||||
border-color: @tech-gray-300;
|
border-color: @tech-gray-300;
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,9 +456,7 @@
|
|||||||
padding: 0 24px !important;
|
padding: 0 24px !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.05);
|
||||||
box-shadow: @shadow-cyber-glow !important;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
@@ -505,7 +486,6 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: @tech-gray-50 !important;
|
background: @tech-gray-50 !important;
|
||||||
border-color: @tech-gray-300 !important;
|
border-color: @tech-gray-300 !important;
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/utils/tenantConfig.js
Normal file
26
src/utils/tenantConfig.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* 租户配置管理
|
||||||
|
* 用于统一管理特定租户的差异化配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 直播间ID限制配置
|
||||||
|
// key: tenantId (租户ID)
|
||||||
|
// value: limit (最大行数限制)
|
||||||
|
export const TENANT_LIMIT_CONFIG = {
|
||||||
|
'12741': 5000,
|
||||||
|
'12348': 5000,
|
||||||
|
// 在此处添加更多租户ID和对应的限制
|
||||||
|
// '10001': 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_LIMIT = 50
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取租户的直播间ID限制数量
|
||||||
|
* @param {string|number} tenantId 租户ID
|
||||||
|
* @returns {number} 限制数量
|
||||||
|
*/
|
||||||
|
export const getTenantLimit = (tenantId) => {
|
||||||
|
if (!tenantId) return DEFAULT_LIMIT
|
||||||
|
return TENANT_LIMIT_CONFIG[String(tenantId)] || DEFAULT_LIMIT
|
||||||
|
}
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="home">
|
|
||||||
<!-- <div id="main" class="main"></div> -->
|
|
||||||
<div class="noData">
|
|
||||||
暂无数据
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
// <script setup>
|
|
||||||
// import { ref, watch, onMounted, onUpdated, onUnmounted } from "vue";
|
|
||||||
// const refname = ref('');
|
|
||||||
|
|
||||||
// const echarts = require('echarts/lib/echarts');
|
|
||||||
// require('echarts/lib/component/grid');
|
|
||||||
// require('echarts/lib/chart/bar');
|
|
||||||
|
|
||||||
// const initChart = () => {
|
|
||||||
// var chartDom = document.getElementById('main');
|
|
||||||
// var myChart = echarts.init(chartDom);
|
|
||||||
// var option = {
|
|
||||||
// xAxis: {
|
|
||||||
// type: 'category',
|
|
||||||
// data: ['用户量', '123', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
||||||
// },
|
|
||||||
// yAxis: {
|
|
||||||
// type: 'value'
|
|
||||||
// },
|
|
||||||
// series: [
|
|
||||||
// {
|
|
||||||
// data: [120, 200, 150, 80, 70, 110, 130],
|
|
||||||
// type: 'bar'
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// };
|
|
||||||
// myChart.setOption(option);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// watch(refname, (newQuestion) => {
|
|
||||||
// // 变化后执行
|
|
||||||
// // 如果 refname 的变化影响图表数据,可以在这里更新 option 并调用 myChart.setOption(option)
|
|
||||||
// });
|
|
||||||
|
|
||||||
// onMounted(() => {
|
|
||||||
// initChart();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// onUpdated(() => {
|
|
||||||
// // 组件更新后执行
|
|
||||||
// // 如果需要根据更新重新初始化图表或更新数据,可以在这里添加逻辑
|
|
||||||
// });
|
|
||||||
|
|
||||||
// onUnmounted(() => {
|
|
||||||
// // 组件销毁前执行
|
|
||||||
// // 如果需要销毁图表实例以释放资源,可以在这里添加逻辑
|
|
||||||
// });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.home {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.noData{
|
|
||||||
font-size: 20px;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.main{
|
|
||||||
width: 40%;
|
|
||||||
height: 40%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -6,12 +6,8 @@
|
|||||||
<div class="filter-row primary-filters">
|
<div class="filter-row primary-filters">
|
||||||
<!-- 左侧筛选控件 -->
|
<!-- 左侧筛选控件 -->
|
||||||
<div class="filter-group flex-start gap-4">
|
<div class="filter-group flex-start gap-4">
|
||||||
<el-checkbox
|
<el-checkbox class="cyber-checkbox" v-model="queryFormData.isFilter"
|
||||||
class="cyber-checkbox"
|
:label="t('hostsList.filterPrivateUsers')" size="large" />
|
||||||
v-model="queryFormData.isFilter"
|
|
||||||
:label="t('hostsList.filterPrivateUsers')"
|
|
||||||
size="large"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
@@ -19,21 +15,11 @@
|
|||||||
{{ t('hostsList.coins') }}
|
{{ t('hostsList.coins') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-pair">
|
<div class="input-pair">
|
||||||
<el-input
|
<el-input v-model="queryFormData.coinMin" :placeholder="t('hostsList.minCoins')" size="large"
|
||||||
v-model="queryFormData.coinMin"
|
type="number" :disabled="streamdialogVisibletext || isRunnings" />
|
||||||
:placeholder="t('hostsList.minCoins')"
|
|
||||||
size="large"
|
|
||||||
type="number"
|
|
||||||
:disabled="streamdialogVisibletext || isRunnings"
|
|
||||||
/>
|
|
||||||
<span class="input-divider">/</span>
|
<span class="input-divider">/</span>
|
||||||
<el-input
|
<el-input v-model="queryFormData.coinMax" :placeholder="t('hostsList.maxCoins')" size="large"
|
||||||
v-model="queryFormData.coinMax"
|
type="number" :disabled="streamdialogVisibletext || isRunnings" />
|
||||||
:placeholder="t('hostsList.maxCoins')"
|
|
||||||
size="large"
|
|
||||||
type="number"
|
|
||||||
:disabled="streamdialogVisibletext || isRunnings"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -43,33 +29,32 @@
|
|||||||
{{ t('hostsList.level') }}
|
{{ t('hostsList.level') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-pair">
|
<div class="input-pair">
|
||||||
<el-input
|
<el-input v-model="queryFormData.levelMin" :placeholder="t('hostsList.minLevel')" size="large"
|
||||||
v-model="queryFormData.levelMin"
|
type="number" :disabled="streamdialogVisibletext || isRunnings" />
|
||||||
:placeholder="t('hostsList.minLevel')"
|
|
||||||
size="large"
|
|
||||||
type="number"
|
|
||||||
:disabled="streamdialogVisibletext || isRunnings"
|
|
||||||
/>
|
|
||||||
<span class="input-divider">/</span>
|
<span class="input-divider">/</span>
|
||||||
<el-input
|
<el-input v-model="queryFormData.levelMax" :placeholder="t('hostsList.maxLevel')" size="large"
|
||||||
v-model="queryFormData.levelMax"
|
type="number" :disabled="streamdialogVisibletext || isRunnings" />
|
||||||
:placeholder="t('hostsList.maxLevel')"
|
|
||||||
size="large"
|
|
||||||
type="number"
|
|
||||||
:disabled="streamdialogVisibletext || isRunnings"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="info-pill" style="margin-top: 15px;">
|
||||||
|
<span class="info-label" style="font-size: 16px">{{ t('hostsList.runningTime') }}:</span>
|
||||||
|
<span class="info-value mono" style="font-size: 16px">
|
||||||
|
{{ String(hourstuo).padStart(2, '0') }}:{{
|
||||||
|
String(minutestuo).padStart(2, '0')
|
||||||
|
}}:{{ String(secondstuo).padStart(2, '0') }}
|
||||||
|
</span>
|
||||||
|
<span class="info-divider"></span>
|
||||||
|
<span class="info-label" style="font-size: 16px">{{ t('hostsList.total') }}:</span>
|
||||||
|
<span class="info-value" style="font-size: 16px">{{ getBrotherInfodata.total }}</span>
|
||||||
|
<span class="info-divider"></span>
|
||||||
|
<span class="info-label" style="font-size: 16px">{{ t('hostsList.valid') }}:</span>
|
||||||
|
<span class="info-value primary" style="font-size: 16px">{{ getBrotherInfodata.valid }}</span>
|
||||||
|
</div>
|
||||||
<!-- 右侧操作按钮 -->
|
<!-- 右侧操作按钮 -->
|
||||||
<div class="action-group flex-start gap-3">
|
<div class="action-group flex-start gap-3">
|
||||||
<el-button
|
<el-button @click="streamdialogVisible = true" :disabled="isRunnings" class="cyber-btn-primary"
|
||||||
@click="streamdialogVisible = true"
|
type="primary">
|
||||||
:disabled="isRunnings"
|
|
||||||
class="cyber-btn-primary"
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
<span class="btn-icon">📍</span>
|
<span class="btn-icon">📍</span>
|
||||||
{{
|
{{
|
||||||
streamdialogVisibletext
|
streamdialogVisibletext
|
||||||
@@ -78,21 +63,11 @@
|
|||||||
}}
|
}}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button v-show="queryFormData.isRunning" class="cyber-btn-success" type="primary" @click="getBigBrother">
|
||||||
v-show="queryFormData.isRunning"
|
|
||||||
class="cyber-btn-success"
|
|
||||||
type="primary"
|
|
||||||
@click="getBigBrother"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.start') }}
|
{{ t('hostsList.start') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button v-show="!queryFormData.isRunning" class="cyber-btn-danger" type="primary" @click="BigBrotherstop">
|
||||||
v-show="!queryFormData.isRunning"
|
|
||||||
class="cyber-btn-danger"
|
|
||||||
type="primary"
|
|
||||||
@click="BigBrotherstop"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.end') }}
|
{{ t('hostsList.end') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,28 +79,13 @@
|
|||||||
<!-- 第二行:搜索和操作 -->
|
<!-- 第二行:搜索和操作 -->
|
||||||
<div class="filter-row secondary-filters">
|
<div class="filter-row secondary-filters">
|
||||||
<div class="filter-group flex-start gap-4">
|
<div class="filter-group flex-start gap-4">
|
||||||
<el-select
|
<el-select v-model="searchForm.region" filterable :placeholder="t('hostsList.selectCountry')" size="large"
|
||||||
v-model="searchForm.region"
|
class="cyber-select">
|
||||||
filterable
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
:placeholder="t('hostsList.selectCountry')"
|
|
||||||
size="large"
|
|
||||||
class="cyber-select"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in options"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
<el-input
|
<el-input v-model="searchForm.displayId" :placeholder="t('hostsList.bigBrotherId')" size="large"
|
||||||
v-model="searchForm.displayId"
|
class="cyber-input-search" clearable>
|
||||||
:placeholder="t('hostsList.bigBrotherId')"
|
|
||||||
size="large"
|
|
||||||
class="cyber-input-search"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<span class="input-prefix-icon">@</span>
|
<span class="input-prefix-icon">@</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -141,27 +101,17 @@
|
|||||||
{{ t('hostsList.reset') }}
|
{{ t('hostsList.reset') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button class="ghost-btn" :disabled="tableData.length == 0" @click="exportList">
|
||||||
class="ghost-btn"
|
|
||||||
:disabled="tableData.length == 0"
|
|
||||||
@click="exportList"
|
|
||||||
>
|
|
||||||
<span class="btn-icon">📥</span>
|
<span class="btn-icon">📥</span>
|
||||||
{{ t('hostsList.exportExcel') }}
|
{{ t('hostsList.exportExcel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button @click="filterdialogVisible = true" class="ghost-btn">
|
||||||
@click="filterdialogVisible = true"
|
|
||||||
class="ghost-btn"
|
|
||||||
>
|
|
||||||
<span class="btn-icon">⚙️</span>
|
<span class="btn-icon">⚙️</span>
|
||||||
{{ t('hostsList.moreFilters') }}
|
{{ t('hostsList.moreFilters') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button class="ghost-btn" @click="openTikTok">
|
||||||
class="ghost-btn"
|
|
||||||
@click="openTikTok"
|
|
||||||
>
|
|
||||||
<span class="btn-icon">🎵</span>
|
<span class="btn-icon">🎵</span>
|
||||||
{{ t('hostsList.openTikTok') }}
|
{{ t('hostsList.openTikTok') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -172,20 +122,10 @@
|
|||||||
<div class="info-pill">
|
<div class="info-pill">
|
||||||
<span class="info-label">{{ t('hostsList.currentNetwork') }}:</span>
|
<span class="info-label">{{ t('hostsList.currentNetwork') }}:</span>
|
||||||
<span class="info-value highlight">{{ countryData }}</span>
|
<span class="info-value highlight">{{ countryData }}</span>
|
||||||
<span class="info-divider"></span>
|
|
||||||
<span class="info-label">{{ t('hostsList.runningTime') }}:</span>
|
|
||||||
<span class="info-value mono">
|
|
||||||
{{ String(hourstuo).padStart(2, '0') }}:{{
|
|
||||||
String(minutestuo).padStart(2, '0')
|
|
||||||
}}:{{ String(secondstuo).padStart(2, '0') }}
|
|
||||||
</span>
|
|
||||||
<span class="info-divider"></span>
|
|
||||||
<span class="info-label">{{ t('hostsList.total') }}:</span>
|
|
||||||
<span class="info-value">{{ getBrotherInfodata.total }}</span>
|
|
||||||
<span class="info-divider"></span>
|
|
||||||
<span class="info-label">{{ t('hostsList.valid') }}:</span>
|
|
||||||
<span class="info-value primary">{{ getBrotherInfodata.valid }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -193,57 +133,27 @@
|
|||||||
<!-- 表格区域 - 卡片设计 -->
|
<!-- 表格区域 - 卡片设计 -->
|
||||||
<div class="table-container glass-card cyber-table">
|
<div class="table-container glass-card cyber-table">
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<el-table
|
<el-table ref="multipleTableRef" :data="tableData" stripe v-loading="loading" height="100%"
|
||||||
ref="multipleTableRef"
|
@cell-dblclick="handleCellDbClick" @selection-change="handleSelectionChange">
|
||||||
:data="tableData"
|
<el-table-column fixed prop="displayId" :label="t('hostsList.id')" min-width="120">
|
||||||
stripe
|
|
||||||
v-loading="loading"
|
|
||||||
height="100%"
|
|
||||||
@cell-dblclick="handleCellDbClick"
|
|
||||||
@selection-change="handleSelectionChange"
|
|
||||||
>
|
|
||||||
<el-table-column
|
|
||||||
fixed
|
|
||||||
prop="displayId"
|
|
||||||
:label="t('hostsList.id')"
|
|
||||||
min-width="120"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div
|
<div class="link-text" @click="openHTML(scope.row.displayId)">
|
||||||
class="link-text"
|
|
||||||
@click="openHTML(scope.row.displayId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.displayId }}
|
{{ scope.row.displayId }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column prop="hostDisplayId" :label="t('hostsList.hostId')" min-width="120">
|
||||||
prop="hostDisplayId"
|
|
||||||
:label="t('hostsList.hostId')"
|
|
||||||
min-width="120"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div
|
<div class="link-text" @click.ctrl.exact="handleLongPress(scope.row.hostDisplayId)">
|
||||||
class="link-text"
|
|
||||||
@click.ctrl.exact="handleLongPress(scope.row.hostDisplayId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.hostDisplayId }}
|
{{ scope.row.hostDisplayId }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column v-for="label in labelList" :key="label.paramCode" :prop="label.paramCode"
|
||||||
v-for="label in labelList"
|
:label="label.paramCodeMeaning" min-width="100">
|
||||||
:key="label.paramCode"
|
<template v-if="label.paramCode != 'createDt'" #default="scope">
|
||||||
:prop="label.paramCode"
|
|
||||||
:label="label.paramCodeMeaning"
|
|
||||||
min-width="100"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-if="label.paramCode != 'createDt'"
|
|
||||||
#default="scope"
|
|
||||||
>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -257,32 +167,17 @@
|
|||||||
{{ t('hostsList.reset') }}
|
{{ t('hostsList.reset') }}
|
||||||
</el-button> -->
|
</el-button> -->
|
||||||
</div>
|
</div>
|
||||||
<el-pagination
|
<el-pagination v-model:current-page="page" v-model:page-size="pageSize" background
|
||||||
v-model:current-page="page"
|
layout="sizes, prev, pager, next" :total="total" :page-sizes="[10, 20, 50, 100, 500, 1000]"
|
||||||
v-model:page-size="pageSize"
|
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||||
background
|
|
||||||
layout="sizes, prev, pager, next"
|
|
||||||
:total="total"
|
|
||||||
:page-sizes="[10, 20, 50, 100, 500, 1000]"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 更多筛选弹窗 -->
|
<!-- 更多筛选弹窗 -->
|
||||||
<el-dialog
|
<el-dialog v-model="filterdialogVisible" width="800px" :before-close="handleClose" class="cyber-dialog">
|
||||||
v-model="filterdialogVisible"
|
|
||||||
width="800px"
|
|
||||||
:before-close="handleClose"
|
|
||||||
class="cyber-dialog"
|
|
||||||
>
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<div
|
<div style="height: 100%; padding-top: 10px" class="center-justify">
|
||||||
style="height: 100%; padding-top: 10px"
|
|
||||||
class="center-justify"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.time') }}
|
{{ t('hostsList.time') }}
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -293,23 +188,12 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-date-picker
|
<el-date-picker v-model="createTimes" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
v-model="createTimes"
|
:placeholder="t('hostsList.selectTime')" size="large" style="width: 600px; margin-top: 10px" />
|
||||||
type="datetimerange"
|
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
:placeholder="t('hostsList.selectTime')"
|
|
||||||
size="large"
|
|
||||||
style="width: 600px; margin-top: 10px"
|
|
||||||
/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row
|
<el-row v-for="(field, index) in fields" :key="index" :gutter="20" style="margin-bottom: 10px">
|
||||||
v-for="(field, index) in fields"
|
|
||||||
:key="index"
|
|
||||||
:gutter="20"
|
|
||||||
style="margin-bottom: 10px"
|
|
||||||
>
|
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<div style="height: 100%" class="center-justify">
|
<div style="height: 100%" class="center-justify">
|
||||||
{{ field.label }}
|
{{ field.label }}
|
||||||
@@ -319,32 +203,21 @@
|
|||||||
<div>
|
<div>
|
||||||
<label>{{ t('hostsList.minValue') }}</label>
|
<label>{{ t('hostsList.minValue') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<el-input
|
<el-input type="number" :oninput="'if(value.length>9)value=value.slice(0,9)'"
|
||||||
type="number"
|
v-model.number="searchForm[field.minModel]" :placeholder="t('hostsList.enterMinValue')" />
|
||||||
:oninput="'if(value.length>9)value=value.slice(0,9)'"
|
|
||||||
v-model.number="searchForm[field.minModel]"
|
|
||||||
:placeholder="t('hostsList.enterMinValue')"
|
|
||||||
/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
<div>
|
<div>
|
||||||
<label>{{ t('hostsList.maxValue') }}</label>
|
<label>{{ t('hostsList.maxValue') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<el-input
|
<el-input type="number" :oninput="'if(value.length>9)value=value.slice(0,9)'"
|
||||||
type="number"
|
v-model.number="searchForm[field.maxModel]" :placeholder="t('hostsList.enterMaxValue')" />
|
||||||
:oninput="'if(value.length>9)value=value.slice(0,9)'"
|
|
||||||
v-model.number="searchForm[field.maxModel]"
|
|
||||||
:placeholder="t('hostsList.enterMaxValue')"
|
|
||||||
/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<div
|
<div style="height: 100%; padding-top: 10px" class="center-justify">
|
||||||
style="height: 100%; padding-top: 10px"
|
|
||||||
class="center-justify"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.sort') }}
|
{{ t('hostsList.sort') }}
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -353,18 +226,9 @@
|
|||||||
<label>{{ t('hostsList.sortType') }}</label>
|
<label>{{ t('hostsList.sortType') }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-select
|
<el-select v-model="sortData.sortName" filterable :placeholder="t('hostsList.pleaseSelect')"
|
||||||
v-model="sortData.sortName"
|
style="width: 240px">
|
||||||
filterable
|
<el-option v-for="item in sortNameOptions" :key="item.type" :label="item.label" :value="item.type" />
|
||||||
:placeholder="t('hostsList.pleaseSelect')"
|
|
||||||
style="width: 240px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in sortNameOptions"
|
|
||||||
:key="item.type"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.type"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
@@ -372,21 +236,11 @@
|
|||||||
<label>{{ t('hostsList.sortOrder') }}</label>
|
<label>{{ t('hostsList.sortOrder') }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-select
|
<el-select v-model="sortData.sort" filterable :placeholder="t('hostsList.pleaseSelect')" style="width: 240px">
|
||||||
v-model="sortData.sort"
|
<el-option v-for="item in [
|
||||||
filterable
|
|
||||||
:placeholder="t('hostsList.pleaseSelect')"
|
|
||||||
style="width: 240px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in [
|
|
||||||
{ label: t('hostsList.ascending'), value: 'asc' },
|
{ label: t('hostsList.ascending'), value: 'asc' },
|
||||||
{ label: t('hostsList.descending'), value: 'desc' },
|
{ label: t('hostsList.descending'), value: 'desc' },
|
||||||
]"
|
]" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -404,41 +258,26 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 指定直播间弹窗 -->
|
<!-- 指定直播间弹窗 -->
|
||||||
<el-dialog
|
<el-dialog v-model="streamdialogVisible" width="800px" :before-close="handleClose" class="cyber-dialog">
|
||||||
v-model="streamdialogVisible"
|
|
||||||
width="800px"
|
|
||||||
:before-close="handleClose"
|
|
||||||
class="cyber-dialog"
|
|
||||||
>
|
|
||||||
<div class="specify-dialog">
|
<div class="specify-dialog">
|
||||||
<el-input
|
<el-input v-model="textarea" style="width: 100%" :rows="15" type="textarea"
|
||||||
v-model="textarea"
|
:placeholder="t('hostsList.enterRoomIds')" @input="handleInput" />
|
||||||
style="width: 100%"
|
<div v-if="maxSpecifyLines != 5000" class="specify-count-hint">
|
||||||
:rows="15"
|
{{ t('hostsList.currentCount') }} {{ currentLineCount }} {{ t('hostsList.countSeparator') }} {{
|
||||||
type="textarea"
|
maxSpecifyLines }}
|
||||||
:placeholder="t('hostsList.enterRoomIds')"
|
{{ t('hostsList.countUnit') }}
|
||||||
@input="handleInput"
|
</div>
|
||||||
/>
|
|
||||||
<div class="specify-footer">
|
<div class="specify-footer">
|
||||||
<el-button class="ghost-btn" @click="specifyCancel">
|
<el-button class="ghost-btn" @click="specifyCancel">
|
||||||
{{ t('hostsList.cancelSpecify') }}
|
{{ t('hostsList.cancelSpecify') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button class="ghost-btn" @click="specifyreset">
|
||||||
class="ghost-btn"
|
|
||||||
@click="specifyreset"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.specifyReset') }}
|
{{ t('hostsList.specifyReset') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button type="primary" @click="specifyClick">
|
||||||
type="primary"
|
|
||||||
@click="specifyClick"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.specifyConfirm') }}
|
{{ t('hostsList.specifyConfirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button type="primary" @click="specifyClickStart">
|
||||||
type="primary"
|
|
||||||
@click="specifyClickStart"
|
|
||||||
>
|
|
||||||
{{ t('hostsList.specifyStart') }}
|
{{ t('hostsList.specifyStart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -446,12 +285,7 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 网络不可访问弹窗 -->
|
<!-- 网络不可访问弹窗 -->
|
||||||
<el-dialog
|
<el-dialog v-model="Inaccessible" width="800px" :before-close="handleClose" class="cyber-dialog">
|
||||||
v-model="Inaccessible"
|
|
||||||
width="800px"
|
|
||||||
:before-close="handleClose"
|
|
||||||
class="cyber-dialog"
|
|
||||||
>
|
|
||||||
<div class="inaccessible-dialog">
|
<div class="inaccessible-dialog">
|
||||||
<img class="inaccessible-img" src="@/assets/wifi.png" />
|
<img class="inaccessible-img" src="@/assets/wifi.png" />
|
||||||
</div>
|
</div>
|
||||||
@@ -480,6 +314,7 @@ import {
|
|||||||
} from "@/api/account";
|
} from "@/api/account";
|
||||||
import { usePythonBridge } from "@/utils/pythonBridge";
|
import { usePythonBridge } from "@/utils/pythonBridge";
|
||||||
import { getUser, setSerch, getSerch } from "@/utils/storage";
|
import { getUser, setSerch, getSerch } from "@/utils/storage";
|
||||||
|
import { getTenantLimit } from "@/utils/tenantConfig";
|
||||||
import { ref, reactive, onMounted, onBeforeUnmount } from "vue";
|
import { ref, reactive, onMounted, onBeforeUnmount } from "vue";
|
||||||
const hourstuo = ref(0);
|
const hourstuo = ref(0);
|
||||||
const minutestuo = ref(0);
|
const minutestuo = ref(0);
|
||||||
@@ -686,8 +521,30 @@ function specifyCancel() {
|
|||||||
queryFormData.value.anchor_ids = [];
|
queryFormData.value.anchor_ids = [];
|
||||||
}
|
}
|
||||||
// 输入框 input
|
// 输入框 input
|
||||||
function handleInput() {
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
// 动态计算最大行数限制
|
||||||
|
const maxSpecifyLines = computed(() => {
|
||||||
|
return getTenantLimit(userInfo.value.tenantId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前行数
|
||||||
|
const currentLineCount = computed(() => {
|
||||||
|
if (!textarea.value || textarea.value.trim() === "") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return textarea.value.split("\n").filter(line => line.trim() !== "").length;
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleInput(value) {
|
||||||
streamdialogVisibletext.value = false;
|
streamdialogVisibletext.value = false;
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lines = value.split("\n");
|
||||||
|
if (lines.length > maxSpecifyLines.value) {
|
||||||
|
textarea.value = lines.slice(0, maxSpecifyLines.value).join("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取主播列表 - 开始爬取
|
// 获取主播列表 - 开始爬取
|
||||||
@@ -1078,16 +935,14 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
// 玻璃态头部区域
|
// 玻璃态头部区域 - 简化版(移除 backdrop-filter 提升性能)
|
||||||
// ================================
|
// ================================
|
||||||
.filter-header.glass-header {
|
.filter-header.glass-header {
|
||||||
background: rgba(255, 255, 255, 0.329);
|
background: #ffffff;
|
||||||
backdrop-filter: blur(12px);
|
border: 1px solid #e8ecf0;
|
||||||
-webkit-backdrop-filter: blur(12px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.9);
|
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
padding: 24px 28px;
|
padding: 24px 28px;
|
||||||
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.06);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
@@ -1156,7 +1011,7 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
// 按钮样式
|
// 按钮样式 - 简化版(移除 transform 动画提升性能)
|
||||||
// ================================
|
// ================================
|
||||||
.cyber-btn-primary {
|
.cyber-btn-primary {
|
||||||
background: linear-gradient(135deg, #00D2FF 0%, #0052CC 100%) !important;
|
background: linear-gradient(135deg, #00D2FF 0%, #0052CC 100%) !important;
|
||||||
@@ -1165,17 +1020,14 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
padding: 0 20px !important;
|
padding: 0 20px !important;
|
||||||
height: 42px !important;
|
height: 42px !important;
|
||||||
box-shadow: 0 4px 12px rgba(0, 82, 204, 0.25) !important;
|
box-shadow: 0 2px 8px rgba(0, 82, 204, 0.2) !important;
|
||||||
transition: all 0.3s ease !important;
|
|
||||||
|
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.05);
|
||||||
box-shadow: 0 0 20px rgba(0, 210, 255, 0.4) !important;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1186,7 +1038,11 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
padding: 0 24px !important;
|
padding: 0 24px !important;
|
||||||
height: 42px !important;
|
height: 42px !important;
|
||||||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3) !important;
|
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.2) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cyber-btn-danger {
|
.cyber-btn-danger {
|
||||||
@@ -1196,7 +1052,11 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
padding: 0 24px !important;
|
padding: 0 24px !important;
|
||||||
height: 42px !important;
|
height: 42px !important;
|
||||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3) !important;
|
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ghost-btn {
|
.ghost-btn {
|
||||||
@@ -1207,7 +1067,6 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
padding: 0 16px !important;
|
padding: 0 16px !important;
|
||||||
height: 40px !important;
|
height: 40px !important;
|
||||||
transition: all 0.2s ease !important;
|
|
||||||
display: inline-flex !important;
|
display: inline-flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
gap: 6px !important;
|
gap: 6px !important;
|
||||||
@@ -1219,8 +1078,6 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: #f8fafc !important;
|
background: #f8fafc !important;
|
||||||
border-color: #cbd5e1 !important;
|
border-color: #cbd5e1 !important;
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
@@ -1266,6 +1123,7 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
.info-label {
|
.info-label {
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-value {
|
.info-value {
|
||||||
@@ -1470,6 +1328,7 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
margin-top: 22px !important;
|
margin-top: 22px !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #cbd5e1;
|
border-color: #cbd5e1;
|
||||||
}
|
}
|
||||||
@@ -1541,6 +1400,17 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
min-height: 350px;
|
min-height: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.specify-count-hint {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: linear-gradient(135deg, rgba(0, 210, 255, 0.08) 0%, rgba(0, 82, 204, 0.08) 100%);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #475569;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
.specify-footer {
|
.specify-footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1608,8 +1478,13 @@ function openhostDisplayId(hostDisplayId) {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gap-3 { gap: 12px; }
|
.gap-3 {
|
||||||
.gap-4 { gap: 16px; }
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-4 {
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.center-justify {
|
.center-justify {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,313 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
|
||||||
<title>Yolo Assistant - Data Console</title>
|
|
||||||
<link href="https://fonts.googleapis.com" rel="preconnect" />
|
|
||||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
|
|
||||||
rel="stylesheet" />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
|
||||||
rel="stylesheet" />
|
|
||||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
|
||||||
<script>
|
|
||||||
tailwind.config = {
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
primary: "#0052CC",
|
|
||||||
"cyber-blue": "#00D2FF",
|
|
||||||
"cyber-blue-dark": "#0052CC",
|
|
||||||
"tech-gray-50": "#F8FAFC",
|
|
||||||
"tech-gray-100": "#F1F5F9",
|
|
||||||
"tech-gray-200": "#E2E8F0",
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
display: ["Inter", "system-ui", "sans-serif"],
|
|
||||||
},
|
|
||||||
borderRadius: {
|
|
||||||
DEFAULT: "12px",
|
|
||||||
'xl': '20px',
|
|
||||||
'2xl': '24px',
|
|
||||||
'3xl': '32px',
|
|
||||||
},
|
|
||||||
boxShadow: {
|
|
||||||
'soft-inner': 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.03)',
|
|
||||||
'cyber-glow': '0 0 20px rgba(0, 210, 255, 0.3)',
|
|
||||||
'premium': '0 10px 25px -5px rgba(0, 0, 0, 0.04), 0 8px 10px -6px rgba(0, 0, 0, 0.04)',
|
|
||||||
'glass': '0 8px 32px 0 rgba(31, 38, 135, 0.07)',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style type="text/tailwindcss">
|
|
||||||
:root {
|
|
||||||
--primary-color: #0052CC;
|
|
||||||
--cyber-gradient: linear-gradient(135deg, #00D2FF 0%, #0052CC 100%);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: radial-gradient(circle at top right, #f8fafc, #eff6ff);
|
|
||||||
}
|
|
||||||
.cyber-button {
|
|
||||||
background: var(--cyber-gradient);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
.cyber-button:hover {
|
|
||||||
filter: brightness(1.1);
|
|
||||||
box-shadow: 0 0 15px rgba(0, 210, 255, 0.4);
|
|
||||||
}
|
|
||||||
.ghost-button {
|
|
||||||
border: 1px solid #E2E8F0;
|
|
||||||
background: white;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
.ghost-button:hover {
|
|
||||||
background: #F8FAFC;
|
|
||||||
border-color: #CBD5E1;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar { width: 6px; }
|
|
||||||
::-webkit-scrollbar-track { background: transparent; }
|
|
||||||
::-webkit-scrollbar-thumb { background: #CBD5E1; border-radius: 10px; }
|
|
||||||
::-webkit-scrollbar-thumb:hover { background: #94A3B8; }
|
|
||||||
.input-tech {
|
|
||||||
@apply bg-white border border-tech-gray-200/60 rounded-xl px-4 py-2.5 text-sm transition-all focus:ring-2 focus:ring-primary/10 focus:border-primary/30 outline-none shadow-soft-inner;
|
|
||||||
}
|
|
||||||
.glass-header {
|
|
||||||
background: rgba(255, 255, 255, 0.7);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="font-display text-slate-900 antialiased min-h-screen">
|
|
||||||
<div class="max-w-[1440px] mx-auto px-6 py-8">
|
|
||||||
<div class="flex items-center justify-between mb-8">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center text-white font-bold text-xl shadow-lg shadow-primary/20">
|
|
||||||
y</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="text-xl font-bold tracking-tight text-slate-900 leading-none">yolo</span>
|
|
||||||
<span class="text-[10px] font-semibold tracking-widest text-primary uppercase">Assistant</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex items-center gap-6 text-xs font-medium bg-white/50 px-4 py-2 rounded-full border border-white/50 shadow-sm">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span class="text-slate-400">当前网络:</span>
|
|
||||||
<span class="text-primary bg-blue-50 px-2 py-0.5 rounded-full">日本 (Japan)</span>
|
|
||||||
</div>
|
|
||||||
<div class="h-3 w-px bg-slate-200"></div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span class="text-slate-400">运行时间:</span>
|
|
||||||
<span class="text-slate-700 font-mono">00:00:00</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<main class="space-y-6">
|
|
||||||
<div class="glass-header rounded-3xl p-8 shadow-glass">
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-end">
|
|
||||||
<div class="lg:col-span-8 grid grid-cols-1 md:grid-cols-12 gap-6">
|
|
||||||
<div class="md:col-span-3 flex items-center h-full">
|
|
||||||
<label class="flex items-center gap-3 cursor-pointer group">
|
|
||||||
<div class="relative">
|
|
||||||
<input class="peer hidden" type="checkbox" />
|
|
||||||
<div
|
|
||||||
class="w-5 h-5 border-2 border-slate-200 rounded-lg peer-checked:bg-primary peer-checked:border-primary transition-all">
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="material-symbols-outlined absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-sm opacity-0 peer-checked:opacity-100">check</span>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="text-sm font-semibold text-slate-600 group-hover:text-primary transition-colors">过滤隐私用户</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="md:col-span-4 space-y-2">
|
|
||||||
<label
|
|
||||||
class="text-[11px] font-bold text-slate-400 uppercase tracking-wider flex items-center gap-2 px-1">
|
|
||||||
<span class="material-symbols-outlined text-xs">payments</span> 金币范围
|
|
||||||
</label>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<input class="input-tech w-full" placeholder="最小金币" type="text" />
|
|
||||||
<span class="text-slate-300">/</span>
|
|
||||||
<input class="input-tech w-full" placeholder="最大金币" type="text" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="md:col-span-5 space-y-2">
|
|
||||||
<label
|
|
||||||
class="text-[11px] font-bold text-slate-400 uppercase tracking-wider flex items-center gap-2 px-1">
|
|
||||||
<span class="material-symbols-outlined text-xs">trending_up</span> 等级范围
|
|
||||||
</label>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<input class="input-tech w-full" placeholder="最小等级" type="text" />
|
|
||||||
<span class="text-slate-300">/</span>
|
|
||||||
<input class="input-tech w-full" placeholder="最大等级" type="text" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="lg:col-span-4 flex gap-3">
|
|
||||||
<button
|
|
||||||
class="cyber-button flex-[2] text-white px-4 py-3 rounded-2xl font-bold text-sm flex items-center justify-center gap-2 shadow-lg shadow-blue-500/20">
|
|
||||||
<span class="material-symbols-outlined text-lg">person_pin_circle</span>
|
|
||||||
选择直播间
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="cyber-button flex-1 py-3 rounded-2xl text-white text-sm font-bold shadow-cyber-glow">
|
|
||||||
开始执行
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-wrap items-center gap-4 mt-8 pt-8 border-t border-slate-200/50">
|
|
||||||
<div class="flex-1 min-w-[300px] flex items-center gap-4">
|
|
||||||
<div class="relative w-48">
|
|
||||||
<select class="input-tech w-full appearance-none pr-10 bg-slate-50/50">
|
|
||||||
<option>全部国家</option>
|
|
||||||
<option>美国</option>
|
|
||||||
<option>日本</option>
|
|
||||||
</select>
|
|
||||||
<span
|
|
||||||
class="material-symbols-outlined absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">expand_more</span>
|
|
||||||
</div>
|
|
||||||
<div class="relative flex-1">
|
|
||||||
<span
|
|
||||||
class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-lg">alternate_email</span>
|
|
||||||
<input class="input-tech w-full pl-10" placeholder="请输入大哥 ID..." type="text" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<button
|
|
||||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
|
||||||
<span class="material-symbols-outlined text-xl">search</span>查询
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
|
||||||
<span class="material-symbols-outlined text-xl">restart_alt</span>重置
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
|
||||||
<span class="material-symbols-outlined text-xl">file_download</span>导出Excel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="ghost-button px-6 py-2.5 rounded-xl text-sm font-semibold text-slate-600 flex items-center gap-2">
|
|
||||||
<span class="material-symbols-outlined text-xl">tune</span>更多筛选
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="bg-white rounded-3xl shadow-premium border border-white flex flex-col min-h-[500px] overflow-hidden">
|
|
||||||
<div class="flex-1 p-8">
|
|
||||||
<div class="w-full overflow-x-auto">
|
|
||||||
<table class="w-full text-left border-separate border-spacing-y-0">
|
|
||||||
<thead>
|
|
||||||
<tr class="text-slate-400 text-[11px] font-bold uppercase tracking-widest">
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">ID</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">所在直播间</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">用户ID</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">等级</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">打赏金币</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">地区</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold">粉丝数</th>
|
|
||||||
<th class="pb-6 px-4 border-b border-slate-100 font-bold text-right">创建时间</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="group hover:bg-slate-50 transition-colors">
|
|
||||||
<td class="py-5 px-4 text-sm text-slate-500">1</td>
|
|
||||||
<td
|
|
||||||
class="py-5 px-4 text-sm font-semibold text-primary underline decoration-primary/20 underline-offset-4 cursor-pointer">
|
|
||||||
toxi_robert</td>
|
|
||||||
<td class="py-5 px-4 text-sm font-mono text-slate-600">706350628...</td>
|
|
||||||
<td class="py-5 px-4">
|
|
||||||
<span
|
|
||||||
class="px-2 py-1 bg-amber-50 text-amber-600 text-[10px] font-bold rounded-lg border border-amber-100">LV
|
|
||||||
29</span>
|
|
||||||
</td>
|
|
||||||
<td class="py-5 px-4 text-sm font-bold text-slate-700">37,420</td>
|
|
||||||
<td class="py-5 px-4 text-sm text-slate-600">意大利</td>
|
|
||||||
<td class="py-5 px-4 text-sm text-slate-500">562</td>
|
|
||||||
<td class="py-5 px-4 text-sm text-slate-400 font-mono text-right">2026-01-16
|
|
||||||
15:15:07</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="hidden">
|
|
||||||
<td class="py-32" colspan="8">
|
|
||||||
<div class="flex flex-col items-center justify-center">
|
|
||||||
<div
|
|
||||||
class="w-16 h-16 bg-slate-50 rounded-full flex items-center justify-center mb-4">
|
|
||||||
<span
|
|
||||||
class="material-symbols-outlined text-3xl text-slate-300">database_off</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-slate-400 text-sm">暂无匹配数据</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<footer
|
|
||||||
class="px-8 py-6 bg-slate-50/50 border-t border-slate-100 flex flex-wrap items-center justify-between gap-6">
|
|
||||||
<div class="flex items-center gap-6">
|
|
||||||
<div class="relative">
|
|
||||||
<select
|
|
||||||
class="bg-white border border-slate-200 rounded-xl py-2 pl-4 pr-10 text-xs font-bold text-slate-600 appearance-none focus:ring-2 focus:ring-primary/10 cursor-pointer">
|
|
||||||
<option>10条/页</option>
|
|
||||||
<option>20条/页</option>
|
|
||||||
<option>50条/页</option>
|
|
||||||
</select>
|
|
||||||
<span
|
|
||||||
class="material-symbols-outlined absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 text-lg pointer-events-none">expand_more</span>
|
|
||||||
</div>
|
|
||||||
<div class="h-6 w-[1px] bg-slate-200"></div>
|
|
||||||
<div class="flex items-center gap-8 text-xs">
|
|
||||||
<div class="flex flex-col gap-0.5">
|
|
||||||
<span class="text-slate-400 font-medium">总数据</span>
|
|
||||||
<span class="font-bold text-slate-900 text-sm">1,248</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-0.5">
|
|
||||||
<span class="text-slate-400 font-medium">有效数</span>
|
|
||||||
<span class="font-bold text-primary text-sm">842</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-0.5">
|
|
||||||
<span class="text-slate-400 uppercase tracking-tighter font-medium">服务到期</span>
|
|
||||||
<span class="text-slate-900 font-bold text-sm">2036-08-01</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<button
|
|
||||||
class="p-2 text-slate-400 hover:bg-white hover:text-primary hover:shadow-sm rounded-xl transition-all">
|
|
||||||
<span class="material-symbols-outlined text-xl">chevron_left</span>
|
|
||||||
</button>
|
|
||||||
<div class="flex items-center gap-1">
|
|
||||||
<button
|
|
||||||
class="w-9 h-9 rounded-xl bg-slate-900 text-white text-xs font-bold transition-all shadow-lg shadow-slate-900/20">1</button>
|
|
||||||
<button
|
|
||||||
class="w-9 h-9 rounded-xl hover:bg-white text-slate-600 text-xs font-bold transition-all">2</button>
|
|
||||||
<button
|
|
||||||
class="w-9 h-9 rounded-xl hover:bg-white text-slate-600 text-xs font-bold transition-all">3</button>
|
|
||||||
<span class="px-2 text-slate-300">...</span>
|
|
||||||
<button
|
|
||||||
class="w-9 h-9 rounded-xl hover:bg-white text-slate-600 text-xs font-bold transition-all">12</button>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
class="p-2 text-slate-400 hover:bg-white hover:text-primary hover:shadow-sm rounded-xl transition-all">
|
|
||||||
<span class="material-symbols-outlined text-xl">chevron_right</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -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>
|
|
||||||
@@ -72,37 +72,17 @@ html {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-container.cyber-page {
|
.app-container.cyber-page {
|
||||||
width: 1600px;
|
width: 1600px;
|
||||||
height: 900px;
|
height: 900px;
|
||||||
background: radial-gradient(ellipse at top right, #f0f9ff 0%, #e0f2fe 30%, #f1f5f9 100%);
|
background: linear-gradient(135deg, #f0f9ff 0%, #e8f4fc 50%, #f1f5f9 100%);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
// 背景装饰
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: -50%;
|
|
||||||
right: -20%;
|
|
||||||
width: 800px;
|
|
||||||
height: 800px;
|
|
||||||
background: radial-gradient(circle, rgba(0, 210, 255, 0.08) 0%, transparent 70%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -30%;
|
|
||||||
left: -10%;
|
|
||||||
width: 600px;
|
|
||||||
height: 600px;
|
|
||||||
background: radial-gradient(circle, rgba(0, 82, 204, 0.06) 0%, transparent 70%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cyber-main-container {
|
.cyber-main-container {
|
||||||
@@ -118,15 +98,10 @@ html {
|
|||||||
|
|
||||||
.content-wrapper.glass-content {
|
.content-wrapper.glass-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: rgba(255, 255, 255, 0.92);
|
background: #ffffff;
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
border-radius: 28px;
|
border-radius: 28px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.95);
|
border: 1px solid #e8ecf0;
|
||||||
box-shadow:
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
||||||
0 20px 60px -10px rgba(0, 0, 0, 0.08),
|
|
||||||
0 10px 30px -5px rgba(0, 0, 0, 0.04),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -144,11 +119,10 @@ html {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: #ffffff;
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
border: 1px solid #e8ecf0;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
.footer-icon {
|
.footer-icon {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
Reference in New Issue
Block a user