Files
ReelShortWeb/src/views/userManagement/WatchHistoryView.vue
T

243 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- Author: fruit.com.cn -->
<script setup>
import { onMounted, reactive, ref } from 'vue'
import PageHeading from '@/views/components/PageHeading.vue'
import { pageWatchHistoryApi } from '@/api/admin/appUser'
import { formatDuration, systemText } from './composables/useAppUsers'
const SOURCE_OPTIONS = [
{ value: '', label: '全部' },
{ value: 'HOME', label: '首页' },
{ value: 'DETAIL', label: '详情页' },
{ value: 'SEARCH', label: '搜索' },
{ value: 'FAVORITE', label: '收藏' }
]
const FINISHED_OPTIONS = [
{ value: '', label: '全部' },
{ value: true, label: '已看完' },
{ value: false, label: '未看完' }
]
const filters = reactive({
keyword: '',
dramaTitle: '',
source: '',
finished: '',
range: null
})
const rows = ref([])
const total = ref(0)
const loading = ref(false)
const pageIndex = ref(1)
const pageSize = ref(10)
async function load() {
loading.value = true
try {
const res = await pageWatchHistoryApi({
pageIndex: pageIndex.value,
pageSize: pageSize.value,
keyword: filters.keyword.trim() || undefined,
dramaTitle: filters.dramaTitle.trim() || undefined,
source: filters.source || undefined,
finished: filters.finished === '' ? undefined : filters.finished,
startTime: filters.range?.[0] || undefined,
endTime: filters.range?.[1] || undefined
})
rows.value = res.data?.list || []
total.value = res.data?.total || 0
} finally {
loading.value = false
}
}
function search() {
pageIndex.value = 1
load()
}
function resetFilters() {
filters.keyword = ''
filters.dramaTitle = ''
filters.source = ''
filters.finished = ''
filters.range = null
search()
}
function handleSizeChange() {
pageIndex.value = 1
load()
}
function sourceText(source) {
return SOURCE_OPTIONS.find((item) => item.value === source)?.label || source || '—'
}
function episodeText(row) {
if (row.episodeNo) return `第 ${row.episodeNo} 集`
return row.episodeId ? `分集 #${row.episodeId}` : '未关联分集'
}
function progressText(row) {
if (!row.durationMs) return '—'
return `${formatDuration(row.positionMs)} / ${formatDuration(row.durationMs)}`
}
function contentTooltip(row) {
return `短剧 ID:${row.dramaId} / 分集 ID:${row.episodeId || '未关联'}`
}
onMounted(load)
</script>
<template>
<PageHeading
eyebrow="用户管理"
title="观看记录"
description="跨用户检索观看流水,核对短剧、分集、进度与来源。"
/>
<section class="panel">
<div class="console-toolbar">
<el-input
v-model="filters.keyword"
class="console-keyword"
clearable
placeholder="uid / 昵称"
@keyup.enter="search"
@clear="search"
/>
<el-input
v-model="filters.dramaTitle"
class="console-keyword"
clearable
placeholder="短剧名称"
@keyup.enter="search"
@clear="search"
/>
<el-select
v-model="filters.source"
class="console-select"
placeholder="全部来源"
@change="search"
>
<el-option
v-for="item in SOURCE_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select
v-model="filters.finished"
class="console-select"
placeholder="全部完成状态"
@change="search"
>
<el-option
v-for="item in FINISHED_OPTIONS"
:key="String(item.value)"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-date-picker
v-model="filters.range"
class="watch-history-range"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change="search"
/>
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="resetFilters">重置</el-button>
<span class="console-count">共 {{ total }} 条</span>
</div>
<el-table
v-loading="loading"
:data="rows"
row-key="id"
border
class="drama-el-table"
empty-text="暂无符合条件的观看记录"
>
<el-table-column prop="watchedAt" label="观看时间" min-width="170" show-overflow-tooltip />
<el-table-column label="用户" min-width="180">
<template #default="{ row }">
<div class="app-user-meta">
<span class="app-user-name">{{ row.nickname || '未设置昵称' }}</span>
<span class="app-user-uid">{{ row.uid || `用户 #${row.userId || '—'}` }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="系统" width="130" show-overflow-tooltip>
<template #default="{ row }">{{ systemText(row) }}</template>
</el-table-column>
<el-table-column label="短剧" min-width="190">
<template #default="{ row }">
<el-tooltip :content="contentTooltip(row)" placement="top">
<div class="episode-table-title">
<b>{{ row.dramaTitle || `短剧 #${row.dramaId}` }}</b>
<el-tag v-if="row.dramaPublished === false" type="info" size="small" effect="plain"
>已下线</el-tag
>
</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="分集" min-width="180" show-overflow-tooltip>
<template #default="{ row }">
<div class="episode-table-title">
<b>{{ episodeText(row) }}</b>
<small v-if="row.episodeTitle">{{ row.episodeTitle }}</small>
</div>
</template>
</el-table-column>
<el-table-column label="进度" min-width="120">
<template #default="{ row }">
<span class="app-user-mono">{{ progressText(row) }}</span>
</template>
</el-table-column>
<el-table-column label="完成" width="92">
<template #default="{ row }">
<el-tag :type="row.finished ? 'success' : 'info'" effect="light">
{{ row.finished ? '已看完' : '未看完' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="来源" width="96">
<template #default="{ row }">{{ sourceText(row.source) }}</template>
</el-table-column>
<el-table-column prop="deviceId" label="设备" min-width="150" show-overflow-tooltip>
<template #default="{ row }">{{ row.deviceId || '—' }}</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageIndex"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
background
@current-change="load"
@size-change="handleSizeChange"
/>
</div>
</section>
</template>
<style scoped>
.watch-history-range {
width: 360px;
max-width: 100%;
}
</style>