登录页优化
This commit is contained in:
@@ -76,3 +76,7 @@ export function accountName(str) {
|
||||
export function liveHostDetail(data) {
|
||||
return postAxios({ url: 'api/save_data/live_host_detail', data })
|
||||
}
|
||||
|
||||
export function revenueStats(hostId) {
|
||||
return getAxios({ url: 'api/save_data/revenue_stats?displayId=' + hostId })
|
||||
}
|
||||
|
||||
BIN
src/assets/logo2.png
Normal file
BIN
src/assets/logo2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
src/assets/logoBg2.png
Normal file
BIN
src/assets/logoBg2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 363 KiB |
@@ -79,5 +79,19 @@ export default {
|
||||
invitationType: 'invitationType',
|
||||
invitationType1: 'Regular',
|
||||
invitationType2: 'Golden',
|
||||
liveSessions: 'Live Sessions',
|
||||
viewSessions: 'View Sessions',
|
||||
liveRevenue: 'Live Revenue',
|
||||
viewRevenue: 'View Revenue',
|
||||
revenueHost: 'Host',
|
||||
todayRevenueUsd: 'Today Revenue (USD)',
|
||||
totalRevenueUsd: 'Total Revenue (USD)',
|
||||
liveDays: 'Live Days',
|
||||
historyRevenueUsd: 'History Revenue (USD)',
|
||||
revenueHigh: 'High',
|
||||
revenueLow: 'Low',
|
||||
revenueTime: 'Time',
|
||||
close: 'Close',
|
||||
selectPlaceholder: 'Please select',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export default {
|
||||
export default {
|
||||
login: {
|
||||
title: '账号登陆',
|
||||
title: '账号登录',
|
||||
version: '版本号',
|
||||
login: '登录',
|
||||
tenantName: '租户名称',
|
||||
account: '账户',
|
||||
account: '账号',
|
||||
password: '密码',
|
||||
Language: '语言设置',
|
||||
network: '网络设置',
|
||||
@@ -27,6 +27,7 @@ export default {
|
||||
guildPassPlace: '请输入登录密码',
|
||||
queriedNum: '今日已查询次数',
|
||||
loginBackend: '登录后台',
|
||||
workbenches: '工作台',
|
||||
},
|
||||
workbenchesSetup: {
|
||||
workbenches: '工作台',
|
||||
@@ -34,7 +35,7 @@ export default {
|
||||
setCoinsNum: '设置金币数量',
|
||||
setFansNum: '设置粉丝数量',
|
||||
setQuery: '后台查询频率',
|
||||
setNum: ' 期望可邀请主播数量',
|
||||
setNum: '期望可邀请主播数量',
|
||||
minCoinsNum: '最小金币数量',
|
||||
maxCoinsNum: '最大金币数量',
|
||||
minFansNum: '最小粉丝数量',
|
||||
@@ -44,11 +45,9 @@ export default {
|
||||
num: '个',
|
||||
start: '开始获取数据',
|
||||
stop: '停止',
|
||||
prompt: '到达数量后停止爬取',
|
||||
prompt: '达到数量后停止爬取',
|
||||
setHostNum: '设置爬取数量',
|
||||
unlimitedQuantity: '不限爬取数量',
|
||||
|
||||
|
||||
},
|
||||
hostList: {
|
||||
placeCountry: '选择国家',
|
||||
@@ -80,5 +79,19 @@ export default {
|
||||
invitationType: '邀请类型',
|
||||
invitationType1: '普票',
|
||||
invitationType2: '金票',
|
||||
liveSessions: '直播场次',
|
||||
viewSessions: '查看场次',
|
||||
liveRevenue: '直播收益',
|
||||
viewRevenue: '查看收益',
|
||||
revenueHost: '主播',
|
||||
todayRevenueUsd: '今日收益(美元)',
|
||||
totalRevenueUsd: '总收益(美元)',
|
||||
liveDays: '直播天数',
|
||||
historyRevenueUsd: '历史收益(美元)',
|
||||
revenueHigh: '高',
|
||||
revenueLow: '低',
|
||||
revenueTime: '时间',
|
||||
close: '关闭',
|
||||
selectPlaceholder: '请选择',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,55 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="container">
|
||||
<div class="right">
|
||||
<img src="../assets/logoBg.png" class="background-video" alt="">
|
||||
<!-- 设置 -->
|
||||
<div class="center-align">
|
||||
<div></div>
|
||||
<div class="setup">
|
||||
<div class="setup-item center-justify">
|
||||
<div></div>
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<span style="font-size:20px; color: #fff;">
|
||||
{{ $t('login.network') }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="setup-item center-justify">
|
||||
<div></div>
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<span style="font-size:20px; color: #fff;">
|
||||
{{ $t('login.Language') }}
|
||||
</span>
|
||||
<!-- <el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon> -->
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="switchLanguage('zh')">
|
||||
中文
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="switchLanguage('en')">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<!-- <el-radio-group v-model="localeData" size="large">
|
||||
<el-radio-button @click="switchLanguage('zh')" label="中文" />
|
||||
<el-radio-button @click="switchLanguage('en')" label="English" />
|
||||
</el-radio-group> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="auth-shell">
|
||||
<div class="top-actions ">
|
||||
<div class="setup-item center-justify">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link ">
|
||||
<span class="setup-text">{{ $t('login.network') }}</span>
|
||||
</span>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="center-line" style="margin-top: 40px;">
|
||||
<!-- logo -->
|
||||
<div class="logo">
|
||||
<div class="center-justify" style="height: 80px; width: 300px;">
|
||||
<!-- <img style="margin-right: 20px;height: 100%;" src="@/assets/logo.png"> -->
|
||||
<img style="height: 100%;" src="@/assets/logotext.png">
|
||||
</div>
|
||||
<div class="setup-item center-justify">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<span class="setup-text">{{ $t('login.Language') }}</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="switchLanguage('zh')">
|
||||
中文
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="switchLanguage('en')">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="auth-card">
|
||||
<div class="auth-left">
|
||||
<div class="logo center-justify">
|
||||
<img class="logo-image" src="@/assets/logo2.png" alt="">
|
||||
<span class="logo-title">TK主播数据助手</span>
|
||||
</div>
|
||||
|
||||
<!-- From -->
|
||||
<div class="welcome">{{ $t('login.title') }}</div>
|
||||
<div class="from">
|
||||
<div class="from-title center-justify">
|
||||
<div>{{ $t('login.title') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="from-input">
|
||||
<el-form label-position="left" label-width="100px" :model="formData">
|
||||
<div class="field-label">{{ $t('login.tenantName') }}</div>
|
||||
<div class="from-input-item1">
|
||||
<img src="@/assets/username.png" alt="">
|
||||
<el-input style="height: 25px;" v-model="formData.tenantName"
|
||||
:placeholder="$t('login.tenantName')" clearable @keyup.enter="onSubmit" />
|
||||
</div>
|
||||
<div class="field-label">{{ $t('login.account') }}</div>
|
||||
<div class="from-input-item1">
|
||||
<img src="@/assets/username.png" alt="">
|
||||
<el-input style="height: 25px;" v-model="formData.userId"
|
||||
:placeholder="$t('login.account')" clearable @keyup.enter="onSubmit" />
|
||||
</div>
|
||||
<div class="field-label">{{ $t('login.password') }}</div>
|
||||
<div class="from-input-item1">
|
||||
<img src="@/assets/password.png" alt="">
|
||||
<el-input style="height: 25px; " v-model="formData.password" type="password"
|
||||
@@ -82,18 +57,19 @@
|
||||
</div>
|
||||
|
||||
<div class="from-input-item">
|
||||
<el-button class="loginButton" color="#8f7ee7" type="primary" @click="onSubmit">{{
|
||||
$t('login.login') }}</el-button>
|
||||
|
||||
<el-button class="loginButton" style="height: 3vw;" color="#4f6ef7" type="primary"
|
||||
:loading="isSubmitting" :disabled="isSubmitting" @click="onSubmit">{{
|
||||
$t('login.login') }}</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version">{{ $t('login.version') }}:{{ version }}</div>
|
||||
</div>
|
||||
<div class="auth-right">
|
||||
<img src="@/assets/logoBg2.png" class="illustration" alt="">
|
||||
</div>
|
||||
<div class="version center-justify ">{{ $t('login.version') }}:{{ version }}</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -103,7 +79,6 @@ import { ref, reactive, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { login, getIdByName } from '@/api/account';
|
||||
import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/utils/storage';
|
||||
import { ElLoading } from 'element-plus';
|
||||
import { usePythonBridge } from '@/utils/pythonBridge'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -117,7 +92,7 @@ function switchLanguage(lang) {
|
||||
}
|
||||
|
||||
const { getVersion, stopScript } = usePythonBridge();
|
||||
let version = ref('5.1.0');
|
||||
let version = ref('5.9.0');
|
||||
onMounted(() => {
|
||||
|
||||
stopScript();
|
||||
@@ -132,6 +107,7 @@ onMounted(() => {
|
||||
|
||||
|
||||
const router = useRouter();
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
const formData = ref({
|
||||
tenantName: getUserPass() == null ? '' : getUserPass().tenantName,
|
||||
@@ -142,11 +118,10 @@ const formData = ref({
|
||||
|
||||
|
||||
const onSubmit = () => {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: 'Loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
});
|
||||
if (isSubmitting.value) {
|
||||
return;
|
||||
}
|
||||
isSubmitting.value = true;
|
||||
setUserPass(formData.value);
|
||||
getIdByName(formData.value.tenantName).then((tenantId) => {
|
||||
console.log(tenantId)
|
||||
@@ -155,14 +130,16 @@ const onSubmit = () => {
|
||||
username: formData.value.userId,
|
||||
password: formData.value.password,
|
||||
}).then((res) => {
|
||||
loading.close();
|
||||
isSubmitting.value = false;
|
||||
// console.log(res)
|
||||
setToken(res.tokenValue);
|
||||
setUser(res);
|
||||
router.push('/nav');
|
||||
}).catch((err) => {
|
||||
loading.close();
|
||||
isSubmitting.value = false;
|
||||
});
|
||||
}).catch(() => {
|
||||
isSubmitting.value = false;
|
||||
})
|
||||
|
||||
|
||||
@@ -175,153 +152,23 @@ const onSubmit = () => {
|
||||
height: 900px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(180deg, #f3f5f9 0%, #eef1f5 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Source Han Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
|
||||
/* 页面无法选中 */
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 1600px;
|
||||
height: 900px;
|
||||
|
||||
.right {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 1600px;
|
||||
height: 900px;
|
||||
padding: 20px 40px 20px 50px;
|
||||
border-left: 3px solid #23516e;
|
||||
position: relative;
|
||||
/* 添加 position: relative */
|
||||
overflow: hidden;
|
||||
/* 防止内容溢出 */
|
||||
|
||||
.version {
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
font-size: 20px;
|
||||
bottom: 20px;
|
||||
left: calc(50% - 50px);
|
||||
// box-sizing: border-box;
|
||||
// width: 1600px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.background-video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
/* 确保视频在内容之下 */
|
||||
}
|
||||
|
||||
.setup {
|
||||
display: flex;
|
||||
color: #fff;
|
||||
|
||||
.setup-item {
|
||||
padding: 10px 6px;
|
||||
display: flex;
|
||||
|
||||
&>div:nth-child(1) {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(255, 255, 255);
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.from {
|
||||
width: 420px;
|
||||
// height: 320px;
|
||||
color: @bg-color;
|
||||
background-color: #ffffff44;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #fff;
|
||||
padding: 32px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.from-title {
|
||||
font-family: Source Han Sans SC;
|
||||
font-weight: 500;
|
||||
font-size: 24px;
|
||||
color: @bg-color;
|
||||
line-height: 37px;
|
||||
|
||||
|
||||
div {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
// border-bottom: 4px solid #1db97d;
|
||||
}
|
||||
}
|
||||
|
||||
.from-input {
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
|
||||
.from-input-item {
|
||||
display: flex;
|
||||
padding: 8px 0;
|
||||
|
||||
.from-input-item-title {
|
||||
color: @bg-color;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
width: 80px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.loginButton {
|
||||
width: 359px;
|
||||
height: 50px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24px;
|
||||
border: 1px solid #FFFFFF;
|
||||
|
||||
|
||||
font-family: Source Han Sans SC;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
color: @bg-color;
|
||||
line-height: 37px;
|
||||
}
|
||||
}
|
||||
|
||||
.from-input-item1 {
|
||||
display: flex;
|
||||
width: 359px;
|
||||
height: 50px;
|
||||
background: @bg-color-light-light;
|
||||
border-radius: 24px;
|
||||
border: 1px solid #FFFFFF;
|
||||
padding: 12px 25px 13px 25px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.center-line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
// justify-content: center;
|
||||
}
|
||||
|
||||
.center-justify {
|
||||
@@ -330,37 +177,247 @@ const onSubmit = () => {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.center-align {
|
||||
.auth-shell {
|
||||
width: 1320px;
|
||||
height: 760px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.center-flex {
|
||||
.auth-shell::before,
|
||||
.auth-shell::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
.auth-shell::before {
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
background: radial-gradient(circle, rgba(214, 226, 255, 0.7), rgba(214, 226, 255, 0));
|
||||
left: -80px;
|
||||
top: -40px;
|
||||
}
|
||||
|
||||
.auth-shell::after {
|
||||
width: 360px;
|
||||
height: 360px;
|
||||
background: radial-gradient(circle, rgba(255, 236, 214, 0.6), rgba(255, 236, 214, 0));
|
||||
right: -40px;
|
||||
bottom: -60px;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.setup-item {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid #e1e6f2;
|
||||
border-radius: 16px;
|
||||
padding: 8px 12px 8px 14px;
|
||||
box-shadow: 0 8px 18px rgba(40, 57, 108, 0.12);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.setup-text {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2b3347;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.setup-item::after {
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-right: 2px solid #9aa1b3;
|
||||
border-bottom: 2px solid #9aa1b3;
|
||||
transform: rotate(45deg);
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.setup-item:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: #c9d2f4;
|
||||
box-shadow: 0 12px 24px rgba(40, 57, 108, 0.16);
|
||||
}
|
||||
|
||||
.setup-item:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
width: 1200px;
|
||||
height: 680px;
|
||||
display: flex;
|
||||
background: #ffffff;
|
||||
border-radius: 28px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 24px 60px rgba(40, 57, 108, 0.16);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.auth-left {
|
||||
width: 42%;
|
||||
padding: 48px 56px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
// margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
height: 200px;
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: #2b3347;
|
||||
letter-spacing: 2px;
|
||||
position: relative;
|
||||
padding-left: 14px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.logo-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
width: 4px;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(180deg, #4f6ef7, #5b7bff);
|
||||
}
|
||||
|
||||
.welcome {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #2b3347;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.from {
|
||||
width: 100%;
|
||||
color: #2b3347;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.from-input {
|
||||
width: 100%;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
margin: 12px 0 6px;
|
||||
}
|
||||
|
||||
.from-input-item {
|
||||
display: flex;
|
||||
padding: 16px 0 0 0;
|
||||
}
|
||||
|
||||
.loginButton {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #4f6ef7, #5b7bff);
|
||||
border-radius: 14px;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 12px 24px rgba(79, 110, 247, 0.28);
|
||||
}
|
||||
|
||||
.from-input-item1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: #f6f8fc;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e6eaf2;
|
||||
padding: 10px 14px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 4px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.from-input-item1 img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-top: 24px;
|
||||
font-size: 12px;
|
||||
color: #9aa1b3;
|
||||
}
|
||||
|
||||
.auth-right {
|
||||
width: 58%;
|
||||
background: linear-gradient(160deg, #f8f9fc 0%, #f0f3f9 100%);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.illustration {
|
||||
width: 90%;
|
||||
height: auto;
|
||||
max-height: 88%;
|
||||
object-fit: contain;
|
||||
filter: drop-shadow(0 30px 60px rgba(40, 57, 108, 0.25));
|
||||
}
|
||||
|
||||
.el-input__wrapper {
|
||||
--el-input-focus-border-color: rgba(255, 255, 0, 0);
|
||||
--el-menu-hover-bg-color: rgba(255, 255, 0, 0);
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="less">
|
||||
::v-deep(.el-input__wrapper) {
|
||||
background-color: rgba(255, 0, 0, 0);
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
}
|
||||
|
||||
::v-deep(.el-input__inner) {
|
||||
color: #fff;
|
||||
|
||||
color: #2b3347;
|
||||
}
|
||||
|
||||
::v-deep(.el-input__inner::placeholder) {
|
||||
color: @bg-color;
|
||||
color: #9aa1b3;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
347
src/views/hometest.html
Normal file
347
src/views/hometest.html
Normal file
@@ -0,0 +1,347 @@
|
||||
<!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 - Elite Workstation</title>
|
||||
<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/icon?family=Material+Icons+Round" rel="stylesheet" />
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#0066FF", // Cyber Blue
|
||||
secondary: "#6366F1", // Electric Indigo
|
||||
"background-light": "#F8FAFC",
|
||||
"background-dark": "#0F172A",
|
||||
},
|
||||
fontFamily: {
|
||||
display: ["Inter", "sans-serif"],
|
||||
},
|
||||
borderRadius: {
|
||||
DEFAULT: "12px",
|
||||
'xl': '20px',
|
||||
},
|
||||
boxShadow: {
|
||||
'premium': '0 4px 20px -2px rgba(0, 0, 0, 0.05), 0 2px 10px -2px rgba(0, 0, 0, 0.02)',
|
||||
'glass': 'inset 0 0 0 1px rgba(255, 255, 255, 0.4)',
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.sidebar-active {
|
||||
background: linear-gradient(90deg, #F0F7FF 0%, #FFFFFF 100%);
|
||||
border-right: 3px solid #0066FF;
|
||||
box-shadow: 4px 0 15px -5px rgba(0, 102, 255, 0.15);
|
||||
}
|
||||
|
||||
.subtle-grid {
|
||||
background-image: radial-gradient(#E2E8F0 0.5px, transparent 0.5px);
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-background-light dark:bg-background-dark text-slate-800 dark:text-slate-200 subtle-grid min-h-screen">
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
<aside
|
||||
class="w-64 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-800 flex flex-col z-20">
|
||||
<div class="p-8 flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center shadow-lg shadow-primary/20">
|
||||
<span class="text-white font-bold text-xl">y</span>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold tracking-tight text-slate-900 dark:text-white">Yolo <span
|
||||
class="text-primary">助手</span></h1>
|
||||
</div>
|
||||
<nav class="flex-1 px-4 space-y-2">
|
||||
<a class="sidebar-active flex items-center gap-3 px-4 py-3 rounded-lg text-primary font-medium"
|
||||
href="#">
|
||||
<span class="material-icons-round">grid_view</span>
|
||||
工作台
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-500 hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors"
|
||||
href="#">
|
||||
<span class="material-icons-round">person_search</span>
|
||||
主播列表
|
||||
</a>
|
||||
</nav>
|
||||
<div class="p-6 border-t border-slate-100 dark:border-slate-800">
|
||||
<button
|
||||
class="flex items-center gap-3 text-slate-500 hover:text-red-500 transition-colors w-full px-4 py-2">
|
||||
<span class="material-icons-round text-lg">logout</span>
|
||||
<span class="font-medium">退出登录</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="flex-1 overflow-y-auto p-8 relative">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-premium border border-slate-100 dark:border-slate-800">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-sm font-medium text-slate-500">总数量</span>
|
||||
<span class="material-icons-round text-primary/40">analytics</span>
|
||||
</div>
|
||||
<div class="text-3xl font-bold text-slate-900 dark:text-white">0</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-premium border border-slate-100 dark:border-slate-800">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-sm font-medium text-slate-500">新建主播</span>
|
||||
<span class="material-icons-round text-secondary/40">person_add</span>
|
||||
</div>
|
||||
<div class="text-3xl font-bold text-slate-900 dark:text-white">0</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-premium border border-slate-100 dark:border-slate-800">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-sm font-medium text-slate-500">查询 / 邀请</span>
|
||||
<span class="material-icons-round text-slate-400">compare_arrows</span>
|
||||
</div>
|
||||
<div class="text-3xl font-bold text-slate-900 dark:text-white">0 <span
|
||||
class="text-slate-300 text-lg mx-2">/</span> 0</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-premium border border-slate-100 dark:border-slate-800 flex flex-col justify-center">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<span
|
||||
class="text-xs font-semibold text-slate-400 uppercase tracking-wider block mb-1">运行时间</span>
|
||||
<div class="text-2xl font-mono font-bold text-primary">00:00:00</div>
|
||||
</div>
|
||||
<button
|
||||
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">
|
||||
开启TK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div class="glass-card p-6 rounded-xl shadow-premium">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-red-500"></span>
|
||||
<h3 class="font-bold text-slate-800 dark:text-white">公会账号 A</h3>
|
||||
</div>
|
||||
<span class="text-xs text-slate-500">今日查询: 0</span>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-slate-500 mb-1 block">账号</label>
|
||||
<input
|
||||
class="w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
type="text" value="11111" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-slate-500 mb-1 block">密码</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
class="w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
type="password" value="******" />
|
||||
<span
|
||||
class="material-icons-round absolute right-3 top-2 text-slate-400 text-sm cursor-pointer">visibility_off</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="w-full bg-slate-900 dark:bg-slate-700 hover:bg-black text-white py-2.5 rounded-lg font-medium transition-all">登录后台</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass-card p-6 rounded-xl shadow-premium">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-red-500"></span>
|
||||
<h3 class="font-bold text-slate-800 dark:text-white">公会账号 B</h3>
|
||||
</div>
|
||||
<span class="text-xs text-slate-500">今日查询: 0</span>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-slate-500 mb-1 block">账号</label>
|
||||
<input
|
||||
class="w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
type="text" value="22222" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-slate-500 mb-1 block">密码</label>
|
||||
<input
|
||||
class="w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
placeholder="请输入登录密码" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="w-full bg-slate-900 dark:bg-slate-700 hover:bg-black text-white py-2.5 rounded-lg font-medium transition-all">登录后台</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900 rounded-2xl shadow-premium border border-slate-100 dark:border-slate-800 overflow-hidden">
|
||||
<div class="p-6 border-b border-slate-100 dark:border-slate-800 flex justify-between items-center">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8 h-8 bg-slate-100 dark:bg-slate-800 rounded-lg flex items-center justify-center">
|
||||
<span
|
||||
class="material-icons-round text-slate-600 dark:text-slate-400 text-lg">settings</span>
|
||||
</div>
|
||||
<h2 class="font-bold text-slate-800 dark:text-white">任务工作台配置</h2>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<div class="text-slate-500">当前网络: <span class="text-primary font-bold">日本</span></div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-500">指定国家:</span>
|
||||
<select
|
||||
class="bg-slate-50 dark:bg-slate-800 border-none rounded-lg text-xs font-medium focus:ring-0">
|
||||
<option>全部</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12 mb-10">
|
||||
<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-primary rounded-full"></span>
|
||||
设置金币数量
|
||||
</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">最小金币</span>
|
||||
<input
|
||||
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0"
|
||||
type="number" value="0" />
|
||||
</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">最大金币</span>
|
||||
<input
|
||||
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0"
|
||||
type="number" value="0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
设置粉丝数量
|
||||
</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">最小粉丝</span>
|
||||
<input
|
||||
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0"
|
||||
type="number" value="0" />
|
||||
</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">最大粉丝</span>
|
||||
<input
|
||||
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0"
|
||||
type="number" value="0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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-emerald-500 rounded-full"></span>
|
||||
后台查询频率
|
||||
</h4>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
class="flex shadow-sm rounded-lg overflow-hidden border border-slate-200 dark:border-slate-700">
|
||||
<input
|
||||
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0"
|
||||
type="number" value="0" />
|
||||
<span
|
||||
class="bg-slate-100 dark:bg-slate-800 px-3 py-2 text-xs font-medium text-slate-500 w-24 flex items-center justify-center border-l border-slate-200 dark:border-slate-700">次/小时</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex shadow-sm rounded-lg overflow-hidden border border-slate-200 dark:border-slate-700">
|
||||
<input
|
||||
class="flex-1 px-4 py-2 text-sm bg-white dark:bg-slate-900 border-none outline-none focus:ring-0"
|
||||
type="number" value="0" />
|
||||
<span
|
||||
class="bg-slate-100 dark:bg-slate-800 px-3 py-2 text-xs font-medium text-slate-500 w-24 flex items-center justify-center border-l border-slate-200 dark:border-slate-700">次/24小时</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col lg:flex-row items-center justify-between gap-8 pt-8 border-t border-slate-100 dark:border-slate-800">
|
||||
<div class="flex items-center gap-6">
|
||||
<label class="flex items-center gap-2 cursor-pointer group">
|
||||
<input class="w-4 h-4 rounded border-slate-300 text-primary focus:ring-primary/20"
|
||||
type="checkbox" />
|
||||
<span
|
||||
class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">过滤游戏主播</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer group">
|
||||
<input class="w-4 h-4 rounded border-slate-300 text-primary focus:ring-primary/20"
|
||||
type="checkbox" />
|
||||
<span
|
||||
class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">过滤带货主播</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer group">
|
||||
<input class="w-4 h-4 rounded border-slate-300 text-primary focus:ring-primary/20"
|
||||
type="checkbox" />
|
||||
<span
|
||||
class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">排行榜模式</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-xs font-bold text-slate-500 uppercase tracking-widest">期望邀请上限:</span>
|
||||
<div class="flex p-1 bg-slate-100 dark:bg-slate-800 rounded-lg">
|
||||
<button
|
||||
class="px-4 py-1.5 text-xs font-semibold rounded-md bg-white dark:bg-slate-700 text-primary shadow-sm">设置爬取数量</button>
|
||||
<button
|
||||
class="px-4 py-1.5 text-xs font-semibold rounded-md text-slate-500 hover:text-slate-700">不限爬取数量</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-12 text-center">
|
||||
<button
|
||||
class="bg-slate-900 dark:bg-primary hover:scale-[1.02] active:scale-[0.98] text-white px-12 py-4 rounded-xl font-bold text-lg shadow-xl shadow-slate-900/10 dark:shadow-primary/20 transition-all flex items-center gap-3 mx-auto">
|
||||
<span class="material-icons-round">bolt</span>
|
||||
开始获取数据
|
||||
</button>
|
||||
<p class="mt-4 text-xs font-medium text-emerald-600 dark:text-emerald-400">
|
||||
到期时间: 2036-08-01 00:00:00
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute -top-24 -right-24 w-64 h-64 bg-primary/5 rounded-full blur-3xl pointer-events-none">
|
||||
</div>
|
||||
<div
|
||||
class="absolute -bottom-24 -left-24 w-64 h-64 bg-secondary/5 rounded-full blur-3xl pointer-events-none">
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="hostList">
|
||||
<div>
|
||||
<div style="display: flex;">
|
||||
@@ -52,10 +52,18 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="直播场次" width="120">
|
||||
<el-table-column :label="$t('hostList.liveSessions')" width="120">
|
||||
<template #default="scope">
|
||||
<el-button class="live-btn" size="small" @click="getliveHost(scope.row.hostId)">
|
||||
查看场次
|
||||
{{ $t('hostList.viewSessions') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('hostList.liveRevenue')" width="120">
|
||||
<template #default="scope">
|
||||
<el-button class="live-btn" size="small" @click="getRevenueStats(scope.row.hostId)">
|
||||
{{ $t('hostList.viewRevenue') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -119,14 +127,16 @@
|
||||
<div><label>{{ $t('hostList.sortType') }}</label></div>
|
||||
|
||||
|
||||
<el-select v-model="sortData.sortType" filterable placeholder="请选择" style="width: 240px">
|
||||
<el-select v-model="sortData.sortType" filterable :placeholder="$t('hostList.selectPlaceholder')"
|
||||
style="width: 240px">
|
||||
<el-option v-for="item in sortNameOptions" :key="item.type" :label="item.label" :value="item.type" />
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<div><label>{{ $t('hostList.ascending') }}/{{ $t('hostList.descending') }}</label></div>
|
||||
|
||||
<el-select v-model="sortData.sortForm" filterable placeholder="请选择" style="width: 240px">
|
||||
<el-select v-model="sortData.sortForm" filterable :placeholder="$t('hostList.selectPlaceholder')"
|
||||
style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in [{ label: $t('hostList.ascending'), value: 'asc' }, { label: $t('hostList.descending'), value: 'desc' }]"
|
||||
:key="item.value" :label="item.label" :value="item.value" />
|
||||
@@ -150,6 +160,55 @@
|
||||
<LiveRecordDialog v-model:modelValue="liveDetailDialogVisible" :rows="liveDetailRecords"
|
||||
@select="handleLiveSelect" />
|
||||
|
||||
<el-dialog v-model="revenueDialogVisible" :title="$t('hostList.liveRevenue')" width="80vw" top="6vh"
|
||||
:close-on-click-modal="false" destroy-on-close>
|
||||
<el-table :data="revenueRecords" border height="62vh" style="width: 100%" v-loading="revenueLoading"
|
||||
table-layout="auto">
|
||||
<el-table-column prop="displayId" :label="$t('hostList.revenueHost')" />
|
||||
<el-table-column prop="todayRevenue" :label="$t('hostList.todayRevenueUsd')" />
|
||||
<el-table-column prop="totalRevenue" :label="$t('hostList.totalRevenueUsd')" />
|
||||
<el-table-column prop="lastDaysCount" :label="$t('hostList.liveDays')" />
|
||||
<el-table-column prop="history" :label="$t('hostList.historyRevenueUsd')">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip v-if="hasHistory(row.history)" effect="dark" placement="top">
|
||||
<template #content>{{ buildHistoryTitle(row.history) }}</template>
|
||||
<div class="history-sparkline-wrap">
|
||||
<div class="history-sparkline-top">
|
||||
<span class="history-sparkline-min-top">
|
||||
{{ $t('hostList.revenueLow') }}: {{ formatRevenueValue(getHistoryMin(row.history)) }}
|
||||
</span>
|
||||
<span class="history-sparkline-max">
|
||||
{{ $t('hostList.revenueHigh') }}: {{ formatRevenueValue(getHistoryMax(row.history)) }}
|
||||
</span>
|
||||
</div>
|
||||
<svg class="history-sparkline" viewBox="0 0 180 48" preserveAspectRatio="none">
|
||||
<line x1="2" y1="46" x2="178" y2="46" stroke="#e6eef7" stroke-width="1" />
|
||||
<polyline :points="buildSparklinePoints(row.history)" fill="none" stroke="#45a1ff" stroke-width="2" />
|
||||
</svg>
|
||||
<div class="history-sparkline-bottom">
|
||||
<div class="history-sparkline-dates">
|
||||
<span class="history-sparkline-date">{{ getHistoryStartDate(row.history) }}</span>
|
||||
<span class="history-sparkline-date">{{ getHistoryEndDate(row.history) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="updatedAt" label="updatedAt" /> -->
|
||||
<el-table-column prop="createdAt" :label="$t('hostList.revenueTime')">
|
||||
<template #default="scope">
|
||||
{{ formatTimestamp(scope.row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="revenueDialogVisible = false">{{ $t('hostList.close') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
@@ -157,7 +216,7 @@
|
||||
|
||||
<script setup>
|
||||
// import { getToken, setToken, removeToken } from '@/utils/storage'
|
||||
import { tkhostdata, dicts, tkhostdetail, downList, getStaffList, managerhosts, upholdinfo, getCountryinfo, accountName, liveHostDetail } from '@/api/account';
|
||||
import { tkhostdata, dicts, tkhostdetail, downList, getStaffList, managerhosts, upholdinfo, getCountryinfo, accountName, liveHostDetail, revenueStats } from '@/api/account';
|
||||
import { usePythonBridge, } from '@/utils/pythonBridge'
|
||||
import { getUser, setSerch, getSerch } from '@/utils/storage'
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
@@ -239,6 +298,9 @@ let commentHost = ref('')
|
||||
let liveDetailDialogVisible = ref(false)
|
||||
let liveDetailRecords = ref([])
|
||||
let liveDetailLoading = ref(false)
|
||||
let revenueDialogVisible = ref(false)
|
||||
let revenueRecords = ref([])
|
||||
let revenueLoading = ref(false)
|
||||
//分页
|
||||
let pageSize = ref(10)
|
||||
let page = ref(1)
|
||||
@@ -397,6 +459,146 @@ function handleLiveSelect(row) {
|
||||
liveDetailDialogVisible.value = false
|
||||
}
|
||||
|
||||
function getRevenueStats(hostId) {
|
||||
revenueLoading.value = true
|
||||
revenueStats(hostId).then(res => {
|
||||
const detailList = Array.isArray(res) ? res : (res?.records || [])
|
||||
revenueRecords.value = detailList
|
||||
revenueDialogVisible.value = true
|
||||
}).catch(err => {
|
||||
console.log('revenueStats error', err)
|
||||
}).finally(() => {
|
||||
revenueLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function parseHistoryItems(history) {
|
||||
if (!history) return []
|
||||
let arr = history
|
||||
if (typeof history === 'string') {
|
||||
try {
|
||||
arr = JSON.parse(history)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(arr)) return []
|
||||
return arr
|
||||
.map((item, index) => {
|
||||
if (typeof item === 'number') {
|
||||
return { date: `Day ${index + 1}`, value: item }
|
||||
}
|
||||
if (item && typeof item === 'object') {
|
||||
return {
|
||||
date: item.date ? String(item.date) : '',
|
||||
value: Number(item.value ?? 0),
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function hasHistory(history) {
|
||||
return parseHistoryItems(history).length > 0
|
||||
}
|
||||
|
||||
function getHistoryStats(history) {
|
||||
const items = parseHistoryItems(history)
|
||||
if (items.length === 0) {
|
||||
return { min: 0, max: 0, start: '', end: '' }
|
||||
}
|
||||
const values = items.map(item => item.value)
|
||||
return {
|
||||
min: Math.min(...values),
|
||||
max: Math.max(...values),
|
||||
start: items[0].date || '',
|
||||
end: items[items.length - 1].date || '',
|
||||
}
|
||||
}
|
||||
|
||||
function getHistoryMin(history) {
|
||||
return getHistoryStats(history).min
|
||||
}
|
||||
|
||||
function getHistoryMax(history) {
|
||||
return getHistoryStats(history).max
|
||||
}
|
||||
|
||||
function getHistoryStartDate(history) {
|
||||
return getHistoryStats(history).start
|
||||
}
|
||||
|
||||
function getHistoryEndDate(history) {
|
||||
return getHistoryStats(history).end
|
||||
}
|
||||
|
||||
function formatRevenueValue(value) {
|
||||
const num = Number(value)
|
||||
if (!Number.isFinite(num)) return ''
|
||||
const fixed = num.toFixed(3)
|
||||
return fixed.replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1')
|
||||
}
|
||||
|
||||
function buildHistoryTitle(history) {
|
||||
const items = parseHistoryItems(history)
|
||||
if (items.length === 0) return '-'
|
||||
return items.map(item => `${item.date}: ${formatRevenueValue(item.value)}`).join('\n')
|
||||
}
|
||||
|
||||
function buildSparklinePoints(history) {
|
||||
const items = parseHistoryItems(history)
|
||||
if (items.length === 0) return ''
|
||||
const values = items.map(item => item.value)
|
||||
const min = Math.min(...values)
|
||||
const max = Math.max(...values)
|
||||
const range = max - min || 1
|
||||
const width = 180
|
||||
const height = 48
|
||||
const padding = 2
|
||||
const step = items.length > 1 ? (width - padding * 2) / (items.length - 1) : 0
|
||||
return items.map((item, index) => {
|
||||
const x = padding + index * step
|
||||
const y = padding + (height - padding * 2) * (1 - (item.value - min) / range)
|
||||
return `${x},${y}`
|
||||
}).join(' ')
|
||||
}
|
||||
function formatTimestamp(value) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return ''
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return formatDate(value)
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
const ms = value < 1e12 ? value * 1000 : value
|
||||
return formatDate(new Date(ms))
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim()
|
||||
if (/^\d+$/.test(trimmed)) {
|
||||
const num = Number(trimmed)
|
||||
const ms = num < 1e12 ? num * 1000 : num
|
||||
return formatDate(new Date(ms))
|
||||
}
|
||||
const parsed = Date.parse(trimmed)
|
||||
if (!Number.isNaN(parsed)) {
|
||||
return formatDate(new Date(parsed))
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function formatDate(date) {
|
||||
const y = date.getFullYear()
|
||||
const m = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const d = String(date.getDate()).padStart(2, '0')
|
||||
const hh = String(date.getHours()).padStart(2, '0')
|
||||
const mm = String(date.getMinutes()).padStart(2, '0')
|
||||
const ss = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
|
||||
}
|
||||
//修改主播维护状态
|
||||
// function handleSelectChange(event, data) {
|
||||
|
||||
@@ -644,6 +846,51 @@ function openHTML(id) {
|
||||
</style>
|
||||
|
||||
<style scoped lang="less">
|
||||
.history-sparkline {
|
||||
width: 180px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.history-sparkline-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.history-sparkline-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
color: #1f5fa8;
|
||||
}
|
||||
|
||||
.history-sparkline-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
color: #7a8aa0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.history-sparkline-min-top {
|
||||
color: #4a5b73;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.history-sparkline-dates {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.history-sparkline-date {
|
||||
font-size: 12px;
|
||||
color: #7a8aa0;
|
||||
}
|
||||
|
||||
::v-deep(.el-input__wrapper) {
|
||||
background-color: #F2FAF9;
|
||||
border: 1px solid @bg-color;
|
||||
|
||||
@@ -128,13 +128,13 @@
|
||||
<div class="input-group">
|
||||
<label>{{ $t('workbenchesSetup.setNum') }}</label>
|
||||
<label style="color: #00000070; font-size: 15px;">({{ $t('workbenchesSetup.prompt')
|
||||
}})</label>
|
||||
}})</label>
|
||||
<el-button type="primary" @click="isLimit = true" :disabled="!pyData.isStart">{{
|
||||
$t('workbenchesSetup.setHostNum')
|
||||
}}</el-button>
|
||||
}}</el-button>
|
||||
<el-button type="info" @click="isLimit = false" :disabled="!pyData.isStart">{{
|
||||
$t('workbenchesSetup.unlimitedQuantity')
|
||||
}}</el-button>
|
||||
}}</el-button>
|
||||
<!-- <el-input type='number' v-model="pyData.frequency.hour" @input="handleInputHour" -->
|
||||
<div v-if="isLimit" class="center-justify">
|
||||
<el-input type='number' v-model="hostNum" :placeholder="$t('workbenchesSetup.num')"
|
||||
|
||||
Reference in New Issue
Block a user