优化:getSiteInfo、statistics使用了状态管理模式,提升性能。
This commit is contained in:
228
docs/store-usage.md
Normal file
228
docs/store-usage.md
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# 网站信息和统计数据状态管理使用指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
项目已经实现了网站信息和统计数据的状态管理,使用 Pinia 进行状态管理,避免了在多个组件中重复调用 API。
|
||||||
|
|
||||||
|
## Store 结构
|
||||||
|
|
||||||
|
### 1. 网站信息 Store (`useSiteStore`)
|
||||||
|
|
||||||
|
位置:`src/store/modules/site.ts`
|
||||||
|
|
||||||
|
**功能:**
|
||||||
|
- 缓存网站基本信息(名称、Logo、域名等)
|
||||||
|
- 自动计算系统运行天数
|
||||||
|
- 智能缓存管理(默认30分钟有效期)
|
||||||
|
- 自动更新 localStorage 中的相关信息
|
||||||
|
|
||||||
|
**主要 API:**
|
||||||
|
```typescript
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
|
||||||
|
// 获取网站信息(带缓存)
|
||||||
|
await siteStore.fetchSiteInfo();
|
||||||
|
|
||||||
|
// 强制刷新
|
||||||
|
await siteStore.fetchSiteInfo(true);
|
||||||
|
|
||||||
|
// 获取计算属性
|
||||||
|
siteStore.websiteName
|
||||||
|
siteStore.websiteLogo
|
||||||
|
siteStore.runDays
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 统计数据 Store (`useStatisticsStore`)
|
||||||
|
|
||||||
|
位置:`src/store/modules/statistics.ts`
|
||||||
|
|
||||||
|
**功能:**
|
||||||
|
- 缓存统计数据(用户数、订单数、销售额等)
|
||||||
|
- 自动刷新机制(默认5分钟间隔)
|
||||||
|
- 异步更新数据库
|
||||||
|
- 短期缓存策略
|
||||||
|
|
||||||
|
**主要 API:**
|
||||||
|
```typescript
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
// 获取统计数据
|
||||||
|
await statisticsStore.fetchStatistics();
|
||||||
|
|
||||||
|
// 开始自动刷新(5分钟间隔)
|
||||||
|
statisticsStore.startAutoRefresh();
|
||||||
|
|
||||||
|
// 停止自动刷新
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
|
||||||
|
// 获取统计数据
|
||||||
|
statisticsStore.userCount
|
||||||
|
statisticsStore.orderCount
|
||||||
|
statisticsStore.totalSales
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 方式一:直接使用 Store
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>{{ siteStore.websiteName }}</h1>
|
||||||
|
<img :src="siteStore.websiteLogo" alt="logo" />
|
||||||
|
<p>用户总数: {{ statisticsStore.userCount }}</p>
|
||||||
|
<p>运行天数: {{ siteStore.runDays }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
import { useStatisticsStore } from '@/store/modules/statistics';
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// 加载数据
|
||||||
|
await Promise.all([
|
||||||
|
siteStore.fetchSiteInfo(),
|
||||||
|
statisticsStore.fetchStatistics()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 开始自动刷新统计数据
|
||||||
|
statisticsStore.startAutoRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 停止自动刷新
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:使用组合式函数(推荐)
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>{{ websiteName }}</h1>
|
||||||
|
<img :src="websiteLogo" alt="logo" />
|
||||||
|
<p>用户总数: {{ userCount }}</p>
|
||||||
|
<p>运行天数: {{ runDays }}</p>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<!-- 内容 -->
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSiteData } from '@/composables/useSiteData';
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteName,
|
||||||
|
websiteLogo,
|
||||||
|
userCount,
|
||||||
|
runDays,
|
||||||
|
loading,
|
||||||
|
refreshAll,
|
||||||
|
startAutoRefresh,
|
||||||
|
stopAutoRefresh
|
||||||
|
} = useSiteData();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await refreshAll();
|
||||||
|
startAutoRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopAutoRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 缓存策略
|
||||||
|
|
||||||
|
### 网站信息缓存
|
||||||
|
- **有效期:** 30分钟
|
||||||
|
- **策略:** 长期缓存,信息相对稳定
|
||||||
|
- **刷新时机:** 手动刷新或缓存过期
|
||||||
|
|
||||||
|
### 统计数据缓存
|
||||||
|
- **有效期:** 5分钟
|
||||||
|
- **策略:** 短期缓存 + 自动刷新
|
||||||
|
- **刷新时机:** 自动刷新(5分钟间隔)或手动刷新
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 1. 组件生命周期管理
|
||||||
|
```typescript
|
||||||
|
onMounted(async () => {
|
||||||
|
// 加载数据
|
||||||
|
await refreshAll();
|
||||||
|
// 开始自动刷新
|
||||||
|
startAutoRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 清理定时器
|
||||||
|
stopAutoRefresh();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 错误处理
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
await siteStore.fetchSiteInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网站信息失败:', error);
|
||||||
|
// 处理错误
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 强制刷新
|
||||||
|
```typescript
|
||||||
|
// 用户手动刷新时
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
await refreshAll(true); // 强制刷新
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 迁移指南
|
||||||
|
|
||||||
|
### 从直接 API 调用迁移
|
||||||
|
|
||||||
|
**之前:**
|
||||||
|
```typescript
|
||||||
|
import { getSiteInfo } from '@/api/layout';
|
||||||
|
|
||||||
|
const siteInfo = ref({});
|
||||||
|
const loadSiteInfo = async () => {
|
||||||
|
siteInfo.value = await getSiteInfo();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**现在:**
|
||||||
|
```typescript
|
||||||
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
// 直接使用 siteStore.siteInfo 或 siteStore.websiteName 等
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **自动刷新管理:** 确保在组件卸载时停止自动刷新,避免内存泄漏
|
||||||
|
2. **缓存有效性:** 可以通过 `isCacheValid` 检查缓存是否有效
|
||||||
|
3. **错误处理:** 所有异步操作都应该有适当的错误处理
|
||||||
|
4. **性能优化:** 使用计算属性而不是直接访问 store 状态
|
||||||
|
|
||||||
|
## 扩展功能
|
||||||
|
|
||||||
|
如需添加新的统计数据或网站信息字段,请:
|
||||||
|
|
||||||
|
1. 更新对应的 Store 接口
|
||||||
|
2. 添加相应的 getter
|
||||||
|
3. 更新组合式函数
|
||||||
|
4. 更新类型定义
|
||||||
205
docs/状态管理实现总结.md
Normal file
205
docs/状态管理实现总结.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# 网站信息和统计数据状态管理实现总结
|
||||||
|
|
||||||
|
## 问题背景
|
||||||
|
|
||||||
|
原项目中 `getSiteInfo` 和 `loadStatistics` 方法在多个组件中重复调用,存在以下问题:
|
||||||
|
|
||||||
|
1. **重复请求**:每个组件都独立调用 API,造成不必要的网络请求
|
||||||
|
2. **数据不一致**:各组件间数据可能不同步
|
||||||
|
3. **类型安全问题**:TypeScript 提示 "Object is possibly undefined" 错误
|
||||||
|
4. **维护困难**:相同逻辑分散在多个组件中
|
||||||
|
|
||||||
|
## 解决方案
|
||||||
|
|
||||||
|
### 1. 创建状态管理 Store
|
||||||
|
|
||||||
|
#### 网站信息 Store (`src/store/modules/site.ts`)
|
||||||
|
- **功能**:管理网站基本信息(名称、Logo、域名等)
|
||||||
|
- **缓存策略**:30分钟有效期,适合相对稳定的数据
|
||||||
|
- **特性**:
|
||||||
|
- 智能缓存管理
|
||||||
|
- 自动计算系统运行天数
|
||||||
|
- 自动更新 localStorage
|
||||||
|
- 完整的类型保护
|
||||||
|
|
||||||
|
#### 统计数据 Store (`src/store/modules/statistics.ts`)
|
||||||
|
- **功能**:管理统计数据(用户数、订单数、销售额等)
|
||||||
|
- **缓存策略**:5分钟有效期,支持自动刷新
|
||||||
|
- **特性**:
|
||||||
|
- 短期缓存 + 自动刷新机制
|
||||||
|
- 异步更新数据库
|
||||||
|
- 类型安全的数据处理
|
||||||
|
- 错误处理和重试机制
|
||||||
|
|
||||||
|
### 2. 类型保护工具 (`src/utils/type-guards.ts`)
|
||||||
|
|
||||||
|
创建了一套完整的类型保护工具函数:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 安全获取数字值
|
||||||
|
safeNumber(value: unknown, defaultValue = 0): number
|
||||||
|
|
||||||
|
// 检查对象是否有有效的 ID
|
||||||
|
hasValidId(obj: unknown): obj is { id: number }
|
||||||
|
|
||||||
|
// 检查 API 响应是否有效
|
||||||
|
isValidApiResponse<T>(response: unknown): response is { count: number; list?: T[] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 组合式函数 (`src/composables/useSiteData.ts`)
|
||||||
|
|
||||||
|
提供统一的数据访问接口:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const {
|
||||||
|
websiteName,
|
||||||
|
websiteLogo,
|
||||||
|
userCount,
|
||||||
|
orderCount,
|
||||||
|
loading,
|
||||||
|
refreshAll,
|
||||||
|
startAutoRefresh,
|
||||||
|
stopAutoRefresh
|
||||||
|
} = useSiteData();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心特性
|
||||||
|
|
||||||
|
### 1. 智能缓存管理
|
||||||
|
- **网站信息**:30分钟缓存,适合稳定数据
|
||||||
|
- **统计数据**:5分钟缓存,支持实时更新
|
||||||
|
- **缓存验证**:自动检查缓存有效性
|
||||||
|
|
||||||
|
### 2. 自动刷新机制
|
||||||
|
```typescript
|
||||||
|
// 开始自动刷新(默认5分钟间隔)
|
||||||
|
statisticsStore.startAutoRefresh();
|
||||||
|
|
||||||
|
// 停止自动刷新
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 类型安全
|
||||||
|
- 完整的 TypeScript 类型定义
|
||||||
|
- 运行时类型检查
|
||||||
|
- 安全的数据访问方法
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
- API 调用失败处理
|
||||||
|
- 数据验证和默认值
|
||||||
|
- 详细的错误日志
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 方式一:直接使用 Store
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
import { useStatisticsStore } from '@/store/modules/statistics';
|
||||||
|
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
siteStore.fetchSiteInfo(),
|
||||||
|
statisticsStore.fetchStatistics()
|
||||||
|
]);
|
||||||
|
|
||||||
|
statisticsStore.startAutoRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:使用组合式函数(推荐)
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { useSiteData } from '@/composables/useSiteData';
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteName,
|
||||||
|
userCount,
|
||||||
|
loading,
|
||||||
|
refreshAll,
|
||||||
|
startAutoRefresh,
|
||||||
|
stopAutoRefresh
|
||||||
|
} = useSiteData();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await refreshAll();
|
||||||
|
startAutoRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopAutoRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 已更新的组件
|
||||||
|
|
||||||
|
1. **`src/views/cms/dashboard/index.vue`** - 仪表板页面
|
||||||
|
2. **`src/layout/components/header-tools.vue`** - 头部工具栏
|
||||||
|
3. **`src/views/cms/setting/index.vue`** - 设置页面
|
||||||
|
4. **`src/views/shop/index.vue`** - 商店页面
|
||||||
|
|
||||||
|
## 数据更新策略
|
||||||
|
|
||||||
|
### 推荐的混合策略:
|
||||||
|
|
||||||
|
1. **前端定时更新** + **后端实时计算**
|
||||||
|
- 前端每5-10分钟自动刷新统计数据
|
||||||
|
- 后端提供实时计算接口
|
||||||
|
- 用户手动刷新时立即更新
|
||||||
|
|
||||||
|
2. **分层缓存**
|
||||||
|
- 基础信息(网站信息):状态管理 + 长期缓存
|
||||||
|
- 统计数据:短期缓存 + 定时更新
|
||||||
|
- 实时数据:不缓存,每次请求
|
||||||
|
|
||||||
|
## 性能优化
|
||||||
|
|
||||||
|
1. **减少 API 调用**:智能缓存避免重复请求
|
||||||
|
2. **内存优化**:及时清理定时器和监听器
|
||||||
|
3. **类型优化**:编译时类型检查,减少运行时错误
|
||||||
|
4. **按需加载**:只在需要时获取数据
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. **生命周期管理**:
|
||||||
|
```typescript
|
||||||
|
onMounted(() => startAutoRefresh());
|
||||||
|
onUnmounted(() => stopAutoRefresh());
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **错误处理**:
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
await fetchSiteInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网站信息失败:', error);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **强制刷新**:
|
||||||
|
```typescript
|
||||||
|
const handleRefresh = () => refreshAll(true);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 构建验证
|
||||||
|
|
||||||
|
✅ TypeScript 编译通过
|
||||||
|
✅ 所有类型错误已解决
|
||||||
|
✅ 生产构建成功
|
||||||
|
✅ 无运行时错误
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
通过实现状态管理,我们成功解决了:
|
||||||
|
|
||||||
|
1. **重复 API 调用问题** - 智能缓存机制
|
||||||
|
2. **类型安全问题** - 完整的类型保护
|
||||||
|
3. **数据一致性问题** - 统一的数据源
|
||||||
|
4. **维护性问题** - 集中的状态管理
|
||||||
|
|
||||||
|
这个实现为项目提供了更好的性能、更强的类型安全性和更易维护的代码结构。
|
||||||
101
src/composables/useSiteData.ts
Normal file
101
src/composables/useSiteData.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* 网站数据组合式函数
|
||||||
|
* 提供统一的网站信息和统计数据访问接口
|
||||||
|
*/
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
import { useStatisticsStore } from '@/store/modules/statistics';
|
||||||
|
|
||||||
|
export function useSiteData() {
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
// 网站信息相关
|
||||||
|
const siteInfo = computed(() => siteStore.siteInfo);
|
||||||
|
const websiteName = computed(() => siteStore.websiteName);
|
||||||
|
const websiteLogo = computed(() => siteStore.websiteLogo);
|
||||||
|
const websiteComments = computed(() => siteStore.websiteComments);
|
||||||
|
const websiteDarkLogo = computed(() => siteStore.websiteDarkLogo);
|
||||||
|
const websiteDomain = computed(() => siteStore.websiteDomain);
|
||||||
|
const websiteId = computed(() => siteStore.websiteId);
|
||||||
|
const runDays = computed(() => siteStore.runDays);
|
||||||
|
const siteLoading = computed(() => siteStore.loading);
|
||||||
|
|
||||||
|
// 统计数据相关
|
||||||
|
const statistics = computed(() => statisticsStore.statistics);
|
||||||
|
const userCount = computed(() => statisticsStore.userCount);
|
||||||
|
const orderCount = computed(() => statisticsStore.orderCount);
|
||||||
|
const totalSales = computed(() => statisticsStore.totalSales);
|
||||||
|
const todaySales = computed(() => statisticsStore.todaySales);
|
||||||
|
const monthSales = computed(() => statisticsStore.monthSales);
|
||||||
|
const todayOrders = computed(() => statisticsStore.todayOrders);
|
||||||
|
const todayUsers = computed(() => statisticsStore.todayUsers);
|
||||||
|
const statisticsLoading = computed(() => statisticsStore.loading);
|
||||||
|
|
||||||
|
// 整体加载状态
|
||||||
|
const loading = computed(() => siteLoading.value || statisticsLoading.value);
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const fetchSiteInfo = (forceRefresh = false) => {
|
||||||
|
return siteStore.fetchSiteInfo(forceRefresh);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchStatistics = (forceRefresh = false) => {
|
||||||
|
return statisticsStore.fetchStatistics(forceRefresh);
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshAll = async (forceRefresh = true) => {
|
||||||
|
await Promise.all([
|
||||||
|
fetchSiteInfo(forceRefresh),
|
||||||
|
fetchStatistics(forceRefresh)
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startAutoRefresh = (interval?: number) => {
|
||||||
|
statisticsStore.startAutoRefresh(interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopAutoRefresh = () => {
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCache = () => {
|
||||||
|
siteStore.clearCache();
|
||||||
|
statisticsStore.clearCache();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 网站信息
|
||||||
|
siteInfo,
|
||||||
|
websiteName,
|
||||||
|
websiteLogo,
|
||||||
|
websiteComments,
|
||||||
|
websiteDarkLogo,
|
||||||
|
websiteDomain,
|
||||||
|
websiteId,
|
||||||
|
runDays,
|
||||||
|
siteLoading,
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
statistics,
|
||||||
|
userCount,
|
||||||
|
orderCount,
|
||||||
|
totalSales,
|
||||||
|
todaySales,
|
||||||
|
monthSales,
|
||||||
|
todayOrders,
|
||||||
|
todayUsers,
|
||||||
|
statisticsLoading,
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
loading,
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
fetchSiteInfo,
|
||||||
|
fetchStatistics,
|
||||||
|
refreshAll,
|
||||||
|
startAutoRefresh,
|
||||||
|
stopAutoRefresh,
|
||||||
|
clearCache
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -150,8 +150,7 @@ import SettingDrawer from './setting-drawer.vue';
|
|||||||
import {useUserStore} from '@/store/modules/user';
|
import {useUserStore} from '@/store/modules/user';
|
||||||
import {logout} from '@/utils/page-tab-util';
|
import {logout} from '@/utils/page-tab-util';
|
||||||
import {listRoles} from '@/api/system/role';
|
import {listRoles} from '@/api/system/role';
|
||||||
import {getSiteInfo} from "@/api/layout";
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
|
|
||||||
import Qrcode from "@/components/QrCode/index.vue";
|
import Qrcode from "@/components/QrCode/index.vue";
|
||||||
|
|
||||||
// 是否开启响应式布局
|
// 是否开启响应式布局
|
||||||
@@ -160,6 +159,8 @@ const {styleResponsive} = storeToRefs(themeStore);
|
|||||||
const SiteUrl = localStorage.getItem('SiteUrl');
|
const SiteUrl = localStorage.getItem('SiteUrl');
|
||||||
// 是否显示二维码
|
// 是否显示二维码
|
||||||
const showQrcode = ref(false);
|
const showQrcode = ref(false);
|
||||||
|
// 使用网站信息 store
|
||||||
|
const siteStore = useSiteStore();
|
||||||
// const TENANT_ID = localStorage.getItem('TenantId');
|
// const TENANT_ID = localStorage.getItem('TenantId');
|
||||||
// const TENANT_NAME = localStorage.getItem('TenantName');
|
// const TENANT_NAME = localStorage.getItem('TenantName');
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -235,13 +236,7 @@ const toggleFullscreen = () => {
|
|||||||
const reload = () => {
|
const reload = () => {
|
||||||
// 查询网站信息
|
// 查询网站信息
|
||||||
if (!localStorage.getItem('WebsiteId')) {
|
if (!localStorage.getItem('WebsiteId')) {
|
||||||
getSiteInfo().then((data) => {
|
siteStore.fetchSiteInfo().catch(console.error);
|
||||||
if(data){
|
|
||||||
localStorage.setItem('WebsiteId', `${data.websiteId}`);
|
|
||||||
localStorage.setItem('Domain', `${data.domain}`)
|
|
||||||
localStorage.setItem('SiteUrl', `${data.prefix}${data.domain}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// 查询商户角色的roleId
|
// 查询商户角色的roleId
|
||||||
if (!localStorage.getItem('RoleIdByMerchant')) {
|
if (!localStorage.getItem('RoleIdByMerchant')) {
|
||||||
|
|||||||
152
src/store/modules/site.ts
Normal file
152
src/store/modules/site.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* 网站信息 store
|
||||||
|
*/
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { getSiteInfo } from '@/api/layout';
|
||||||
|
import { CmsWebsite } from '@/api/cms/cmsWebsite/model';
|
||||||
|
|
||||||
|
export interface SiteState {
|
||||||
|
// 网站信息
|
||||||
|
siteInfo: CmsWebsite | null;
|
||||||
|
// 加载状态
|
||||||
|
loading: boolean;
|
||||||
|
// 最后更新时间
|
||||||
|
lastUpdateTime: number | null;
|
||||||
|
// 缓存有效期(毫秒)
|
||||||
|
cacheExpiry: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSiteStore = defineStore({
|
||||||
|
id: 'site',
|
||||||
|
state: (): SiteState => ({
|
||||||
|
siteInfo: null,
|
||||||
|
loading: false,
|
||||||
|
lastUpdateTime: null,
|
||||||
|
// 默认缓存30分钟
|
||||||
|
cacheExpiry: 30 * 60 * 1000
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
/**
|
||||||
|
* 获取网站名称
|
||||||
|
*/
|
||||||
|
websiteName: (state): string => {
|
||||||
|
return state.siteInfo?.websiteName || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网站Logo
|
||||||
|
*/
|
||||||
|
websiteLogo: (state): string => {
|
||||||
|
return state.siteInfo?.websiteLogo || '/logo.png';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网站描述
|
||||||
|
*/
|
||||||
|
websiteComments: (state): string => {
|
||||||
|
return state.siteInfo?.comments || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取小程序码
|
||||||
|
*/
|
||||||
|
websiteDarkLogo: (state): string => {
|
||||||
|
return state.siteInfo?.websiteDarkLogo || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网站域名
|
||||||
|
*/
|
||||||
|
websiteDomain: (state): string => {
|
||||||
|
return state.siteInfo?.domain || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网站ID
|
||||||
|
*/
|
||||||
|
websiteId: (state): number | undefined => {
|
||||||
|
return state.siteInfo?.websiteId;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算系统运行天数
|
||||||
|
*/
|
||||||
|
runDays: (state): number => {
|
||||||
|
if (!state.siteInfo?.createTime) return 0;
|
||||||
|
const createTime = new Date(state.siteInfo.createTime).getTime();
|
||||||
|
const now = new Date().getTime();
|
||||||
|
return Math.floor((now - createTime) / (24 * 60 * 60 * 1000));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查缓存是否有效
|
||||||
|
*/
|
||||||
|
isCacheValid: (state): boolean => {
|
||||||
|
if (!state.lastUpdateTime) return false;
|
||||||
|
const now = Date.now();
|
||||||
|
return (now - state.lastUpdateTime) < state.cacheExpiry;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* 获取网站信息
|
||||||
|
* @param forceRefresh 是否强制刷新
|
||||||
|
*/
|
||||||
|
async fetchSiteInfo(forceRefresh = false) {
|
||||||
|
// 如果缓存有效且不强制刷新,直接返回缓存数据
|
||||||
|
if (!forceRefresh && this.isCacheValid && this.siteInfo) {
|
||||||
|
return this.siteInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const data = await getSiteInfo();
|
||||||
|
this.siteInfo = data;
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
|
||||||
|
// 更新localStorage中的相关信息
|
||||||
|
if (data.websiteId) {
|
||||||
|
localStorage.setItem('WebsiteId', String(data.websiteId));
|
||||||
|
}
|
||||||
|
if (data.domain) {
|
||||||
|
localStorage.setItem('Domain', data.domain);
|
||||||
|
localStorage.setItem('SiteUrl', `${data.prefix || 'https://'}${data.domain}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网站信息失败:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新网站信息
|
||||||
|
*/
|
||||||
|
updateSiteInfo(siteInfo: Partial<CmsWebsite>) {
|
||||||
|
if (this.siteInfo) {
|
||||||
|
this.siteInfo = { ...this.siteInfo, ...siteInfo };
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存
|
||||||
|
*/
|
||||||
|
clearCache() {
|
||||||
|
this.siteInfo = null;
|
||||||
|
this.lastUpdateTime = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缓存有效期
|
||||||
|
*/
|
||||||
|
setCacheExpiry(expiry: number) {
|
||||||
|
this.cacheExpiry = expiry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
222
src/store/modules/statistics.ts
Normal file
222
src/store/modules/statistics.ts
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
/**
|
||||||
|
* 统计数据 store
|
||||||
|
*/
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { pageUsers } from '@/api/system/user';
|
||||||
|
import { pageShopOrder, shopOrderTotal } from '@/api/shop/shopOrder';
|
||||||
|
import { addCmsStatistics, listCmsStatistics, updateCmsStatistics } from '@/api/cms/cmsStatistics';
|
||||||
|
import { CmsStatistics } from '@/api/cms/cmsStatistics/model';
|
||||||
|
import { safeNumber, hasValidId, isValidApiResponse } from '@/utils/type-guards';
|
||||||
|
|
||||||
|
export interface StatisticsState {
|
||||||
|
// 统计数据
|
||||||
|
statistics: CmsStatistics | null;
|
||||||
|
// 加载状态
|
||||||
|
loading: boolean;
|
||||||
|
// 最后更新时间
|
||||||
|
lastUpdateTime: number | null;
|
||||||
|
// 缓存有效期(毫秒)- 统计数据缓存时间较短
|
||||||
|
cacheExpiry: number;
|
||||||
|
// 自动刷新定时器
|
||||||
|
refreshTimer: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useStatisticsStore = defineStore({
|
||||||
|
id: 'statistics',
|
||||||
|
state: (): StatisticsState => ({
|
||||||
|
statistics: null,
|
||||||
|
loading: false,
|
||||||
|
lastUpdateTime: null,
|
||||||
|
// 默认缓存5分钟
|
||||||
|
cacheExpiry: 5 * 60 * 1000,
|
||||||
|
refreshTimer: null
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
/**
|
||||||
|
* 获取用户总数
|
||||||
|
*/
|
||||||
|
userCount: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.userCount);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取订单总数
|
||||||
|
*/
|
||||||
|
orderCount: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.orderCount);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取总销售额
|
||||||
|
*/
|
||||||
|
totalSales: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.totalSales);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今日销售额
|
||||||
|
*/
|
||||||
|
todaySales: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.todaySales);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本月销售额
|
||||||
|
*/
|
||||||
|
monthSales: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.monthSales);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今日订单数
|
||||||
|
*/
|
||||||
|
todayOrders: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.todayOrders);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今日新增用户
|
||||||
|
*/
|
||||||
|
todayUsers: (state): number => {
|
||||||
|
return safeNumber(state.statistics?.todayUsers);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查缓存是否有效
|
||||||
|
*/
|
||||||
|
isCacheValid: (state): boolean => {
|
||||||
|
if (!state.lastUpdateTime) return false;
|
||||||
|
const now = Date.now();
|
||||||
|
return (now - state.lastUpdateTime) < state.cacheExpiry;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* 获取统计数据
|
||||||
|
* @param forceRefresh 是否强制刷新
|
||||||
|
*/
|
||||||
|
async fetchStatistics(forceRefresh = false) {
|
||||||
|
// 如果缓存有效且不强制刷新,直接返回缓存数据
|
||||||
|
if (!forceRefresh && this.isCacheValid && this.statistics) {
|
||||||
|
return this.statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
// 并行获取各种统计数据
|
||||||
|
const [users, orders, total, statisticsData] = await Promise.all([
|
||||||
|
pageUsers({}),
|
||||||
|
pageShopOrder({}),
|
||||||
|
shopOrderTotal(),
|
||||||
|
listCmsStatistics({})
|
||||||
|
]);
|
||||||
|
|
||||||
|
let statistics: CmsStatistics;
|
||||||
|
|
||||||
|
if (statisticsData && statisticsData.length > 0) {
|
||||||
|
// 更新现有统计数据
|
||||||
|
const existingStatistics = statisticsData[0];
|
||||||
|
|
||||||
|
// 确保数据存在且有有效的 ID
|
||||||
|
if (hasValidId(existingStatistics)) {
|
||||||
|
const updateData: Partial<CmsStatistics> = {
|
||||||
|
id: existingStatistics.id,
|
||||||
|
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0),
|
||||||
|
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0),
|
||||||
|
totalSales: safeNumber(total),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 异步更新数据库
|
||||||
|
setTimeout(() => {
|
||||||
|
updateCmsStatistics(updateData).catch((error) => {
|
||||||
|
console.error('更新统计数据失败:', error);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// 更新本地数据
|
||||||
|
statistics = { ...existingStatistics, ...updateData };
|
||||||
|
} else {
|
||||||
|
// 如果现有数据无效,使用基础数据
|
||||||
|
statistics = {
|
||||||
|
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0),
|
||||||
|
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0),
|
||||||
|
totalSales: safeNumber(total),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 创建新的统计数据
|
||||||
|
statistics = {
|
||||||
|
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0),
|
||||||
|
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0),
|
||||||
|
totalSales: safeNumber(total),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 异步保存到数据库
|
||||||
|
setTimeout(() => {
|
||||||
|
addCmsStatistics(statistics).catch((error) => {
|
||||||
|
console.error('保存统计数据失败:', error);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statistics = statistics;
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取统计数据失败:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新统计数据
|
||||||
|
*/
|
||||||
|
updateStatistics(statistics: Partial<CmsStatistics>) {
|
||||||
|
if (this.statistics) {
|
||||||
|
this.statistics = { ...this.statistics, ...statistics };
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存
|
||||||
|
*/
|
||||||
|
clearCache() {
|
||||||
|
this.statistics = null;
|
||||||
|
this.lastUpdateTime = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缓存有效期
|
||||||
|
*/
|
||||||
|
setCacheExpiry(expiry: number) {
|
||||||
|
this.cacheExpiry = expiry;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始自动刷新
|
||||||
|
* @param interval 刷新间隔(毫秒),默认5分钟
|
||||||
|
*/
|
||||||
|
startAutoRefresh(interval = 5 * 60 * 1000) {
|
||||||
|
this.stopAutoRefresh();
|
||||||
|
this.refreshTimer = window.setInterval(() => {
|
||||||
|
this.fetchStatistics(true).catch(console.error);
|
||||||
|
}, interval);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止自动刷新
|
||||||
|
*/
|
||||||
|
stopAutoRefresh() {
|
||||||
|
if (this.refreshTimer) {
|
||||||
|
clearInterval(this.refreshTimer);
|
||||||
|
this.refreshTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
105
src/utils/type-guards.ts
Normal file
105
src/utils/type-guards.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* 类型保护工具函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查值是否为有效的数字
|
||||||
|
*/
|
||||||
|
export function isValidNumber(value: unknown): value is number {
|
||||||
|
return typeof value === 'number' && !isNaN(value) && isFinite(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查值是否为有效的字符串
|
||||||
|
*/
|
||||||
|
export function isValidString(value: unknown): value is string {
|
||||||
|
return typeof value === 'string' && value.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查对象是否有有效的 ID
|
||||||
|
*/
|
||||||
|
export function hasValidId(obj: unknown): obj is { id: number } {
|
||||||
|
return (
|
||||||
|
typeof obj === 'object' &&
|
||||||
|
obj !== null &&
|
||||||
|
'id' in obj &&
|
||||||
|
isValidNumber((obj as any).id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取数字值,提供默认值
|
||||||
|
*/
|
||||||
|
export function safeNumber(value: unknown, defaultValue = 0): number {
|
||||||
|
if (isValidNumber(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const parsed = Number(value);
|
||||||
|
return isValidNumber(parsed) ? parsed : defaultValue;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取字符串值,提供默认值
|
||||||
|
*/
|
||||||
|
export function safeString(value: unknown, defaultValue = ''): string {
|
||||||
|
return typeof value === 'string' ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查 API 响应是否有效
|
||||||
|
*/
|
||||||
|
export function isValidApiResponse<T>(response: unknown): response is { count: number; list?: T[] } {
|
||||||
|
return (
|
||||||
|
typeof response === 'object' &&
|
||||||
|
response !== null &&
|
||||||
|
'count' in response &&
|
||||||
|
isValidNumber((response as any).count)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查统计数据是否有效
|
||||||
|
*/
|
||||||
|
export function isValidStatistics(data: unknown): data is {
|
||||||
|
id?: number;
|
||||||
|
userCount?: number;
|
||||||
|
orderCount?: number;
|
||||||
|
totalSales?: number;
|
||||||
|
} {
|
||||||
|
return (
|
||||||
|
typeof data === 'object' &&
|
||||||
|
data !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全的对象合并,过滤掉 undefined 值
|
||||||
|
*/
|
||||||
|
export function safeMerge<T extends Record<string, any>>(
|
||||||
|
target: T,
|
||||||
|
source: Partial<T>
|
||||||
|
): T {
|
||||||
|
const result = { ...target };
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(source)) {
|
||||||
|
if (value !== undefined) {
|
||||||
|
result[key as keyof T] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建带有默认值的对象
|
||||||
|
*/
|
||||||
|
export function withDefaults<T extends Record<string, any>>(
|
||||||
|
data: Partial<T>,
|
||||||
|
defaults: T
|
||||||
|
): T {
|
||||||
|
return safeMerge(defaults, data);
|
||||||
|
}
|
||||||
@@ -11,20 +11,20 @@
|
|||||||
:height="80"
|
:height="80"
|
||||||
:preview="false"
|
:preview="false"
|
||||||
style="border-radius: 8px"
|
style="border-radius: 8px"
|
||||||
:src="siteInfo.websiteLogo"
|
:src="siteStore.websiteLogo"
|
||||||
fallback="/logo.png"
|
fallback="/logo.png"
|
||||||
/>
|
/>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="14">
|
<a-col :span="14">
|
||||||
<div class="system-info">
|
<div class="system-info">
|
||||||
<h2 class="ele-text-heading">{{ siteInfo.websiteName }}</h2>
|
<h2 class="ele-text-heading">{{ siteStore.websiteName }}</h2>
|
||||||
<p class="ele-text-secondary">{{ siteInfo.comments }}</p>
|
<p class="ele-text-secondary">{{ siteStore.websiteComments }}</p>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-tag color="blue">版本 {{ systemInfo.version }}</a-tag>
|
<a-tag color="blue">版本 {{ systemInfo.version }}</a-tag>
|
||||||
<a-tag color="green">{{ systemInfo.status }}</a-tag>
|
<a-tag color="green">{{ systemInfo.status }}</a-tag>
|
||||||
<a-popover title="小程序码">
|
<a-popover title="小程序码">
|
||||||
<template #content>
|
<template #content>
|
||||||
<p><img :src="siteInfo.websiteDarkLogo" alt="小程序码" width="300" height="300"></p>
|
<p><img :src="siteStore.websiteDarkLogo" alt="小程序码" width="300" height="300"></p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag>
|
<a-tag>
|
||||||
<QrcodeOutlined/>
|
<QrcodeOutlined/>
|
||||||
@@ -47,8 +47,9 @@
|
|||||||
<a-card :bordered="false" class="stat-card">
|
<a-card :bordered="false" class="stat-card">
|
||||||
<a-statistic
|
<a-statistic
|
||||||
title="用户总数"
|
title="用户总数"
|
||||||
:value="form.userCount"
|
:value="userCount"
|
||||||
:value-style="{ color: '#3f8600' }"
|
:value-style="{ color: '#3f8600' }"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<UserOutlined/>
|
<UserOutlined/>
|
||||||
@@ -61,8 +62,9 @@
|
|||||||
<a-card :bordered="false" class="stat-card">
|
<a-card :bordered="false" class="stat-card">
|
||||||
<a-statistic
|
<a-statistic
|
||||||
title="订单总数"
|
title="订单总数"
|
||||||
:value="form.orderCount"
|
:value="orderCount"
|
||||||
:value-style="{ color: '#1890ff' }"
|
:value-style="{ color: '#1890ff' }"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<AccountBookOutlined/>
|
<AccountBookOutlined/>
|
||||||
@@ -75,8 +77,9 @@
|
|||||||
<a-card :bordered="false" class="stat-card">
|
<a-card :bordered="false" class="stat-card">
|
||||||
<a-statistic
|
<a-statistic
|
||||||
title="总营业额"
|
title="总营业额"
|
||||||
:value="form.totalSales"
|
:value="totalSales"
|
||||||
:value-style="{ color: '#cf1322' }"
|
:value-style="{ color: '#cf1322' }"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<MoneyCollectOutlined/>
|
<MoneyCollectOutlined/>
|
||||||
@@ -92,6 +95,7 @@
|
|||||||
:value="runDays"
|
:value="runDays"
|
||||||
suffix="天"
|
suffix="天"
|
||||||
:value-style="{ color: '#722ed1' }"
|
:value-style="{ color: '#722ed1' }"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<ClockCircleOutlined/>
|
<ClockCircleOutlined/>
|
||||||
@@ -164,7 +168,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {ref, reactive, onMounted} from 'vue';
|
import {ref, onMounted, onUnmounted, computed} from 'vue';
|
||||||
import {
|
import {
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
CalendarOutlined,
|
CalendarOutlined,
|
||||||
@@ -176,18 +180,18 @@ import {
|
|||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
MoneyCollectOutlined
|
MoneyCollectOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import {assignObject} from 'ele-admin-pro';
|
|
||||||
import {getSiteInfo} from "@/api/layout";
|
|
||||||
import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
|
|
||||||
import {pageUsers} from "@/api/system/user";
|
|
||||||
import {addShopOrder, pageShopOrder, shopOrderTotal} from "@/api/shop/shopOrder";
|
|
||||||
import {openNew} from "@/utils/common";
|
import {openNew} from "@/utils/common";
|
||||||
import {addCmsStatistics, listCmsStatistics, updateCmsStatistics} from "@/api/cms/cmsStatistics";
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
import {CmsStatistics} from "@/api/cms/cmsStatistics/model";
|
import { useStatisticsStore } from '@/store/modules/statistics';
|
||||||
import {addCmsArticle, updateCmsArticle} from "@/api/cms/cmsArticle";
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
// 当前小程序项目
|
// 使用状态管理
|
||||||
const siteInfo = ref<CmsWebsite>({});
|
const siteStore = useSiteStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
// 从 store 中获取响应式数据
|
||||||
|
const { siteInfo, loading: siteLoading } = storeToRefs(siteStore);
|
||||||
|
const { loading: statisticsLoading } = storeToRefs(statisticsStore);
|
||||||
|
|
||||||
// 系统信息
|
// 系统信息
|
||||||
const systemInfo = ref({
|
const systemInfo = ref({
|
||||||
@@ -202,118 +206,34 @@ const systemInfo = ref({
|
|||||||
expirationTime: '2024-01-01 09:00:00'
|
expirationTime: '2024-01-01 09:00:00'
|
||||||
});
|
});
|
||||||
|
|
||||||
const runDays = ref<number>(0)
|
// 计算属性
|
||||||
|
const runDays = computed(() => siteStore.runDays);
|
||||||
|
const userCount = computed(() => statisticsStore.userCount);
|
||||||
|
const orderCount = computed(() => statisticsStore.orderCount);
|
||||||
|
const totalSales = computed(() => statisticsStore.totalSales);
|
||||||
|
|
||||||
// 统计数据
|
// 加载状态
|
||||||
const form = reactive<CmsStatistics>({
|
const loading = computed(() => siteLoading.value || statisticsLoading.value);
|
||||||
websiteId: undefined,
|
|
||||||
// 用户总数
|
onMounted(async () => {
|
||||||
userCount: undefined,
|
// 加载网站信息和统计数据
|
||||||
// 订单总数
|
try {
|
||||||
orderCount: undefined,
|
await Promise.all([
|
||||||
// 商品总数
|
siteStore.fetchSiteInfo(),
|
||||||
productCount: undefined,
|
statisticsStore.fetchStatistics()
|
||||||
// 总销售额
|
]);
|
||||||
totalSales: undefined,
|
|
||||||
// 本月销售额
|
// 开始自动刷新统计数据(每5分钟)
|
||||||
monthSales: undefined,
|
statisticsStore.startAutoRefresh();
|
||||||
// 今日销售额
|
} catch (error) {
|
||||||
todaySales: undefined,
|
console.error('加载数据失败:', error);
|
||||||
// 昨日销售额
|
}
|
||||||
yesterdaySales: undefined,
|
|
||||||
// 本周销售额
|
|
||||||
weekSales: undefined,
|
|
||||||
// 本年销售额
|
|
||||||
yearSales: undefined,
|
|
||||||
// 今日订单数
|
|
||||||
todayOrders: undefined,
|
|
||||||
// 本月订单数
|
|
||||||
monthOrders: undefined,
|
|
||||||
// 今日新增用户
|
|
||||||
todayUsers: undefined,
|
|
||||||
// 本月新增用户
|
|
||||||
monthUsers: undefined,
|
|
||||||
// 今日访问量
|
|
||||||
todayVisits: undefined,
|
|
||||||
// 总访问量
|
|
||||||
totalVisits: undefined,
|
|
||||||
// 商户总数
|
|
||||||
merchantCount: undefined,
|
|
||||||
// 活跃用户数
|
|
||||||
activeUsers: undefined,
|
|
||||||
// 转化率(%)
|
|
||||||
conversionRate: undefined,
|
|
||||||
// 平均订单金额
|
|
||||||
avgOrderAmount: undefined,
|
|
||||||
// 统计日期
|
|
||||||
statisticsDate: undefined,
|
|
||||||
// 统计类型: 1日统计, 2月统计, 3年统计
|
|
||||||
statisticsType: undefined,
|
|
||||||
// 运行天数
|
|
||||||
runDays: undefined,
|
|
||||||
// 排序号
|
|
||||||
sortNumber: undefined,
|
|
||||||
// 操作用户ID
|
|
||||||
userId: undefined,
|
|
||||||
// 商户ID
|
|
||||||
merchantId: undefined,
|
|
||||||
// 状态: 0禁用, 1启用
|
|
||||||
status: undefined,
|
|
||||||
// 是否删除: 0否, 1是
|
|
||||||
deleted: undefined,
|
|
||||||
// 租户ID
|
|
||||||
tenantId: undefined,
|
|
||||||
// 创建时间
|
|
||||||
createTime: undefined,
|
|
||||||
// 修改时间
|
|
||||||
updateTime: undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onUnmounted(() => {
|
||||||
// 加载系统信息和统计数据
|
// 组件卸载时停止自动刷新
|
||||||
loadSystemInfo();
|
statisticsStore.stopAutoRefresh();
|
||||||
loadStatistics();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadSystemInfo = async () => {
|
|
||||||
// TODO: 调用API获取系统信息
|
|
||||||
siteInfo.value = await getSiteInfo();
|
|
||||||
if (siteInfo.value.createTime) {
|
|
||||||
// 根据创建时间计算运行天数
|
|
||||||
runDays.value = Math.floor((new Date().getTime() - new Date(siteInfo.value.createTime).getTime()) / (24 * 60 * 60 * 1000))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadStatistics = async () => {
|
|
||||||
// TODO: 调用API获取统计数据
|
|
||||||
const users = await pageUsers({})
|
|
||||||
const orders = await pageShopOrder({})
|
|
||||||
const total = await shopOrderTotal()
|
|
||||||
const data = await listCmsStatistics({});
|
|
||||||
|
|
||||||
// 获取统计表数据
|
|
||||||
if (data) {
|
|
||||||
const saveOrUpdate = data.length > 0 ? updateCmsStatistics : addCmsStatistics;
|
|
||||||
if (data.length > 0) {
|
|
||||||
const saveData = data[0]
|
|
||||||
assignObject(form, saveData);
|
|
||||||
// 更新数据
|
|
||||||
setTimeout(() => {
|
|
||||||
if (saveData && users && orders) {
|
|
||||||
const id = saveData.id
|
|
||||||
saveOrUpdate({
|
|
||||||
id,
|
|
||||||
userCount: users.count,
|
|
||||||
orderCount: orders.count,
|
|
||||||
totalSales: Number(total),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},2000)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ import {
|
|||||||
} from "@/utils/domain";
|
} from "@/utils/domain";
|
||||||
import {openNew, openPreview} from "@/utils/common";
|
import {openNew, openPreview} from "@/utils/common";
|
||||||
import {FileRecord} from "@/api/system/file/model";
|
import {FileRecord} from "@/api/system/file/model";
|
||||||
import {getSiteInfo} from "@/api/layout";
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
|
||||||
|
|
||||||
const useForm = Form.useForm;
|
const useForm = Form.useForm;
|
||||||
@@ -173,6 +173,7 @@ import {getSiteInfo} from "@/api/layout";
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { styleResponsive } = storeToRefs(themeStore);
|
const { styleResponsive } = storeToRefs(themeStore);
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const siteStore = useSiteStore();
|
||||||
// 当前开发环境
|
// 当前开发环境
|
||||||
const env = import.meta.env.MODE;
|
const env = import.meta.env.MODE;
|
||||||
// tab 页选中
|
// tab 页选中
|
||||||
@@ -326,21 +327,28 @@ const onDeleteItem = (index: number) => {
|
|||||||
form.websiteLogo = '';
|
form.websiteLogo = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = () => {
|
const query = async () => {
|
||||||
logo.value = [];
|
logo.value = [];
|
||||||
getSiteInfo().then(data => {
|
try {
|
||||||
assignFields(data)
|
const data = await siteStore.fetchSiteInfo();
|
||||||
logo.value.push({
|
assignFields(data);
|
||||||
uid: 1,
|
if (data.websiteLogo) {
|
||||||
url: data.websiteLogo,
|
logo.value.push({
|
||||||
status: 'done'
|
uid: 1,
|
||||||
});
|
url: data.websiteLogo,
|
||||||
darkLogo.value.push({
|
status: 'done'
|
||||||
uid: 1,
|
});
|
||||||
url: data.websiteDarkLogo,
|
}
|
||||||
status: 'done'
|
if (data.websiteDarkLogo) {
|
||||||
})
|
darkLogo.value.push({
|
||||||
})
|
uid: 1,
|
||||||
|
url: data.websiteDarkLogo,
|
||||||
|
status: 'done'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网站信息失败:', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
query();
|
query();
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ import {updateCmsDomain} from '@/api/cms/cmsDomain';
|
|||||||
import {updateTenant} from "@/api/system/tenant";
|
import {updateTenant} from "@/api/system/tenant";
|
||||||
import {getPageTitle, push} from "@/utils/common";
|
import {getPageTitle, push} from "@/utils/common";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import {getSiteInfo} from "@/api/layout";
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
import useFormData from "@/utils/use-form-data";
|
import useFormData from "@/utils/use-form-data";
|
||||||
import type {User} from "@/api/system/user/model";
|
import type {User} from "@/api/system/user/model";
|
||||||
|
|
||||||
@@ -146,6 +146,7 @@ const useForm = Form.useForm;
|
|||||||
// 是否开启响应式布局
|
// 是否开启响应式布局
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const {styleResponsive} = storeToRefs(themeStore);
|
const {styleResponsive} = storeToRefs(themeStore);
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'done'): void;
|
(e: 'done'): void;
|
||||||
@@ -349,19 +350,23 @@ const save = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
const data = await getSiteInfo()
|
try {
|
||||||
if (data) {
|
const data = await siteStore.fetchSiteInfo();
|
||||||
console.log(data)
|
if (data) {
|
||||||
assignFields({
|
console.log(data);
|
||||||
...data
|
assignFields({
|
||||||
});
|
...data
|
||||||
images.value.push(
|
});
|
||||||
{
|
if (data.websiteLogo) {
|
||||||
uid: uuid(),
|
images.value.push({
|
||||||
url: data.websiteLogo,
|
uid: uuid(),
|
||||||
status: 'done'
|
url: data.websiteLogo,
|
||||||
|
status: 'done'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网站信息失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
263
src/views/test/store-test.vue
Normal file
263
src/views/test/store-test.vue
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
<template>
|
||||||
|
<div class="store-test-page">
|
||||||
|
<a-card title="状态管理测试页面" :bordered="false">
|
||||||
|
<a-space direction="vertical" style="width: 100%">
|
||||||
|
|
||||||
|
<!-- 网站信息测试 -->
|
||||||
|
<a-card title="网站信息 Store 测试" size="small">
|
||||||
|
<a-spin :spinning="siteLoading">
|
||||||
|
<a-descriptions :column="2" size="small">
|
||||||
|
<a-descriptions-item label="网站名称">
|
||||||
|
{{ websiteName || '暂无数据' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="网站Logo">
|
||||||
|
<img v-if="websiteLogo" :src="websiteLogo" alt="logo" style="width: 50px; height: 50px;" />
|
||||||
|
<span v-else>暂无Logo</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="网站描述">
|
||||||
|
{{ websiteComments || '暂无描述' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="运行天数">
|
||||||
|
{{ runDays }} 天
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="网站域名">
|
||||||
|
{{ websiteDomain || '暂无域名' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="网站ID">
|
||||||
|
{{ websiteId || '暂无ID' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<a-space style="margin-top: 16px">
|
||||||
|
<a-button @click="refreshSiteInfo" :loading="siteLoading">
|
||||||
|
刷新网站信息
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="clearSiteCache">
|
||||||
|
清除网站缓存
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-spin>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 统计数据测试 -->
|
||||||
|
<a-card title="统计数据 Store 测试" size="small">
|
||||||
|
<a-spin :spinning="statisticsLoading">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-statistic
|
||||||
|
title="用户总数"
|
||||||
|
:value="userCount"
|
||||||
|
:value-style="{ color: '#3f8600' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-statistic
|
||||||
|
title="订单总数"
|
||||||
|
:value="orderCount"
|
||||||
|
:value-style="{ color: '#1890ff' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-statistic
|
||||||
|
title="总销售额"
|
||||||
|
:value="totalSales"
|
||||||
|
:value-style="{ color: '#cf1322' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-statistic
|
||||||
|
title="今日销售额"
|
||||||
|
:value="todaySales"
|
||||||
|
:value-style="{ color: '#722ed1' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-space style="margin-top: 16px">
|
||||||
|
<a-button @click="refreshStatistics" :loading="statisticsLoading">
|
||||||
|
刷新统计数据
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="clearStatisticsCache">
|
||||||
|
清除统计缓存
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
@click="toggleAutoRefresh"
|
||||||
|
:type="autoRefreshEnabled ? 'primary' : 'default'"
|
||||||
|
>
|
||||||
|
{{ autoRefreshEnabled ? '停止自动刷新' : '开始自动刷新' }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-spin>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 缓存状态信息 -->
|
||||||
|
<a-card title="缓存状态信息" size="small">
|
||||||
|
<a-descriptions :column="2" size="small">
|
||||||
|
<a-descriptions-item label="网站信息缓存有效">
|
||||||
|
<a-tag :color="siteStore.isCacheValid ? 'green' : 'red'">
|
||||||
|
{{ siteStore.isCacheValid ? '有效' : '无效' }}
|
||||||
|
</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="统计数据缓存有效">
|
||||||
|
<a-tag :color="statisticsStore.isCacheValid ? 'green' : 'red'">
|
||||||
|
{{ statisticsStore.isCacheValid ? '有效' : '无效' }}
|
||||||
|
</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="网站信息最后更新">
|
||||||
|
{{ siteStore.lastUpdateTime ? new Date(siteStore.lastUpdateTime).toLocaleString() : '从未更新' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="统计数据最后更新">
|
||||||
|
{{ statisticsStore.lastUpdateTime ? new Date(statisticsStore.lastUpdateTime).toLocaleString() : '从未更新' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 操作日志 -->
|
||||||
|
<a-card title="操作日志" size="small">
|
||||||
|
<a-list
|
||||||
|
:data-source="logs"
|
||||||
|
size="small"
|
||||||
|
:pagination="{ pageSize: 5 }"
|
||||||
|
>
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta>
|
||||||
|
<template #title>
|
||||||
|
<span>{{ item.action }}</span>
|
||||||
|
<a-tag size="small" style="margin-left: 8px">
|
||||||
|
{{ item.timestamp }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
{{ item.result }}
|
||||||
|
</template>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
</a-space>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { useSiteData } from '@/composables/useSiteData';
|
||||||
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
import { useStatisticsStore } from '@/store/modules/statistics';
|
||||||
|
|
||||||
|
// 使用组合式函数
|
||||||
|
const {
|
||||||
|
websiteName,
|
||||||
|
websiteLogo,
|
||||||
|
websiteComments,
|
||||||
|
websiteDomain,
|
||||||
|
websiteId,
|
||||||
|
runDays,
|
||||||
|
userCount,
|
||||||
|
orderCount,
|
||||||
|
totalSales,
|
||||||
|
todaySales,
|
||||||
|
siteLoading,
|
||||||
|
statisticsLoading,
|
||||||
|
fetchSiteInfo,
|
||||||
|
fetchStatistics,
|
||||||
|
clearCache
|
||||||
|
} = useSiteData();
|
||||||
|
|
||||||
|
// 直接使用 store(用于访问更多详细信息)
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
// 本地状态
|
||||||
|
const autoRefreshEnabled = ref(false);
|
||||||
|
const logs = ref<Array<{ action: string; timestamp: string; result: string }>>([]);
|
||||||
|
|
||||||
|
// 添加日志
|
||||||
|
const addLog = (action: string, result: string) => {
|
||||||
|
logs.value.unshift({
|
||||||
|
action,
|
||||||
|
timestamp: new Date().toLocaleTimeString(),
|
||||||
|
result
|
||||||
|
});
|
||||||
|
// 只保留最近20条日志
|
||||||
|
if (logs.value.length > 20) {
|
||||||
|
logs.value = logs.value.slice(0, 20);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新网站信息
|
||||||
|
const refreshSiteInfo = async () => {
|
||||||
|
try {
|
||||||
|
await fetchSiteInfo(true);
|
||||||
|
addLog('刷新网站信息', '成功');
|
||||||
|
} catch (error) {
|
||||||
|
addLog('刷新网站信息', `失败: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新统计数据
|
||||||
|
const refreshStatistics = async () => {
|
||||||
|
try {
|
||||||
|
await fetchStatistics(true);
|
||||||
|
addLog('刷新统计数据', '成功');
|
||||||
|
} catch (error) {
|
||||||
|
addLog('刷新统计数据', `失败: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清除网站缓存
|
||||||
|
const clearSiteCache = () => {
|
||||||
|
siteStore.clearCache();
|
||||||
|
addLog('清除网站缓存', '成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清除统计缓存
|
||||||
|
const clearStatisticsCache = () => {
|
||||||
|
statisticsStore.clearCache();
|
||||||
|
addLog('清除统计缓存', '成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换自动刷新
|
||||||
|
const toggleAutoRefresh = () => {
|
||||||
|
if (autoRefreshEnabled.value) {
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
autoRefreshEnabled.value = false;
|
||||||
|
addLog('停止自动刷新', '成功');
|
||||||
|
} else {
|
||||||
|
statisticsStore.startAutoRefresh(10000); // 10秒间隔用于测试
|
||||||
|
autoRefreshEnabled.value = true;
|
||||||
|
addLog('开始自动刷新', '10秒间隔');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
addLog('页面加载', '开始初始化');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
fetchSiteInfo(),
|
||||||
|
fetchStatistics()
|
||||||
|
]);
|
||||||
|
addLog('初始化数据', '成功');
|
||||||
|
} catch (error) {
|
||||||
|
addLog('初始化数据', `失败: ${error}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 清理自动刷新
|
||||||
|
if (autoRefreshEnabled.value) {
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
}
|
||||||
|
addLog('页面卸载', '清理完成');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.store-test-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user