登录页优化

This commit is contained in:
2026-01-16 13:35:17 +08:00
parent 62dd79a2fc
commit 74bf170490
9 changed files with 922 additions and 240 deletions

View File

@@ -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;

View File

@@ -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')"