融合版

This commit is contained in:
2026-01-23 14:35:09 +08:00
parent 2ce7cd6344
commit 1606f9a3e3
3 changed files with 219 additions and 129 deletions

View File

@@ -31,7 +31,7 @@
<div class="auth-left"> <div class="auth-left">
<div class="logo "> <div class="logo ">
<img class="logo-image" src="@/assets/logo2.png" alt=""> <img class="logo-image" src="@/assets/logo2.png" alt="">
<span class="logo-title">TK主播数据助手</span> <span class="logo-title">TK主播数据助手定制版</span>
</div> </div>
<div class="welcome">{{ $t('login.title') }}</div> <div class="welcome">{{ $t('login.title') }}</div>
<div class="from"> <div class="from">

View File

@@ -41,6 +41,13 @@
<span class="material-icons-round text-sm">search</span> <span class="material-icons-round text-sm">search</span>
{{ $t('hostList.query') }} {{ $t('hostList.query') }}
</button> </button>
<button
@click="handleExport"
class="bg-emerald-500 hover:bg-emerald-600 text-white px-6 py-3 rounded-xl font-medium text-sm transition-all shadow-lg shadow-emerald-500/20 flex items-center gap-2"
>
<span class="material-icons-round text-sm">download</span>
{{ $t('hostList.export') }}
</button>
<button <button
@click="filterdialogVisible = true" @click="filterdialogVisible = true"
class="bg-slate-100 hover:bg-slate-200 text-slate-600 p-3 rounded-xl transition-all" class="bg-slate-100 hover:bg-slate-200 text-slate-600 p-3 rounded-xl transition-all"
@@ -300,6 +307,7 @@ import { usePythonBridge } from '@/utils/pythonBridge'
import { getUser } from '@/utils/storage' import { getUser } from '@/utils/storage'
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { ElMessageBox } from 'element-plus'
import LiveRecordDialog from '@/components/LiveRecordDialog.vue' import LiveRecordDialog from '@/components/LiveRecordDialog.vue'
const { t } = useI18n() const { t } = useI18n()
@@ -477,6 +485,47 @@ function handelClick() {
getlist() getlist()
} }
// 导出Excel
function handleExport() {
ElMessageBox.confirm(
'请选择导出方式',
'导出数据',
{
confirmButtonText: '导出并删除',
cancelButtonText: '仅导出',
distinguishCancelAndClose: true,
type: 'info',
}
)
.then(() => {
// 点击"导出并删除"
doExport(true)
})
.catch((action) => {
if (action === 'cancel') {
// 点击"仅导出"
doExport(false)
}
// 点击关闭按钮则不做任何操作
})
}
function doExport(isDelete) {
exportToExcel({
page: page.value,
limit: pageSize.value,
order: sortData.value.order,
sortBy: sortData.value.sortBy,
country: searchForm.value.country || undefined,
hostsId: searchForm.value.hostsId || undefined,
hostsLevel: searchForm.value.hostsLevel || undefined,
userId: searchForm.value.userId || undefined,
min_fans: searchForm.value.min_fans || undefined,
max_fans: searchForm.value.max_fans || undefined,
isDelete: isDelete
})
}
function reset() { function reset() {
searchForm.value.min_fans = null searchForm.value.min_fans = null
searchForm.value.max_fans = null searchForm.value.max_fans = null

View File

@@ -1,147 +1,188 @@
<template> <template>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-4 mb-4"> <!-- 主容器 - 全屏高度布局 -->
<!-- Stat Cards --> <div class="flex flex-col h-full gap-6">
<!-- 总数量 (较小) -->
<div <!-- 顶部统计区域 - 大数字展示 -->
class="lg:col-span-2 bg-white dark:bg-slate-900 p-4 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="flex items-center justify-between mb-1"> <!-- 总数量 -->
<span class="text-xs font-medium text-slate-500">{{ $t('workbenches.totalnumber') }}</span> <div class="bg-gradient-to-br from-blue-500 to-blue-600 p-6 rounded-2xl shadow-lg shadow-blue-500/20 text-white relative overflow-hidden">
<span class="material-icons-round text-primary/40 text-lg">analytics</span> <div class="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full -translate-y-1/2 translate-x-1/2"></div>
</div> <div class="relative z-10">
<div class="text-xl font-bold text-slate-900 dark:text-white">{{ hostData.totalCount }}</div> <div class="flex items-center gap-2 mb-2">
</div> <span class="material-icons-round text-white/80">analytics</span>
<span class="text-sm font-medium text-white/80">{{ $t('workbenches.totalnumber') }}</span>
<!-- 新建主播 (较小) --> </div>
<div <div class="text-4xl font-bold tracking-tight">{{ hostData.totalCount.toLocaleString() }}</div>
class="lg:col-span-2 bg-white dark:bg-slate-900 p-4 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800"> <div class="mt-2 text-xs text-white/60">累计获取主播</div>
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-slate-500">{{ $t('workbenches.createHost') }}</span>
<span class="material-icons-round text-secondary/40 text-lg">person_add</span>
</div>
<div class="text-xl font-bold text-slate-900 dark:text-white">{{ hostData.validAnchorsCount }}</div>
</div>
<!-- 运行时间 (较大) -->
<div
class="lg:col-span-3 bg-white dark:bg-slate-900 p-5 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800 flex flex-col justify-center">
<div class="flex items-center justify-between">
<!-- <span class="w-4 h-4 rounded-full" :class="1 ? 'bg-red-500' : 'bg-emerald-500'"></span> -->
<div>
<span class="text-xs font-semibold text-slate-400 uppercase tracking-wider block mb-1">{{
$t('workbenches.runTime') }}</span>
<div class="text-2xl font-mono font-bold text-primary">{{ formattedTime }}</div>
</div> </div>
<button @click="openTK"
class="bg-primary hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-semibold transition-all shadow-lg shadow-primary/25">
{{ $t('workbenches.openTK') }}
</button>
</div> </div>
</div>
</div>
<!-- 新建主播 -->
<div class="bg-gradient-to-br from-emerald-500 to-emerald-600 p-6 rounded-2xl shadow-lg shadow-emerald-500/20 text-white relative overflow-hidden">
<!-- Configuration Panel --> <div class="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full -translate-y-1/2 translate-x-1/2"></div>
<div <div class="relative z-10">
class="bg-white dark:bg-slate-900 rounded-2xl shadow-sm border border-slate-100 dark:border-slate-800 overflow-hidden"> <div class="flex items-center gap-2 mb-2">
<div class="p-6 border-b border-slate-100 dark:border-slate-800 flex justify-between items-center"> <span class="material-icons-round text-white/80">person_add</span>
<div class="flex items-center gap-3"> <span class="text-sm font-medium text-white/80">{{ $t('workbenches.createHost') }}</span>
<div class="w-8 h-8 bg-slate-100 dark:bg-slate-800 rounded-lg flex items-center justify-center"> </div>
<span class="material-icons-round text-slate-600 dark:text-slate-400 text-lg">settings</span> <div class="text-4xl font-bold tracking-tight">{{ hostData.validAnchorsCount.toLocaleString() }}</div>
<div class="mt-2 text-xs text-white/60">有效主播数量</div>
</div> </div>
<h2 class="font-bold text-slate-800 dark:text-white">{{ $t('workbenchesSetup.workbenches') }}</h2>
</div> </div>
<div class="flex items-center gap-4 text-sm">
<div class="text-slate-500">{{ $t('workbenchesSetup.network') }}: <span class="text-primary font-bold">{{ locale <!-- 网络状态 -->
== 'zh' ? countryData : countryDataEN }}</span></div> <div class="bg-gradient-to-br from-violet-500 to-violet-600 p-6 rounded-2xl shadow-lg shadow-violet-500/20 text-white relative overflow-hidden">
<div class="flex items-center gap-2"> <div class="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full -translate-y-1/2 translate-x-1/2"></div>
<span class="text-slate-500">指定国家:</span> <div class="relative z-10">
<select v-model="country_info" <div class="flex items-center gap-2 mb-2">
class="bg-slate-50 dark:bg-slate-800 border-none rounded-lg text-xs font-medium focus:ring-0"> <span class="material-icons-round text-white/80">public</span>
<option value="全部">全部</option> <span class="text-sm font-medium text-white/80">{{ $t('workbenchesSetup.network') }}</span>
<option v-for="(item, index) in country_Lst" :key="index" :value="item">{{ item }}</option> </div>
</select> <div class="text-2xl font-bold tracking-tight">{{ locale == 'zh' ? countryData : countryDataEN }}</div>
<div class="mt-2 flex items-center gap-2">
<span class="w-2 h-2 rounded-full" :class="isWifi ? 'bg-red-400 animate-pulse' : 'bg-green-400'"></span>
<span class="text-xs text-white/60">{{ isWifi ? 'VPN 断开' : 'VPN 已连接' }}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="p-6">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> <!-- 中央核心操作区 -->
<!-- Coins --> <div class="flex-1 bg-white dark:bg-slate-900 rounded-3xl shadow-xl border border-slate-100 dark:border-slate-800 overflow-hidden">
<div> <div class="h-full flex flex-col">
<h4 class="text-sm font-bold text-slate-800 dark:text-white mb-4 flex items-center gap-2">
<span class="w-1 h-4 bg-primary rounded-full"></span> <!-- 运行时间 - 中央突出展示 -->
{{ $t('workbenchesSetup.setCoinsNum') }} <div class="bg-gradient-to-r from-slate-50 to-slate-100 dark:from-slate-800 dark:to-slate-900 p-8 text-center border-b border-slate-200 dark:border-slate-700">
</h4> <div class="inline-flex items-center gap-3 mb-4">
<div class="space-y-3"> <span class="w-3 h-3 rounded-full animate-pulse" :class="!pyData.isStart ? 'bg-emerald-500' : 'bg-slate-300'"></span>
<div class="flex shadow-sm rounded-lg overflow-hidden border border-slate-200 dark:border-slate-700"> <span class="text-sm font-semibold text-slate-500 uppercase tracking-widest">{{ $t('workbenches.runTime') }}</span>
<span </div>
class="bg-slate-50 dark:bg-slate-800 px-3 py-2 text-xs font-medium text-slate-500 w-28 flex items-center border-r border-slate-200 dark:border-slate-700">{{ <div class="text-6xl font-mono font-bold text-slate-900 dark:text-white tracking-wider mb-6">
$t('workbenchesSetup.minCoinsNum') }}</span> {{ formattedTime }}
<input </div>
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0 disabled:bg-slate-100"
type="number" v-model="pyData.gold.min" :disabled="!pyData.isStart" /> <!-- 操作按钮组 -->
<div class="flex items-center justify-center gap-4">
<button @click="openTK"
class="bg-slate-200 dark:bg-slate-700 hover:bg-slate-300 dark:hover:bg-slate-600 text-slate-700 dark:text-slate-200 px-6 py-3 rounded-xl text-sm font-semibold transition-all flex items-center gap-2">
<span class="material-icons-round text-lg">play_arrow</span>
{{ $t('workbenches.openTK') }}
</button>
<button v-if="pyData.isStart" @click="submit"
class="bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-10 py-4 rounded-xl font-bold text-lg shadow-xl shadow-blue-500/30 transition-all flex items-center gap-3 hover:scale-[1.02] active:scale-[0.98]">
<span class="material-icons-round">bolt</span>
{{ $t('workbenchesSetup.start') }}
</button>
<button v-else @click="unsubmit"
class="bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-10 py-4 rounded-xl font-bold text-lg shadow-xl shadow-red-500/30 transition-all flex items-center gap-3 hover:scale-[1.02] active:scale-[0.98] animate-pulse">
<span class="material-icons-round">stop</span>
{{ $t('workbenchesSetup.stop') }}
</button>
</div>
<p class="mt-4 text-xs font-medium text-slate-400">
到期时间: <span class="text-emerald-600 dark:text-emerald-400">{{ timestampToTime(expiredTime) }}</span>
</p>
</div>
<!-- 配置区域 - 左右两侧卡片 -->
<div class="flex-1 p-6">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 h-full">
<!-- 左侧金币设置 -->
<div class="bg-slate-50 dark:bg-slate-800/50 rounded-2xl p-5 border border-slate-200 dark:border-slate-700">
<div class="flex items-center gap-3 mb-5">
<div class="w-10 h-10 bg-gradient-to-br from-amber-400 to-orange-500 rounded-xl flex items-center justify-center shadow-lg shadow-amber-500/20">
<span class="material-icons-round text-white">monetization_on</span>
</div>
<div>
<h4 class="font-bold text-slate-800 dark:text-white">{{ $t('workbenchesSetup.setCoinsNum') }}</h4>
<p class="text-xs text-slate-400">设置金币筛选范围</p>
</div>
</div>
<div class="space-y-3">
<div>
<label class="text-xs text-slate-500 mb-1.5 block">{{ $t('workbenchesSetup.minCoinsNum') }}</label>
<input
class="w-full px-4 py-3 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-600 rounded-xl outline-none focus:border-amber-500 focus:ring-2 focus:ring-amber-500/20 transition-all disabled:bg-slate-100 disabled:cursor-not-allowed"
type="number" v-model="pyData.gold.min" :disabled="!pyData.isStart" placeholder="0" />
</div>
<div>
<label class="text-xs text-slate-500 mb-1.5 block">{{ $t('workbenchesSetup.maxCoinsNum') }}</label>
<input
class="w-full px-4 py-3 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-600 rounded-xl outline-none focus:border-amber-500 focus:ring-2 focus:ring-amber-500/20 transition-all disabled:bg-slate-100 disabled:cursor-not-allowed"
type="number" v-model="pyData.gold.max" :disabled="!pyData.isStart" placeholder="999999" />
</div>
</div>
</div> </div>
<div class="flex shadow-sm rounded-lg overflow-hidden border border-slate-200 dark:border-slate-700">
<span <!-- 中间国家设置 -->
class="bg-slate-50 dark:bg-slate-800 px-3 py-2 text-xs font-medium text-slate-500 w-28 flex items-center border-r border-slate-200 dark:border-slate-700">{{ <div class="bg-slate-50 dark:bg-slate-800/50 rounded-2xl p-5 border border-slate-200 dark:border-slate-700">
$t('workbenchesSetup.maxCoinsNum') }}</span> <div class="flex items-center gap-3 mb-5">
<input <div class="w-10 h-10 bg-gradient-to-br from-violet-400 to-purple-500 rounded-xl flex items-center justify-center shadow-lg shadow-violet-500/20">
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0 disabled:bg-slate-100" <span class="material-icons-round text-white">flag</span>
type="number" v-model="pyData.gold.max" :disabled="!pyData.isStart" /> </div>
<div>
<h4 class="font-bold text-slate-800 dark:text-white">指定国家</h4>
<p class="text-xs text-slate-400">选择爬取的目标国家</p>
</div>
</div>
<div class="space-y-3">
<div>
<label class="text-xs text-slate-500 mb-1.5 block">目标国家</label>
<select v-model="country_info"
class="w-full px-4 py-3 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-600 rounded-xl outline-none focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 transition-all disabled:bg-slate-100 appearance-none cursor-pointer"
:disabled="!pyData.isStart">
<option value="全部">全部国家</option>
<option v-for="(item, index) in country_Lst" :key="index" :value="item">{{ item }}</option>
</select>
</div>
<div class="pt-2">
<div class="flex items-center justify-between text-xs text-slate-500 mb-2">
<span>当前IP所在地</span>
<span class="font-semibold text-slate-700 dark:text-slate-300">{{ locale == 'zh' ? countryData : countryDataEN }}</span>
</div>
<div class="h-1 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-violet-500 to-purple-500 rounded-full" style="width: 100%"></div>
</div>
</div>
</div>
</div> </div>
<!-- 右侧粉丝设置 -->
<div class="bg-slate-50 dark:bg-slate-800/50 rounded-2xl p-5 border border-slate-200 dark:border-slate-700">
<div class="flex items-center gap-3 mb-5">
<div class="w-10 h-10 bg-gradient-to-br from-pink-400 to-rose-500 rounded-xl flex items-center justify-center shadow-lg shadow-pink-500/20">
<span class="material-icons-round text-white">favorite</span>
</div>
<div>
<h4 class="font-bold text-slate-800 dark:text-white">{{ $t('workbenchesSetup.setFansNum') }}</h4>
<p class="text-xs text-slate-400">设置粉丝筛选范围</p>
</div>
</div>
<div class="space-y-3">
<div>
<label class="text-xs text-slate-500 mb-1.5 block">{{ $t('workbenchesSetup.minFansNum') }}</label>
<input
class="w-full px-4 py-3 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-600 rounded-xl outline-none focus:border-pink-500 focus:ring-2 focus:ring-pink-500/20 transition-all disabled:bg-slate-100 disabled:cursor-not-allowed"
type="number" v-model="pyData.fans.min" :disabled="!pyData.isStart" placeholder="0" />
</div>
<div>
<label class="text-xs text-slate-500 mb-1.5 block">{{ $t('workbenchesSetup.maxFansNum') }}</label>
<input
class="w-full px-4 py-3 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-600 rounded-xl outline-none focus:border-pink-500 focus:ring-2 focus:ring-pink-500/20 transition-all disabled:bg-slate-100 disabled:cursor-not-allowed"
type="number" v-model="pyData.fans.max" :disabled="!pyData.isStart" placeholder="999999" />
</div>
</div>
</div>
</div> </div>
</div> </div>
<!-- Fans -->
<div>
<h4 class="text-sm font-bold text-slate-800 dark:text-white mb-4 flex items-center gap-2">
<span class="w-1 h-4 bg-secondary rounded-full"></span>
{{ $t('workbenchesSetup.setFansNum') }}
</h4>
<div class="space-y-3">
<div class="flex shadow-sm rounded-lg overflow-hidden border border-slate-200 dark:border-slate-700">
<span
class="bg-slate-50 dark:bg-slate-800 px-3 py-2 text-xs font-medium text-slate-500 w-28 flex items-center border-r border-slate-200 dark:border-slate-700">{{
$t('workbenchesSetup.minFansNum') }}</span>
<input
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0 disabled:bg-slate-100"
type="number" v-model="pyData.fans.min" :disabled="!pyData.isStart" />
</div>
<div class="flex shadow-sm rounded-lg overflow-hidden border border-slate-200 dark:border-slate-700">
<span
class="bg-slate-50 dark:bg-slate-800 px-3 py-2 text-xs font-medium text-slate-500 w-28 flex items-center border-r border-slate-200 dark:border-slate-700">{{
$t('workbenchesSetup.maxFansNum') }}</span>
<input
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0 disabled:bg-slate-100"
type="number" v-model="pyData.fans.max" :disabled="!pyData.isStart" />
</div>
</div>
</div>
</div>
<div class="mt-6 text-center">
<button v-if="pyData.isStart" @click="submit"
class="bg-slate-900 dark:bg-primary hover:scale-[1.02] active:scale-[0.98] text-white px-10 py-3 rounded-xl font-bold text-lg shadow-xl shadow-slate-900/10 dark:shadow-primary/20 transition-all flex items-center gap-2 mx-auto">
<span class="material-icons-round">bolt</span>
{{ $t('workbenchesSetup.start') }}
</button>
<button v-else @click="unsubmit"
class="bg-red-500 hover:bg-red-600 hover:scale-[1.02] active:scale-[0.98] text-white px-12 py-4 rounded-xl font-bold text-lg shadow-xl shadow-red-500/20 transition-all flex items-center gap-3 mx-auto">
<span class="material-icons-round">stop</span>
{{ $t('workbenchesSetup.stop') }}
</button>
<p class="mt-4 text-xs font-medium text-emerald-600 dark:text-emerald-400">
到期时间: {{ timestampToTime(expiredTime) }}
</p>
</div> </div>
</div> </div>
</div> </div>
</template> </template>