|
@@ -154,13 +154,13 @@ import { Search } from '@element-plus/icons-vue';
|
|
|
import { PLATFORMS } from '@media-manager/shared';
|
|
import { PLATFORMS } 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';
|
|
|
|
|
|
|
|
-const PYTHON_API_URL = 'http://localhost:5005';
|
|
|
|
|
-
|
|
|
|
|
const authStore = useAuthStore();
|
|
const authStore = useAuthStore();
|
|
|
|
|
+const serverStore = useServerStore();
|
|
|
const loading = ref(false);
|
|
const loading = ref(false);
|
|
|
const refreshing = ref(false);
|
|
const refreshing = ref(false);
|
|
|
|
|
|
|
@@ -315,37 +315,32 @@ async function loadData() {
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- const queryParams = new URLSearchParams({
|
|
|
|
|
- user_id: userId.toString(),
|
|
|
|
|
|
|
+ // 改为直接走 Node 服务(/api/...),避免依赖本地 Python 端口(:5005)
|
|
|
|
|
+ const data = await request.get('/api/work-day-statistics/overview', {
|
|
|
|
|
+ params: { user_id: userId }, // 兼容历史参数;后端会忽略并以 token 用户为准
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
- const response = await fetch(`${PYTHON_API_URL}/work_day_statistics/overview?${queryParams}`);
|
|
|
|
|
- const result = await response.json();
|
|
|
|
|
-
|
|
|
|
|
- if (result.success && result.data) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (data) {
|
|
|
// 确保只保留支持的平台
|
|
// 确保只保留支持的平台
|
|
|
const allowedPlatforms: PlatformType[] = ['douyin', 'baijiahao', 'weixin_video', 'xiaohongshu'];
|
|
const allowedPlatforms: PlatformType[] = ['douyin', 'baijiahao', 'weixin_video', 'xiaohongshu'];
|
|
|
- accounts.value = (result.data.accounts || []).filter((a: AccountData) =>
|
|
|
|
|
|
|
+ accounts.value = (data.accounts || []).filter((a: AccountData) =>
|
|
|
allowedPlatforms.includes(a.platform)
|
|
allowedPlatforms.includes(a.platform)
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
// 使用后端返回的汇总数据
|
|
// 使用后端返回的汇总数据
|
|
|
- if (result.data.summary) {
|
|
|
|
|
|
|
+ if (data.summary) {
|
|
|
summaryData.value = {
|
|
summaryData.value = {
|
|
|
- totalAccounts: result.data.summary.totalAccounts || 0,
|
|
|
|
|
- totalIncome: result.data.summary.totalIncome || 0,
|
|
|
|
|
- yesterdayIncome: result.data.summary.yesterdayIncome || 0,
|
|
|
|
|
- totalViews: result.data.summary.totalViews || 0,
|
|
|
|
|
- yesterdayViews: result.data.summary.yesterdayViews || 0,
|
|
|
|
|
- totalFans: result.data.summary.totalFans || 0,
|
|
|
|
|
- yesterdayComments: result.data.summary.yesterdayComments || 0,
|
|
|
|
|
- yesterdayLikes: result.data.summary.yesterdayLikes || 0,
|
|
|
|
|
- yesterdayFansIncrease: result.data.summary.yesterdayFansIncrease || 0,
|
|
|
|
|
|
|
+ totalAccounts: data.summary.totalAccounts || 0,
|
|
|
|
|
+ totalIncome: data.summary.totalIncome || 0,
|
|
|
|
|
+ yesterdayIncome: data.summary.yesterdayIncome || 0,
|
|
|
|
|
+ totalViews: data.summary.totalViews || 0,
|
|
|
|
|
+ yesterdayViews: data.summary.yesterdayViews || 0,
|
|
|
|
|
+ totalFans: data.summary.totalFans || 0,
|
|
|
|
|
+ yesterdayComments: data.summary.yesterdayComments || 0,
|
|
|
|
|
+ yesterdayLikes: data.summary.yesterdayLikes || 0,
|
|
|
|
|
+ yesterdayFansIncrease: data.summary.yesterdayFansIncrease || 0,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- console.error('加载数据失败:', result.error || '未知错误');
|
|
|
|
|
- ElMessage.error(result.error || '加载数据失败');
|
|
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('加载数据失败:', error);
|
|
console.error('加载数据失败:', error);
|
|
@@ -372,24 +367,11 @@ async function handleRefreshAccount(account: AccountData) {
|
|
|
try {
|
|
try {
|
|
|
const userId = authStore.user?.id;
|
|
const userId = authStore.user?.id;
|
|
|
if (!userId) return;
|
|
if (!userId) return;
|
|
|
-
|
|
|
|
|
- const queryParams = new URLSearchParams({
|
|
|
|
|
- user_id: userId.toString(),
|
|
|
|
|
- account_id: account.id.toString(),
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const response = await fetch(`${PYTHON_API_URL}/work_day_statistics/refresh_account?${queryParams}`, {
|
|
|
|
|
- method: 'POST',
|
|
|
|
|
- });
|
|
|
|
|
- const result = await response.json();
|
|
|
|
|
-
|
|
|
|
|
- if (result.success) {
|
|
|
|
|
- // 更新账号数据
|
|
|
|
|
- Object.assign(account, result.data);
|
|
|
|
|
- ElMessage.success('账号数据刷新成功');
|
|
|
|
|
- } else {
|
|
|
|
|
- ElMessage.error(result.error || '刷新失败');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 直接复用 Node 现有刷新接口
|
|
|
|
|
+ const data = await request.post(`/api/accounts/${account.id}/refresh`);
|
|
|
|
|
+ Object.assign(account, data);
|
|
|
|
|
+ ElMessage.success('账号数据刷新成功');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
ElMessage.error('刷新失败');
|
|
ElMessage.error('刷新失败');
|
|
|
} finally {
|
|
} finally {
|
|
@@ -398,8 +380,67 @@ async function handleRefreshAccount(account: AccountData) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 导出数据
|
|
// 导出数据
|
|
|
-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 buildUrl = () => {
|
|
|
|
|
+ const params = new URLSearchParams();
|
|
|
|
|
+ if (selectedGroup.value) params.set('groupId', String(selectedGroup.value));
|
|
|
|
|
+ if (selectedPlatform.value) params.set('platform', String(selectedPlatform.value));
|
|
|
|
|
+ if (searchKeyword.value) params.set('keyword', searchKeyword.value);
|
|
|
|
|
+ return `${baseUrl}/api/work-day-statistics/overview/export?${params.toString()}`;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const doFetch = async (token: string) => {
|
|
|
|
|
+ const url = buildUrl();
|
|
|
|
|
+ return await fetch(url, {
|
|
|
|
|
+ method: 'GET',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ Authorization: `Bearer ${token}`,
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let resp = await doFetch(authStore.accessToken!);
|
|
|
|
|
+
|
|
|
|
|
+ // token 过期时,手动触发刷新逻辑并重试一次
|
|
|
|
|
+ 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);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error('导出失败:', error);
|
|
|
|
|
+ ElMessage.error(error?.message || '导出失败');
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|