|
@@ -137,8 +137,10 @@
|
|
|
<div class="title-cell">
|
|
<div class="title-cell">
|
|
|
<div class="work-title">{{ row.title }}</div>
|
|
<div class="work-title">{{ row.title }}</div>
|
|
|
<div class="work-stats">
|
|
<div class="work-stats">
|
|
|
|
|
+ <!-- 推荐暂不展示
|
|
|
<span class="stat-item">推荐 <em>{{ row.recommendCount ?? '--' }}</em></span>
|
|
<span class="stat-item">推荐 <em>{{ row.recommendCount ?? '--' }}</em></span>
|
|
|
- <span class="stat-item">阅读 <em>{{ row.viewsCount ?? 0 }}</em></span>
|
|
|
|
|
|
|
+ -->
|
|
|
|
|
+ <span class="stat-item">播放 <em>{{ row.viewsCount ?? 0 }}</em></span>
|
|
|
<span class="stat-item">评论 <em>{{ row.commentsCount ?? 0 }}</em></span>
|
|
<span class="stat-item">评论 <em>{{ row.commentsCount ?? 0 }}</em></span>
|
|
|
<span class="stat-item">分享 <em>{{ row.sharesCount ?? 0 }}</em></span>
|
|
<span class="stat-item">分享 <em>{{ row.sharesCount ?? 0 }}</em></span>
|
|
|
<span class="stat-item">收藏 <em>{{ row.collectsCount ?? 0 }}</em></span>
|
|
<span class="stat-item">收藏 <em>{{ row.collectsCount ?? 0 }}</em></span>
|
|
@@ -337,12 +339,14 @@ import { Search, Picture, Document, View, ChatDotRound, Share, Star, Pointer } f
|
|
|
import { PLATFORMS, AVAILABLE_PLATFORM_TYPES } from '@media-manager/shared';
|
|
import { PLATFORMS, AVAILABLE_PLATFORM_TYPES } from '@media-manager/shared';
|
|
|
import type { PlatformType } from '@media-manager/shared';
|
|
import type { PlatformType } from '@media-manager/shared';
|
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import { useAuthStore } from '@/stores/auth';
|
|
|
|
|
+import { useServerStore } from '@/stores/server';
|
|
|
import { ElMessage } from 'element-plus';
|
|
import { ElMessage } from 'element-plus';
|
|
|
import dayjs from 'dayjs';
|
|
import dayjs from 'dayjs';
|
|
|
import request from '@/api/request';
|
|
import request from '@/api/request';
|
|
|
import * as echarts from 'echarts';
|
|
import * as echarts from 'echarts';
|
|
|
|
|
|
|
|
const authStore = useAuthStore();
|
|
const authStore = useAuthStore();
|
|
|
|
|
+const serverStore = useServerStore();
|
|
|
const loading = ref(false);
|
|
const loading = ref(false);
|
|
|
|
|
|
|
|
// 日期筛选
|
|
// 日期筛选
|
|
@@ -402,7 +406,7 @@ const summaryData = ref({
|
|
|
// 统计卡片数据
|
|
// 统计卡片数据
|
|
|
const summaryStats = computed(() => [
|
|
const summaryStats = computed(() => [
|
|
|
{ label: '作品总数', value: summaryData.value.totalWorks, icon: Document },
|
|
{ label: '作品总数', value: summaryData.value.totalWorks, icon: Document },
|
|
|
- { label: '推荐量', value: summaryData.value.recommendCount, icon: Pointer },
|
|
|
|
|
|
|
+ // { label: '推荐量', value: summaryData.value.recommendCount, icon: Pointer },
|
|
|
{ label: '播放(阅读)量', value: summaryData.value.viewsCount, icon: View },
|
|
{ label: '播放(阅读)量', value: summaryData.value.viewsCount, icon: View },
|
|
|
{ label: '评论量', value: summaryData.value.commentsCount, icon: ChatDotRound },
|
|
{ label: '评论量', value: summaryData.value.commentsCount, icon: ChatDotRound },
|
|
|
{ label: '分享量', value: summaryData.value.sharesCount, icon: Share },
|
|
{ label: '分享量', value: summaryData.value.sharesCount, icon: Share },
|
|
@@ -505,7 +509,7 @@ const trendTitle = computed(() => {
|
|
|
if (!selectedWork.value) return '趋势';
|
|
if (!selectedWork.value) return '趋势';
|
|
|
if (selectedWork.value.platform !== 'xiaohongshu') {
|
|
if (selectedWork.value.platform !== 'xiaohongshu') {
|
|
|
const map: Record<TrendMetricKey, string> = {
|
|
const map: Record<TrendMetricKey, string> = {
|
|
|
- playCount: '播放量趋势',
|
|
|
|
|
|
|
+ playCount: '播放(阅读)量趋势',
|
|
|
totalWatchDuration: '播放总时长趋势',
|
|
totalWatchDuration: '播放总时长趋势',
|
|
|
likeCount: '点赞量趋势',
|
|
likeCount: '点赞量趋势',
|
|
|
commentCount: '评论量趋势',
|
|
commentCount: '评论量趋势',
|
|
@@ -996,9 +1000,66 @@ watch(drawerVisible, (visible) => {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-// 导出数据
|
|
|
|
|
-function handleExport() {
|
|
|
|
|
- ElMessage.info('导出功能开发中');
|
|
|
|
|
|
|
+// 导出数据(按当前筛选条件导出作品列表)
|
|
|
|
|
+async function handleExport() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const baseUrl = serverStore.currentServer?.url;
|
|
|
|
|
+ if (!baseUrl) {
|
|
|
|
|
+ ElMessage.error('未连接服务器');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!authStore.accessToken) {
|
|
|
|
|
+ ElMessage.error('未连接服务器或未登录');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const params = new URLSearchParams();
|
|
|
|
|
+ params.set('startDate', startDate.value);
|
|
|
|
|
+ params.set('endDate', endDate.value);
|
|
|
|
|
+ if (selectedPlatform.value) params.set('platform', selectedPlatform.value);
|
|
|
|
|
+ if (selectedAccounts.value.length > 0) params.set('accountIds', selectedAccounts.value.join(','));
|
|
|
|
|
+ if (selectedGroup.value) params.set('groupId', String(selectedGroup.value));
|
|
|
|
|
+ if (searchKeyword.value) params.set('keyword', searchKeyword.value);
|
|
|
|
|
+ params.set('sortBy', sortBy.value);
|
|
|
|
|
+
|
|
|
|
|
+ const url = `${baseUrl}/api/work-day-statistics/works/export?${params.toString()}`;
|
|
|
|
|
+
|
|
|
|
|
+ const doFetch = async (token: string) => {
|
|
|
|
|
+ return await fetch(url, {
|
|
|
|
|
+ method: 'GET',
|
|
|
|
|
+ headers: { Authorization: `Bearer ${token}` },
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let resp = await doFetch(authStore.accessToken!);
|
|
|
|
|
+ if (resp.status === 401) {
|
|
|
|
|
+ const refreshed = await authStore.refreshAccessToken();
|
|
|
|
|
+ if (!refreshed || !authStore.accessToken) {
|
|
|
|
|
+ ElMessage.error('登录已过期,请重新登录');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ resp = await doFetch(authStore.accessToken!);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!resp.ok) {
|
|
|
|
|
+ const text = await resp.text().catch(() => '');
|
|
|
|
|
+ throw new Error(text || `导出失败,状态码:${resp.status}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const blob = await resp.blob();
|
|
|
|
|
+ const downloadUrl = window.URL.createObjectURL(blob);
|
|
|
|
|
+ const a = document.createElement('a');
|
|
|
|
|
+ a.href = downloadUrl;
|
|
|
|
|
+ a.download = `作品数据_${dayjs().format('YYYYMMDD_HHmmss')}.xlsx`;
|
|
|
|
|
+ document.body.appendChild(a);
|
|
|
|
|
+ a.click();
|
|
|
|
|
+ a.remove();
|
|
|
|
|
+ window.URL.revokeObjectURL(downloadUrl);
|
|
|
|
|
+ ElMessage.success('导出成功');
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error('导出失败:', error);
|
|
|
|
|
+ ElMessage.error(error?.message || '导出失败');
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|