修复:百色中学排行榜统计金额和仪表盘的统计数不一致的问题
This commit is contained in:
224
docs/bszx-统计数据状态管理修复总结.md
Normal file
224
docs/bszx-统计数据状态管理修复总结.md
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# 百色中学统计数据状态管理修复总结
|
||||||
|
|
||||||
|
## 问题背景
|
||||||
|
|
||||||
|
在 `src/views/bszx/dashboard/index.vue` 中,`getTotalBszxPrice` 函数存在以下问题:
|
||||||
|
|
||||||
|
1. **异步函数在计算属性中使用错误**:
|
||||||
|
```typescript
|
||||||
|
const totalBszxPrice = computed(() => getTotalBszxPrice()); // ❌ 返回 Promise 而不是数值
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **TypeScript 类型错误**:IDE 提示 "Object is possibly undefined"
|
||||||
|
|
||||||
|
3. **数据不统一**:`totalPriceAmount` 在多个组件中重复计算和传递
|
||||||
|
|
||||||
|
## 解决方案
|
||||||
|
|
||||||
|
### 1. 创建专门的百色中学统计数据 Store
|
||||||
|
|
||||||
|
**文件位置**:`src/store/modules/bszx-statistics.ts`
|
||||||
|
|
||||||
|
**核心功能**:
|
||||||
|
- 统一管理百色中学相关的统计数据
|
||||||
|
- 智能缓存机制(5分钟有效期)
|
||||||
|
- 自动刷新功能
|
||||||
|
- 完整的类型保护和错误处理
|
||||||
|
|
||||||
|
**主要特性**:
|
||||||
|
```typescript
|
||||||
|
export const useBszxStatisticsStore = defineStore({
|
||||||
|
id: 'bszx-statistics',
|
||||||
|
state: (): BszxStatisticsState => ({
|
||||||
|
totalPrice: 0,
|
||||||
|
loading: false,
|
||||||
|
lastUpdateTime: null,
|
||||||
|
cacheExpiry: 5 * 60 * 1000, // 5分钟缓存
|
||||||
|
refreshTimer: null
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
bszxTotalPrice: (state): number => safeNumber(state.totalPrice)
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async fetchBszxStatistics(forceRefresh = false),
|
||||||
|
startAutoRefresh(interval = 5 * 60 * 1000),
|
||||||
|
stopAutoRefresh()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 修复 Dashboard 页面
|
||||||
|
|
||||||
|
**修复前**:
|
||||||
|
```typescript
|
||||||
|
// ❌ 错误的实现
|
||||||
|
const totalBszxPrice = computed(() => getTotalBszxPrice());
|
||||||
|
const getTotalBszxPrice = async () => {
|
||||||
|
return await bszxOrderTotal()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**修复后**:
|
||||||
|
```typescript
|
||||||
|
// ✅ 正确的实现
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
const totalBszxPrice = computed(() => bszxStatisticsStore.bszxTotalPrice);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
siteStore.fetchSiteInfo(),
|
||||||
|
statisticsStore.fetchStatistics(),
|
||||||
|
bszxStatisticsStore.fetchBszxStatistics() // 加载百色中学统计数据
|
||||||
|
]);
|
||||||
|
|
||||||
|
statisticsStore.startAutoRefresh();
|
||||||
|
bszxStatisticsStore.startAutoRefresh(); // 开始自动刷新
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 统一 totalPriceAmount 的使用
|
||||||
|
|
||||||
|
**涉及的文件**:
|
||||||
|
- `src/views/bszx/bszxPayRanking/index.vue`
|
||||||
|
- `src/views/bszx/bszxPayRanking/components/search.vue`
|
||||||
|
- `src/views/bsyx/bsyxPayRanking/index.vue`
|
||||||
|
- `src/views/bsyx/bsyxPayRanking/components/search.vue`
|
||||||
|
|
||||||
|
**修复策略**:
|
||||||
|
1. **Search 组件**:直接从 store 获取数据
|
||||||
|
```typescript
|
||||||
|
// 使用百色中学统计数据 store
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
const bszxTotalPrice = computed(() => bszxStatisticsStore.bszxTotalPrice);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **主组件**:更新 store 数据而不是本地变量
|
||||||
|
```typescript
|
||||||
|
const datasource: DatasourceFunction = ({where}) => {
|
||||||
|
return ranking({...where}).then(data => {
|
||||||
|
// 计算总金额并更新到 store
|
||||||
|
let totalPrice = 0;
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (item.totalPrice) {
|
||||||
|
totalPrice += item.totalPrice;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新 store 中的数据
|
||||||
|
bszxStatisticsStore.updateStatistics({ totalPrice });
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心改进
|
||||||
|
|
||||||
|
### 1. 类型安全
|
||||||
|
- 使用 `safeNumber` 工具函数确保数据类型安全
|
||||||
|
- 完整的 TypeScript 类型定义
|
||||||
|
- 运行时类型检查
|
||||||
|
|
||||||
|
### 2. 数据一致性
|
||||||
|
- 统一的数据源(store)
|
||||||
|
- 避免重复计算和传递
|
||||||
|
- 自动同步更新
|
||||||
|
|
||||||
|
### 3. 性能优化
|
||||||
|
- 智能缓存机制(5分钟有效期)
|
||||||
|
- 自动刷新功能
|
||||||
|
- 避免不必要的 API 调用
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
- 完善的错误捕获和处理
|
||||||
|
- 优雅的降级策略
|
||||||
|
- 详细的错误日志
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 在组件中使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useBszxStatisticsStore } from '@/store/modules/bszx-statistics';
|
||||||
|
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
|
||||||
|
// 获取总金额
|
||||||
|
const totalPrice = computed(() => bszxStatisticsStore.bszxTotalPrice);
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
onMounted(async () => {
|
||||||
|
await bszxStatisticsStore.fetchBszxStatistics();
|
||||||
|
bszxStatisticsStore.startAutoRefresh(); // 开始自动刷新
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清理资源
|
||||||
|
onUnmounted(() => {
|
||||||
|
bszxStatisticsStore.stopAutoRefresh();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 方法
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 获取统计数据(带缓存)
|
||||||
|
await bszxStatisticsStore.fetchBszxStatistics();
|
||||||
|
|
||||||
|
// 强制刷新
|
||||||
|
await bszxStatisticsStore.fetchBszxStatistics(true);
|
||||||
|
|
||||||
|
// 更新数据
|
||||||
|
bszxStatisticsStore.updateStatistics({ totalPrice: 1000 });
|
||||||
|
|
||||||
|
// 开始自动刷新(默认5分钟间隔)
|
||||||
|
bszxStatisticsStore.startAutoRefresh();
|
||||||
|
|
||||||
|
// 停止自动刷新
|
||||||
|
bszxStatisticsStore.stopAutoRefresh();
|
||||||
|
|
||||||
|
// 清除缓存
|
||||||
|
bszxStatisticsStore.clearCache();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
✅ **TypeScript 编译通过** - 无类型错误
|
||||||
|
✅ **生产构建成功** - 无运行时错误
|
||||||
|
✅ **数据统一管理** - 避免重复计算
|
||||||
|
✅ **类型安全** - 完整的类型保护
|
||||||
|
✅ **性能优化** - 智能缓存和自动刷新
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. **生命周期管理**:
|
||||||
|
```typescript
|
||||||
|
onMounted(() => bszxStatisticsStore.startAutoRefresh());
|
||||||
|
onUnmounted(() => bszxStatisticsStore.stopAutoRefresh());
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **错误处理**:
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
await bszxStatisticsStore.fetchBszxStatistics();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取统计数据失败:', error);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **强制刷新**:
|
||||||
|
```typescript
|
||||||
|
const handleRefresh = () => bszxStatisticsStore.fetchBszxStatistics(true);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
通过创建专门的 `bszx-statistics` store,我们成功解决了:
|
||||||
|
|
||||||
|
1. **异步函数在计算属性中的错误使用**
|
||||||
|
2. **TypeScript 类型安全问题**
|
||||||
|
3. **数据重复计算和传递问题**
|
||||||
|
4. **缺乏统一的数据管理**
|
||||||
|
|
||||||
|
这个实现提供了更好的类型安全性、数据一致性和性能优化,为百色中学相关功能提供了可靠的数据支撑。
|
||||||
171
docs/数据不一致问题修复说明.md
Normal file
171
docs/数据不一致问题修复说明.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# 百色中学统计金额数据不一致问题修复说明
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
用户发现 `/bszx/ranking` 页面的统计金额和 `/bszx/dashboard` 页面的统计金额不一致,需要确定哪个数据是正确的。
|
||||||
|
|
||||||
|
## 问题分析
|
||||||
|
|
||||||
|
### 数据来源差异
|
||||||
|
|
||||||
|
经过分析,发现两个页面使用了不同的数据源:
|
||||||
|
|
||||||
|
#### 1. Dashboard 页面 (`/bszx/dashboard`)
|
||||||
|
- **API**: `bszxOrderTotal()`
|
||||||
|
- **接口**: `/bszx/bszx-order/total`
|
||||||
|
- **数据来源**: **订单表** (`bszx-order`)
|
||||||
|
- **数据类型**: `ShopOrder[]` - 订单数组
|
||||||
|
- **统计逻辑**: 统计所有有效订单的实际支付金额
|
||||||
|
- **字段**: `payPrice` 或 `totalPrice`
|
||||||
|
|
||||||
|
#### 2. Ranking 页面 (`/bszx/ranking`)
|
||||||
|
- **API**: `ranking()`
|
||||||
|
- **接口**: `/bszx/bszx-pay-ranking/ranking`
|
||||||
|
- **数据来源**: **捐款排行表** (`bszx-pay-ranking`)
|
||||||
|
- **数据类型**: `BszxPayRanking[]` - 排行榜记录数组
|
||||||
|
- **统计逻辑**: 统计排行榜中的汇总金额
|
||||||
|
- **字段**: `totalPrice`
|
||||||
|
|
||||||
|
### 数据性质差异
|
||||||
|
|
||||||
|
1. **订单表数据** (Dashboard):
|
||||||
|
- ✅ **真实的业务数据**
|
||||||
|
- ✅ 反映实际的支付情况
|
||||||
|
- ✅ 只统计有效订单(已支付、未取消)
|
||||||
|
- ✅ 实时更新
|
||||||
|
|
||||||
|
2. **排行榜数据** (Ranking):
|
||||||
|
- ⚠️ **展示用的汇总数据**
|
||||||
|
- ⚠️ 可能包含统计逻辑或过滤条件
|
||||||
|
- ⚠️ 可能不是实时更新
|
||||||
|
- ⚠️ 用于排行榜展示,不一定等于实际订单金额
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
**Dashboard 页面的数据是正确的**,因为:
|
||||||
|
|
||||||
|
1. **数据权威性**:直接来自订单表,是真实的业务数据
|
||||||
|
2. **统计准确性**:只统计有效订单的实际支付金额
|
||||||
|
3. **实时性**:反映当前的真实订单状态
|
||||||
|
4. **业务意义**:代表实际的收入情况
|
||||||
|
|
||||||
|
**Ranking 页面的数据是展示数据**,可能:
|
||||||
|
- 是为了排行榜展示而特别处理的数据
|
||||||
|
- 包含不同的统计规则或过滤条件
|
||||||
|
- 不代表实际的订单收入
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
### 1. 修正数据处理逻辑
|
||||||
|
|
||||||
|
**修复前的问题**:
|
||||||
|
```typescript
|
||||||
|
// ❌ 错误:ranking 页面覆盖了真实的订单统计数据
|
||||||
|
const datasource = ({where}) => {
|
||||||
|
return ranking({...where}).then(data => {
|
||||||
|
let totalPrice = 0;
|
||||||
|
data.forEach((item) => {
|
||||||
|
if(item.totalPrice) totalPrice += item.totalPrice;
|
||||||
|
});
|
||||||
|
bszxStatisticsStore.updateStatistics({ totalPrice }); // 错误地覆盖了真实数据
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**修复后**:
|
||||||
|
```typescript
|
||||||
|
// ✅ 正确:分离两种数据,不相互覆盖
|
||||||
|
const datasource = ({where}) => {
|
||||||
|
return ranking({...where}).then(data => {
|
||||||
|
// 计算排行榜总金额(仅用于本页面显示)
|
||||||
|
let total = 0;
|
||||||
|
data.forEach((item) => {
|
||||||
|
if(item.totalPrice) total += item.totalPrice;
|
||||||
|
});
|
||||||
|
rankingTotalPrice.value = total; // 本地变量,不影响全局 store
|
||||||
|
|
||||||
|
// store 中的数据来自 bszxOrderTotal API,代表真实的订单金额
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 优化 Store 数据处理
|
||||||
|
|
||||||
|
**修复 `bszxOrderTotal` 数据解析**:
|
||||||
|
```typescript
|
||||||
|
// 修复前:不正确的数据处理
|
||||||
|
if (Array.isArray(result) && result.length > 0) {
|
||||||
|
totalPrice = safeNumber(result[0]); // ❌ 只取第一个元素
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复后:正确累加所有订单金额
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
result.forEach((order: any) => {
|
||||||
|
if (order.payPrice) {
|
||||||
|
totalPrice += safeNumber(order.payPrice); // ✅ 累加实际支付金额
|
||||||
|
} else if (order.totalPrice) {
|
||||||
|
totalPrice += safeNumber(order.totalPrice); // ✅ 备用字段
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 用户界面优化
|
||||||
|
|
||||||
|
为了让用户清楚地了解两种数据的差异,在 Ranking 页面同时显示两个金额:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<!-- 实际订单总金额(权威数据) -->
|
||||||
|
<a-tooltip title="实际订单总金额(来自订单表)">
|
||||||
|
<span class="text-gray-400">实际订单总金额:</span>
|
||||||
|
<span class="text-gray-700 font-bold">¥{{ formatNumber(bszxTotalPrice) }}</span>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<!-- 排行榜统计金额(展示数据) -->
|
||||||
|
<a-tooltip title="排行榜统计金额(来自排行榜表)">
|
||||||
|
<span class="text-gray-400">排行榜统计金额:</span>
|
||||||
|
<span class="text-gray-700 font-bold">¥{{ formatNumber(rankingTotalPrice) }}</span>
|
||||||
|
</a-tooltip>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 修改的文件
|
||||||
|
|
||||||
|
### 1. Store 层面
|
||||||
|
- `src/store/modules/bszx-statistics.ts` - 修正数据解析逻辑
|
||||||
|
|
||||||
|
### 2. Dashboard 页面
|
||||||
|
- 无需修改,已经使用正确的数据源
|
||||||
|
|
||||||
|
### 3. Ranking 页面
|
||||||
|
- `src/views/bszx/bszxPayRanking/index.vue` - 分离数据处理逻辑
|
||||||
|
- `src/views/bszx/bszxPayRanking/components/search.vue` - 显示两种金额
|
||||||
|
- `src/views/bsyx/bsyxPayRanking/index.vue` - 同样的修改
|
||||||
|
- `src/views/bsyx/bsyxPayRanking/components/search.vue` - 同样的修改
|
||||||
|
|
||||||
|
## 验证方法
|
||||||
|
|
||||||
|
1. **检查 Dashboard 页面**:显示的是真实订单总金额
|
||||||
|
2. **检查 Ranking 页面**:同时显示两种金额,用户可以对比
|
||||||
|
3. **数据一致性**:Dashboard 和 Ranking 页面的"实际订单总金额"应该一致
|
||||||
|
4. **数据差异说明**:两个金额可能不同,这是正常的,因为数据来源和统计逻辑不同
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. **数据权威性**:始终以订单表数据为准进行业务决策
|
||||||
|
2. **数据透明性**:向用户清楚说明不同数据的来源和含义
|
||||||
|
3. **避免混淆**:不同数据源的数据不应相互覆盖
|
||||||
|
4. **文档说明**:为不同的统计数据提供清晰的说明
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
通过这次修复:
|
||||||
|
|
||||||
|
1. ✅ **明确了数据权威性**:订单表数据是权威数据源
|
||||||
|
2. ✅ **分离了数据处理**:不同数据源不再相互干扰
|
||||||
|
3. ✅ **提高了透明度**:用户可以看到两种数据的对比
|
||||||
|
4. ✅ **保持了一致性**:全局 store 中的数据始终来自权威数据源
|
||||||
|
5. ✅ **改善了用户体验**:清楚标注了数据来源和含义
|
||||||
|
|
||||||
|
现在用户可以清楚地看到两种数据,并理解它们的差异和用途。
|
||||||
@@ -18,3 +18,20 @@ export async function pageBszxOrder(params: ShopOrderParam) {
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.data.message));
|
return Promise.reject(new Error(res.data.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计订单总金额(只统计有效订单)
|
||||||
|
*/
|
||||||
|
export async function bszxOrderTotal(params?: ShopOrderParam) {
|
||||||
|
const res = await request.get<ApiResult<ShopOrder[]>>(
|
||||||
|
MODULES_API_URL + '/bszx/bszx-order/total',
|
||||||
|
{
|
||||||
|
params
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.data.code === 0 && res.data.data) {
|
||||||
|
return res.data.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
|
|||||||
141
src/store/modules/bszx-statistics.ts
Normal file
141
src/store/modules/bszx-statistics.ts
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* 百色中学统计数据 store
|
||||||
|
*/
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { bszxOrderTotal } from '@/api/bszx/bszxOrder';
|
||||||
|
import { safeNumber } from '@/utils/type-guards';
|
||||||
|
|
||||||
|
export interface BszxStatisticsState {
|
||||||
|
// 总营业额
|
||||||
|
totalPrice: number;
|
||||||
|
// 加载状态
|
||||||
|
loading: boolean;
|
||||||
|
// 最后更新时间
|
||||||
|
lastUpdateTime: number | null;
|
||||||
|
// 缓存有效期(毫秒)- 5分钟缓存
|
||||||
|
cacheExpiry: number;
|
||||||
|
// 自动刷新定时器
|
||||||
|
refreshTimer: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useBszxStatisticsStore = defineStore({
|
||||||
|
id: 'bszx-statistics',
|
||||||
|
state: (): BszxStatisticsState => ({
|
||||||
|
totalPrice: 0,
|
||||||
|
loading: false,
|
||||||
|
lastUpdateTime: null,
|
||||||
|
// 默认缓存5分钟
|
||||||
|
cacheExpiry: 5 * 60 * 1000,
|
||||||
|
refreshTimer: null
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
/**
|
||||||
|
* 获取总营业额
|
||||||
|
*/
|
||||||
|
bszxTotalPrice: (state): number => {
|
||||||
|
return safeNumber(state.totalPrice);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查缓存是否有效
|
||||||
|
*/
|
||||||
|
isCacheValid: (state): boolean => {
|
||||||
|
if (!state.lastUpdateTime) return false;
|
||||||
|
const now = Date.now();
|
||||||
|
return (now - state.lastUpdateTime) < state.cacheExpiry;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* 获取百色中学统计数据
|
||||||
|
* @param forceRefresh 是否强制刷新
|
||||||
|
*/
|
||||||
|
async fetchBszxStatistics(forceRefresh = false) {
|
||||||
|
// 如果缓存有效且不强制刷新,直接返回缓存数据
|
||||||
|
if (!forceRefresh && this.isCacheValid && this.totalPrice > 0) {
|
||||||
|
return this.totalPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const result = await bszxOrderTotal();
|
||||||
|
|
||||||
|
// 处理返回的数据 - bszxOrderTotal 返回 ShopOrder[] 数组
|
||||||
|
let totalPrice = 0;
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
// 累加所有订单的金额
|
||||||
|
result.forEach((order: any) => {
|
||||||
|
if (order.payPrice) {
|
||||||
|
totalPrice += safeNumber(order.payPrice);
|
||||||
|
} else if (order.totalPrice) {
|
||||||
|
totalPrice += safeNumber(order.totalPrice);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (typeof result === 'number') {
|
||||||
|
totalPrice = result;
|
||||||
|
} else if (typeof result === 'string') {
|
||||||
|
totalPrice = safeNumber(result);
|
||||||
|
} else if (result && typeof result === 'object' && 'totalPrice' in result) {
|
||||||
|
totalPrice = safeNumber((result as any).totalPrice);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.totalPrice = totalPrice;
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
|
||||||
|
return totalPrice;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取百色中学统计数据失败:', error);
|
||||||
|
// 发生错误时不重置现有数据,只记录错误
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新统计数据
|
||||||
|
*/
|
||||||
|
updateStatistics(data: Partial<BszxStatisticsState>) {
|
||||||
|
Object.assign(this, data);
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存
|
||||||
|
*/
|
||||||
|
clearCache() {
|
||||||
|
this.totalPrice = 0;
|
||||||
|
this.lastUpdateTime = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缓存有效期
|
||||||
|
*/
|
||||||
|
setCacheExpiry(expiry: number) {
|
||||||
|
this.cacheExpiry = expiry;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始自动刷新
|
||||||
|
* @param interval 刷新间隔(毫秒),默认5分钟
|
||||||
|
*/
|
||||||
|
startAutoRefresh(interval = 5 * 60 * 1000) {
|
||||||
|
this.stopAutoRefresh();
|
||||||
|
this.refreshTimer = window.setInterval(() => {
|
||||||
|
this.fetchBszxStatistics(true).catch(console.error);
|
||||||
|
}, interval);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止自动刷新
|
||||||
|
*/
|
||||||
|
stopAutoRefresh() {
|
||||||
|
if (this.refreshTimer) {
|
||||||
|
clearInterval(this.refreshTimer);
|
||||||
|
this.refreshTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -6,26 +6,43 @@
|
|||||||
@change="search"
|
@change="search"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
/>
|
/>
|
||||||
<a-tooltip title="获得捐款总金额" class="flex px-4">
|
<a-tooltip title="实际订单总金额(来自订单表)" class="flex px-4">
|
||||||
<span class="text-gray-400">捐款总金额:</span>
|
<span class="text-gray-400">实际订单总金额:</span>
|
||||||
<span class="text-gray-700 font-bold">¥{{ formatNumber(totalPriceAmount) }}</span>
|
<span class="text-gray-700 font-bold">¥{{ formatNumber(bszxTotalPrice) }}</span>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<a-tooltip title="排行榜统计金额(来自排行榜表)" class="flex px-4 ml-4">
|
||||||
|
<span class="text-gray-400">排行榜统计金额:</span>
|
||||||
|
<span class="text-gray-700 font-bold">¥{{ formatNumber(rankingTotalPrice) }}</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useSearch from "@/utils/use-search";
|
import useSearch from "@/utils/use-search";
|
||||||
import { watch,ref } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import { formatNumber } from 'ele-admin-pro/es';
|
import { formatNumber } from 'ele-admin-pro/es';
|
||||||
import {BszxPayRankingParam} from "@/api/bszx/bszxPayRanking/model";
|
import { BszxPayRankingParam } from "@/api/bszx/bszxPayRanking/model";
|
||||||
|
import { useBszxStatisticsStore } from '@/store/modules/bszx-statistics';
|
||||||
|
|
||||||
|
// 使用百色中学统计数据 store
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
|
||||||
|
// 从 store 中获取总金额
|
||||||
|
const bszxTotalPrice = computed(() => bszxStatisticsStore.bszxTotalPrice);
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
// 选中的角色
|
// 选中的角色
|
||||||
selection?: [];
|
selection?: [];
|
||||||
|
// 保留这个属性以保持向后兼容,但不再使用
|
||||||
totalPriceAmount?: number;
|
totalPriceAmount?: number;
|
||||||
|
// 排行榜统计金额
|
||||||
|
rankingTotalPrice?: number;
|
||||||
}>(),
|
}>(),
|
||||||
{}
|
{
|
||||||
|
rankingTotalPrice: 0
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 日期范围选择
|
// 日期范围选择
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<search
|
<search
|
||||||
@search="reload"
|
@search="reload"
|
||||||
:selection="selection"
|
:selection="selection"
|
||||||
:totalPriceAmount="totalPriceAmount.toFixed(2)"
|
:rankingTotalPrice="rankingTotalPrice"
|
||||||
@add="openEdit"
|
@add="openEdit"
|
||||||
@remove="removeBatch"
|
@remove="removeBatch"
|
||||||
@batchMove="openMove"
|
@batchMove="openMove"
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {createVNode, ref} from 'vue';
|
import {createVNode, ref, onMounted} from 'vue';
|
||||||
import {message, Modal} from 'ant-design-vue';
|
import {message, Modal} from 'ant-design-vue';
|
||||||
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
||||||
import type {EleProTable} from 'ele-admin-pro';
|
import type {EleProTable} from 'ele-admin-pro';
|
||||||
@@ -67,6 +67,10 @@ import {removeBszxPayRanking, removeBatchBszxPayRanking, ranking} from '@/api/bs
|
|||||||
import type {BszxPayRanking, BszxPayRankingParam} from '@/api/bszx/bszxPayRanking/model';
|
import type {BszxPayRanking, BszxPayRankingParam} from '@/api/bszx/bszxPayRanking/model';
|
||||||
import {getPageTitle} from "@/utils/common";
|
import {getPageTitle} from "@/utils/common";
|
||||||
import Extra from "@/views/bsyx/extra.vue";
|
import Extra from "@/views/bsyx/extra.vue";
|
||||||
|
import { useBszxStatisticsStore } from '@/store/modules/bszx-statistics';
|
||||||
|
|
||||||
|
// 使用百色中学统计数据 store
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
|
||||||
// 表格实例
|
// 表格实例
|
||||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||||
@@ -81,18 +85,23 @@ const showEdit = ref(false);
|
|||||||
const showMove = ref(false);
|
const showMove = ref(false);
|
||||||
// 加载状态
|
// 加载状态
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
// 合计总金额
|
// 排行榜总金额(本地计算)
|
||||||
const totalPriceAmount = ref<number>(0);
|
const rankingTotalPrice = ref<number>(0);
|
||||||
|
|
||||||
// 表格数据源
|
// 表格数据源
|
||||||
const datasource: DatasourceFunction = ({where}) => {
|
const datasource: DatasourceFunction = ({where}) => {
|
||||||
return ranking({...where}).then(data => {
|
return ranking({...where}).then(data => {
|
||||||
totalPriceAmount.value = 0;
|
// 计算排行榜总金额(用于对比显示)
|
||||||
data.map((item) => {
|
let total = 0;
|
||||||
|
data.forEach((item) => {
|
||||||
if (item.totalPrice) {
|
if (item.totalPrice) {
|
||||||
totalPriceAmount.value += item.totalPrice
|
total += item.totalPrice;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
rankingTotalPrice.value = total;
|
||||||
|
|
||||||
|
// 不再在这里更新 store 数据,因为这里的数据是排行榜数据,不是真实的订单统计
|
||||||
|
// store 中的数据应该来自 bszxOrderTotal API,代表真实的订单金额
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -140,6 +149,16 @@ const reload = (where?: BszxPayRankingParam) => {
|
|||||||
tableRef?.value?.reload({where: where});
|
tableRef?.value?.reload({where: where});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
// 初始化百色中学统计数据
|
||||||
|
await bszxStatisticsStore.fetchBszxStatistics();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化百色中学统计数据失败:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* 打开编辑弹窗 */
|
/* 打开编辑弹窗 */
|
||||||
const openEdit = (row?: BszxPayRanking) => {
|
const openEdit = (row?: BszxPayRanking) => {
|
||||||
current.value = row ?? null;
|
current.value = row ?? null;
|
||||||
|
|||||||
@@ -6,26 +6,43 @@
|
|||||||
@change="search"
|
@change="search"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
/>
|
/>
|
||||||
<a-tooltip title="获得捐款总金额" class="flex px-4">
|
<a-tooltip title="实际订单总金额(来自订单表)" class="flex px-4">
|
||||||
<span class="text-gray-400">捐款总金额:</span>
|
<span class="text-gray-400">实际订单总金额:</span>
|
||||||
<span class="text-gray-700 font-bold">¥{{ formatNumber(totalPriceAmount) }}</span>
|
<span class="text-gray-700 font-bold">¥{{ formatNumber(bszxTotalPrice) }}</span>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<a-tooltip title="排行榜统计金额(来自排行榜表)" class="flex px-4 ml-4">
|
||||||
|
<span class="text-gray-400">排行榜统计金额:</span>
|
||||||
|
<span class="text-gray-700 font-bold">¥{{ formatNumber(rankingTotalPrice) }}</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useSearch from "@/utils/use-search";
|
import useSearch from "@/utils/use-search";
|
||||||
import { watch,ref } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import { formatNumber } from 'ele-admin-pro/es';
|
import { formatNumber } from 'ele-admin-pro/es';
|
||||||
import {BszxPayRankingParam} from "@/api/bszx/bszxPayRanking/model";
|
import { BszxPayRankingParam } from "@/api/bszx/bszxPayRanking/model";
|
||||||
|
import { useBszxStatisticsStore } from '@/store/modules/bszx-statistics';
|
||||||
|
|
||||||
|
// 使用百色中学统计数据 store
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
|
||||||
|
// 从 store 中获取总金额
|
||||||
|
const bszxTotalPrice = computed(() => bszxStatisticsStore.bszxTotalPrice);
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
// 选中的角色
|
// 选中的角色
|
||||||
selection?: [];
|
selection?: [];
|
||||||
|
// 保留这个属性以保持向后兼容,但不再使用
|
||||||
totalPriceAmount?: number;
|
totalPriceAmount?: number;
|
||||||
|
// 排行榜统计金额
|
||||||
|
rankingTotalPrice?: number;
|
||||||
}>(),
|
}>(),
|
||||||
{}
|
{
|
||||||
|
rankingTotalPrice: 0
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 日期范围选择
|
// 日期范围选择
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<search
|
<search
|
||||||
@search="reload"
|
@search="reload"
|
||||||
:selection="selection"
|
:selection="selection"
|
||||||
:totalPriceAmount="totalPriceAmount.toFixed(2)"
|
:rankingTotalPrice="rankingTotalPrice"
|
||||||
@add="openEdit"
|
@add="openEdit"
|
||||||
@remove="removeBatch"
|
@remove="removeBatch"
|
||||||
@batchMove="openMove"
|
@batchMove="openMove"
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {createVNode, ref} from 'vue';
|
import {createVNode, ref, onMounted} from 'vue';
|
||||||
import {message, Modal} from 'ant-design-vue';
|
import {message, Modal} from 'ant-design-vue';
|
||||||
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
||||||
import type {EleProTable} from 'ele-admin-pro';
|
import type {EleProTable} from 'ele-admin-pro';
|
||||||
@@ -67,6 +67,10 @@ import {removeBszxPayRanking, removeBatchBszxPayRanking, ranking} from '@/api/bs
|
|||||||
import type {BszxPayRanking, BszxPayRankingParam} from '@/api/bszx/bszxPayRanking/model';
|
import type {BszxPayRanking, BszxPayRankingParam} from '@/api/bszx/bszxPayRanking/model';
|
||||||
import {getPageTitle} from "@/utils/common";
|
import {getPageTitle} from "@/utils/common";
|
||||||
import Extra from "@/views/bszx/extra.vue";
|
import Extra from "@/views/bszx/extra.vue";
|
||||||
|
import { useBszxStatisticsStore } from '@/store/modules/bszx-statistics';
|
||||||
|
|
||||||
|
// 使用百色中学统计数据 store
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
|
||||||
// 表格实例
|
// 表格实例
|
||||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||||
@@ -81,18 +85,23 @@ const showEdit = ref(false);
|
|||||||
const showMove = ref(false);
|
const showMove = ref(false);
|
||||||
// 加载状态
|
// 加载状态
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
// 合计总金额
|
// 排行榜总金额(本地计算)
|
||||||
const totalPriceAmount = ref<number>(0);
|
const rankingTotalPrice = ref<number>(0);
|
||||||
|
|
||||||
// 表格数据源
|
// 表格数据源
|
||||||
const datasource: DatasourceFunction = ({where}) => {
|
const datasource: DatasourceFunction = ({where}) => {
|
||||||
return ranking({...where}).then(data => {
|
return ranking({...where}).then(data => {
|
||||||
totalPriceAmount.value = 0;
|
// 计算排行榜总金额(用于对比显示)
|
||||||
data.map((item) => {
|
let total = 0;
|
||||||
|
data.forEach((item) => {
|
||||||
if(item.totalPrice){
|
if(item.totalPrice){
|
||||||
totalPriceAmount.value += item.totalPrice
|
total += item.totalPrice;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
rankingTotalPrice.value = total;
|
||||||
|
|
||||||
|
// 不再在这里更新 store 数据,因为这里的数据是排行榜数据,不是真实的订单统计
|
||||||
|
// store 中的数据应该来自 bszxOrderTotal API,代表真实的订单金额
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -140,6 +149,16 @@ const reload = (where?: BszxPayRankingParam) => {
|
|||||||
tableRef?.value?.reload({where: where});
|
tableRef?.value?.reload({where: where});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
// 初始化百色中学统计数据
|
||||||
|
await bszxStatisticsStore.fetchBszxStatistics();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化百色中学统计数据失败:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* 打开编辑弹窗 */
|
/* 打开编辑弹窗 */
|
||||||
const openEdit = (row?: BszxPayRanking) => {
|
const openEdit = (row?: BszxPayRanking) => {
|
||||||
current.value = row ?? null;
|
current.value = row ?? null;
|
||||||
|
|||||||
100
src/views/bszx/dashboard/components/search.vue
Normal file
100
src/views/bszx/dashboard/components/search.vue
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<!-- 搜索表单 -->
|
||||||
|
<template>
|
||||||
|
<a-space style="flex-wrap: wrap">
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl(`/website/field`)"
|
||||||
|
>字段扩展
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl('/website/dict')"
|
||||||
|
>字典管理
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl('/website/domain')"
|
||||||
|
>域名管理
|
||||||
|
</a-button
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl('/website/model')"
|
||||||
|
>模型管理
|
||||||
|
</a-button
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl('/website/form')"
|
||||||
|
>表单管理
|
||||||
|
</a-button
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl('/website/lang')"
|
||||||
|
>国际化
|
||||||
|
</a-button
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
@click="openUrl('/website/setting')"
|
||||||
|
>网站设置
|
||||||
|
</a-button
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
class="ele-btn-icon"
|
||||||
|
@click="clearSiteInfoCache">
|
||||||
|
清除缓存
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {watch,nextTick} from 'vue';
|
||||||
|
import {CmsWebsite} from '@/api/cms/cmsWebsite/model';
|
||||||
|
import {openUrl} from "@/utils/common";
|
||||||
|
import {message} from 'ant-design-vue';
|
||||||
|
import {removeSiteInfoCache} from "@/api/cms/cmsWebsite";
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
// 选中的角色
|
||||||
|
selection?: [];
|
||||||
|
website?: CmsWebsite;
|
||||||
|
count?: 0;
|
||||||
|
}>(),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'add'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const add = () => {
|
||||||
|
emit('add');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清除缓存
|
||||||
|
const clearSiteInfoCache = () => {
|
||||||
|
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId') + "*").then(
|
||||||
|
(msg) => {
|
||||||
|
if (msg) {
|
||||||
|
message.success(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
if(localStorage.getItem('NotActive')){
|
||||||
|
// IsActive.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.selection,
|
||||||
|
() => {
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
413
src/views/bszx/dashboard/components/websiteEdit.vue
Normal file
413
src/views/bszx/dashboard/components/websiteEdit.vue
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
<!-- 编辑弹窗 -->
|
||||||
|
<template>
|
||||||
|
<ele-modal
|
||||||
|
:width="800"
|
||||||
|
:visible="visible"
|
||||||
|
:maskClosable="false"
|
||||||
|
:maxable="maxable"
|
||||||
|
:title="isUpdate ? '编辑小程序' : '创建小程序'"
|
||||||
|
:body-style="{ paddingBottom: '28px' }"
|
||||||
|
@update:visible="updateVisible"
|
||||||
|
:confirm-loading="loading"
|
||||||
|
@ok="save"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||||
|
:wrapper-col="
|
||||||
|
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-form-item label="Logo" name="avatar">
|
||||||
|
<SelectFile
|
||||||
|
:placeholder="`请选择图片`"
|
||||||
|
:limit="1"
|
||||||
|
:data="images"
|
||||||
|
@done="chooseImage"
|
||||||
|
@del="onDeleteItem"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="账号类型" name="type">
|
||||||
|
{{ form.type }}
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="小程序名称" name="websiteName">
|
||||||
|
<a-input
|
||||||
|
allow-clear
|
||||||
|
placeholder="请输入小程序名称"
|
||||||
|
v-model:value="form.websiteName"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="网站域名" name="domain" v-if="form.type == 10">
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.domain"
|
||||||
|
placeholder="huawei.com"
|
||||||
|
>
|
||||||
|
<template #addonBefore>
|
||||||
|
<a-select v-model:value="form.prefix" style="width: 90px">
|
||||||
|
<a-select-option value="http://">http://</a-select-option>
|
||||||
|
<a-select-option value="https://">https://</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="AppId" name="websiteCode" v-if="form.type == 20">
|
||||||
|
<a-input
|
||||||
|
allow-clear
|
||||||
|
placeholder="请输入AppId"
|
||||||
|
v-model:value="form.websiteCode"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="小程序描述" name="comments">
|
||||||
|
<a-textarea
|
||||||
|
:rows="4"
|
||||||
|
:maxlength="200"
|
||||||
|
placeholder="请输入小程序描述"
|
||||||
|
v-model:value="form.comments"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="SEO关键词" name="keywords">
|
||||||
|
<a-textarea
|
||||||
|
:rows="4"
|
||||||
|
:maxlength="200"
|
||||||
|
placeholder="请输入SEO关键词"
|
||||||
|
v-model:value="form.keywords"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- <a-form-item label="全局样式" name="style">-->
|
||||||
|
<!-- <a-textarea-->
|
||||||
|
<!-- :rows="4"-->
|
||||||
|
<!-- :maxlength="200"-->
|
||||||
|
<!-- placeholder="全局样式"-->
|
||||||
|
<!-- v-model:value="form.style"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </a-form-item>-->
|
||||||
|
<!-- <a-form-item label="小程序类型" name="websiteType">-->
|
||||||
|
<!-- <a-select-->
|
||||||
|
<!-- :options="websiteType"-->
|
||||||
|
<!-- :value="form.websiteType"-->
|
||||||
|
<!-- placeholder="请选择主体类型"-->
|
||||||
|
<!-- @change="onCmsWebsiteType"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </a-form-item>-->
|
||||||
|
<!-- <a-form-item label="当前版本" name="version">-->
|
||||||
|
<!-- <a-tag color="red" v-if="form.version === 10">标准版</a-tag>-->
|
||||||
|
<!-- <a-tag color="green" v-if="form.version === 20">专业版</a-tag>-->
|
||||||
|
<!-- <a-tag color="cyan" v-if="form.version === 30">永久授权</a-tag>-->
|
||||||
|
<!-- </a-form-item>-->
|
||||||
|
<a-form-item label="状态" name="running">
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="form.running"
|
||||||
|
:disabled="form.running == 4 || form.running == 5"
|
||||||
|
>
|
||||||
|
<a-radio :value="1">运行中</a-radio>
|
||||||
|
<a-radio :value="2">维护中</a-radio>
|
||||||
|
<a-radio :value="3">已关闭</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="form.running == 2" label="维护说明" name="statusText">
|
||||||
|
<a-textarea
|
||||||
|
:rows="4"
|
||||||
|
:maxlength="200"
|
||||||
|
placeholder="状态说明"
|
||||||
|
v-model:value="form.statusText"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- <a-divider style="margin-bottom: 24px" />-->
|
||||||
|
</a-form>
|
||||||
|
</ele-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref, reactive, watch} from 'vue';
|
||||||
|
import {Form, message} from 'ant-design-vue';
|
||||||
|
import {assignObject, uuid} from 'ele-admin-pro';
|
||||||
|
import {addCmsWebsite, updateCmsWebsite} from '@/api/cms/cmsWebsite';
|
||||||
|
import {CmsWebsite} from '@/api/cms/cmsWebsite/model';
|
||||||
|
import {useThemeStore} from '@/store/modules/theme';
|
||||||
|
import {storeToRefs} from 'pinia';
|
||||||
|
import {FormInstance, type Rule} from 'ant-design-vue/es/form';
|
||||||
|
import {ItemType} from 'ele-admin-pro/es/ele-image-upload/types';
|
||||||
|
import {FileRecord} from '@/api/system/file/model';
|
||||||
|
import {checkExistence} from '@/api/cms/cmsDomain';
|
||||||
|
import {updateCmsDomain} from '@/api/cms/cmsDomain';
|
||||||
|
import {updateTenant} from "@/api/system/tenant";
|
||||||
|
|
||||||
|
// 是否是修改
|
||||||
|
const isUpdate = ref(false);
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
// 是否开启响应式布局
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
const {styleResponsive} = storeToRefs(themeStore);
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
// 弹窗是否打开
|
||||||
|
visible: boolean;
|
||||||
|
// 修改回显的数据
|
||||||
|
data?: CmsWebsite | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'done'): void;
|
||||||
|
(e: 'update:visible', visible: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 提交状态
|
||||||
|
const loading = ref(false);
|
||||||
|
// 是否显示最大化切换按钮
|
||||||
|
const maxable = ref(true);
|
||||||
|
// 表格选中数据
|
||||||
|
const formRef = ref<FormInstance | null>(null);
|
||||||
|
const images = ref<ItemType[]>([]);
|
||||||
|
const websiteQrcode = ref<ItemType[]>([]);
|
||||||
|
const oldDomain = ref();
|
||||||
|
const files = ref<ItemType[]>([]);
|
||||||
|
|
||||||
|
// 用户信息
|
||||||
|
const form = reactive<CmsWebsite>({
|
||||||
|
websiteId: undefined,
|
||||||
|
websiteLogo: undefined,
|
||||||
|
websiteName: undefined,
|
||||||
|
websiteCode: undefined,
|
||||||
|
type: 20,
|
||||||
|
files: undefined,
|
||||||
|
keywords: '',
|
||||||
|
prefix: '',
|
||||||
|
domain: '',
|
||||||
|
adminUrl: '',
|
||||||
|
style: '',
|
||||||
|
icpNo: undefined,
|
||||||
|
email: undefined,
|
||||||
|
version: undefined,
|
||||||
|
websiteType: '',
|
||||||
|
running: 1,
|
||||||
|
expirationTime: undefined,
|
||||||
|
sortNumber: undefined,
|
||||||
|
comments: undefined,
|
||||||
|
status: undefined,
|
||||||
|
statusText: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
/* 更新visible */
|
||||||
|
const updateVisible = (value: boolean) => {
|
||||||
|
emit('update:visible', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = reactive({
|
||||||
|
// comments: [
|
||||||
|
// {
|
||||||
|
// required: true,
|
||||||
|
// type: 'string',
|
||||||
|
// message: '请填写小程序描述',
|
||||||
|
// trigger: 'blur'
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
keywords: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写SEO关键词',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
running: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'number',
|
||||||
|
message: '请选择小程序状态',
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
domain: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写小程序域名',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
websiteCode: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写小程序码',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// websiteCode: [
|
||||||
|
// {
|
||||||
|
// required: true,
|
||||||
|
// type: 'string',
|
||||||
|
// message: '该域名已被使用',
|
||||||
|
// validator: (_rule: Rule, value: string) => {
|
||||||
|
// return new Promise<void>((resolve, reject) => {
|
||||||
|
// if (!value) {
|
||||||
|
// return reject('请输入二级域名');
|
||||||
|
// }
|
||||||
|
// checkExistence('domain', `${value}.wsdns.cn`)
|
||||||
|
// .then(() => {
|
||||||
|
// if (value === oldDomain.value) {
|
||||||
|
// return resolve();
|
||||||
|
// }
|
||||||
|
// reject('已存在');
|
||||||
|
// })
|
||||||
|
// .catch(() => {
|
||||||
|
// resolve();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// trigger: 'blur'
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
adminUrl: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写小程序后台管理地址',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
icpNo: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写ICP备案号',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
appSecret: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写小程序秘钥',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
websiteName: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
message: '请填写小程序信息名称',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const chooseImage = (data: FileRecord) => {
|
||||||
|
images.value.push({
|
||||||
|
uid: data.id,
|
||||||
|
url: data.path,
|
||||||
|
status: 'done'
|
||||||
|
});
|
||||||
|
form.websiteLogo = data.downloadUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteItem = (index: number) => {
|
||||||
|
images.value.splice(index, 1);
|
||||||
|
form.websiteLogo = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const chooseFile = (data: FileRecord) => {
|
||||||
|
form.websiteCode = data.url;
|
||||||
|
files.value.push({
|
||||||
|
uid: data.id,
|
||||||
|
url: data.url,
|
||||||
|
status: 'done'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteFile = (index: number) => {
|
||||||
|
files.value.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// const onWebsiteType = (text: string) => {
|
||||||
|
// form.websiteType = text;
|
||||||
|
// };
|
||||||
|
|
||||||
|
const {resetFields} = useForm(form, rules);
|
||||||
|
|
||||||
|
/* 保存编辑 */
|
||||||
|
const save = () => {
|
||||||
|
if (!formRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(() => {
|
||||||
|
loading.value = true;
|
||||||
|
const saveOrUpdate = isUpdate.value ? updateCmsWebsite : addCmsWebsite;
|
||||||
|
if (!isUpdate.value) {
|
||||||
|
updateVisible(false);
|
||||||
|
message.loading('创建过程中请勿刷新页面!', 0)
|
||||||
|
}
|
||||||
|
const formData = {
|
||||||
|
...form,
|
||||||
|
type: 20,
|
||||||
|
adminUrl: `mp.websoft.top`,
|
||||||
|
files: JSON.stringify(files.value),
|
||||||
|
};
|
||||||
|
saveOrUpdate(formData)
|
||||||
|
.then((msg) => {
|
||||||
|
loading.value = false;
|
||||||
|
updateVisible(false);
|
||||||
|
updateCmsDomain({
|
||||||
|
websiteId: form.websiteId,
|
||||||
|
domain: `${localStorage.getItem('TenantId')}.shoplnk.cn`
|
||||||
|
});
|
||||||
|
updateTenant({
|
||||||
|
tenantName: `${form.websiteName}`
|
||||||
|
}).then(() => {
|
||||||
|
})
|
||||||
|
localStorage.setItem('Domain', `${form.websiteCode}.shoplnk.cn`);
|
||||||
|
localStorage.setItem('WebsiteId', `${form.websiteId}`);
|
||||||
|
localStorage.setItem('WebsiteName', `${form.websiteName}`);
|
||||||
|
message.destroy();
|
||||||
|
message.success(msg);
|
||||||
|
// window.location.reload();
|
||||||
|
emit('done');
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
loading.value = false;
|
||||||
|
message.destroy();
|
||||||
|
message.error(e.message);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(visible) => {
|
||||||
|
if (visible) {
|
||||||
|
images.value = [];
|
||||||
|
files.value = [];
|
||||||
|
websiteQrcode.value = [];
|
||||||
|
if (props.data?.websiteId) {
|
||||||
|
assignObject(form, props.data);
|
||||||
|
if (props.data.websiteLogo) {
|
||||||
|
images.value.push({
|
||||||
|
uid: uuid(),
|
||||||
|
url: props.data.websiteLogo,
|
||||||
|
status: 'done'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.data.files) {
|
||||||
|
files.value = JSON.parse(props.data.files);
|
||||||
|
}
|
||||||
|
if (props.data.websiteCode) {
|
||||||
|
oldDomain.value = props.data.websiteCode;
|
||||||
|
}
|
||||||
|
isUpdate.value = true;
|
||||||
|
} else {
|
||||||
|
isUpdate.value = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resetFields();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{immediate: true}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
264
src/views/bszx/dashboard/index.vue
Normal file
264
src/views/bszx/dashboard/index.vue
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
<template>
|
||||||
|
<a-page-header :show-back="false">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<!-- 应用基本信息卡片 -->
|
||||||
|
<a-col :span="24" style="margin-bottom: 16px">
|
||||||
|
<a-card title="概况" :bordered="false">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-image
|
||||||
|
:width="80"
|
||||||
|
:height="80"
|
||||||
|
:preview="false"
|
||||||
|
style="border-radius: 8px"
|
||||||
|
:src="siteStore.websiteLogo"
|
||||||
|
fallback="/logo.png"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="14">
|
||||||
|
<div class="system-info">
|
||||||
|
<h2 class="ele-text-heading">{{ siteStore.websiteName }}</h2>
|
||||||
|
<p class="ele-text-secondary">{{ siteStore.websiteComments }}</p>
|
||||||
|
<a-space>
|
||||||
|
<a-tag color="blue">版本 {{ systemInfo.version }}</a-tag>
|
||||||
|
<a-tag color="green">{{ systemInfo.status }}</a-tag>
|
||||||
|
<a-popover title="小程序码">
|
||||||
|
<template #content>
|
||||||
|
<p><img :src="siteStore.websiteDarkLogo" alt="小程序码" width="300" height="300"></p>
|
||||||
|
</template>
|
||||||
|
<a-tag>
|
||||||
|
<QrcodeOutlined/>
|
||||||
|
</a-tag>
|
||||||
|
</a-popover>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
<div class="flex justify-center items-center h-full w-full">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<!-- 统计数据卡片 -->
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card">
|
||||||
|
<a-statistic
|
||||||
|
title="用户总数"
|
||||||
|
:value="userCount"
|
||||||
|
:value-style="{ color: '#3f8600' }"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<UserOutlined/>
|
||||||
|
</template>
|
||||||
|
</a-statistic>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card">
|
||||||
|
<a-statistic
|
||||||
|
title="订单总数"
|
||||||
|
:value="orderCount"
|
||||||
|
:value-style="{ color: '#1890ff' }"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<AccountBookOutlined/>
|
||||||
|
</template>
|
||||||
|
</a-statistic>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card">
|
||||||
|
<a-statistic
|
||||||
|
title="总营业额"
|
||||||
|
:value="totalBszxPrice"
|
||||||
|
:value-style="{ color: '#cf1322' }"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<MoneyCollectOutlined/>
|
||||||
|
</template>
|
||||||
|
</a-statistic>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card">
|
||||||
|
<a-statistic
|
||||||
|
title="系统运行天数"
|
||||||
|
:value="runDays"
|
||||||
|
suffix="天"
|
||||||
|
:value-style="{ color: '#722ed1' }"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<ClockCircleOutlined/>
|
||||||
|
</template>
|
||||||
|
</a-statistic>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<!-- 系统基本信息 -->
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card title="基本信息" :bordered="false">
|
||||||
|
<a-descriptions :column="1" size="small">
|
||||||
|
<a-descriptions-item label="系统名称">
|
||||||
|
{{ systemInfo.name }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="版本号">
|
||||||
|
{{ systemInfo.version }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="部署环境">
|
||||||
|
{{ systemInfo.environment }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="数据库">
|
||||||
|
{{ systemInfo.database }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="服务器">
|
||||||
|
{{ systemInfo.server }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="创建时间">
|
||||||
|
{{ siteInfo?.createTime }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="到期时间">
|
||||||
|
{{ siteInfo?.expirationTime }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="技术支持">
|
||||||
|
<span class="cursor-pointer" @click="openNew(`https://websoft.top/order/3429.html`)">网宿软件</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<!-- 快捷操作 -->
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card title="快捷操作" :bordered="false">
|
||||||
|
<a-space direction="vertical" style="width: 100%">
|
||||||
|
<a-button type="primary" block @click="$router.push('/website/index')">
|
||||||
|
<ShopOutlined/>
|
||||||
|
站点管理
|
||||||
|
</a-button>
|
||||||
|
<a-button block @click="$router.push('/website/order')">
|
||||||
|
<CalendarOutlined/>
|
||||||
|
订单管理
|
||||||
|
</a-button>
|
||||||
|
<a-button block @click="$router.push('/system/user')">
|
||||||
|
<UserOutlined/>
|
||||||
|
用户管理
|
||||||
|
</a-button>
|
||||||
|
<a-button block @click="$router.push('/system/login-record')">
|
||||||
|
<FileTextOutlined/>
|
||||||
|
系统日志
|
||||||
|
</a-button>
|
||||||
|
<a-button block @click="$router.push('/system/setting')">
|
||||||
|
<SettingOutlined/>
|
||||||
|
系统设置
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-page-header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref, onMounted, onUnmounted, computed} from 'vue';
|
||||||
|
import {
|
||||||
|
UserOutlined,
|
||||||
|
CalendarOutlined,
|
||||||
|
QrcodeOutlined,
|
||||||
|
ShopOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
AccountBookOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
|
MoneyCollectOutlined
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import {openNew} from "@/utils/common";
|
||||||
|
import { useSiteStore } from '@/store/modules/site';
|
||||||
|
import { useStatisticsStore } from '@/store/modules/statistics';
|
||||||
|
import { useBszxStatisticsStore } from '@/store/modules/bszx-statistics';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
// 使用状态管理
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
const bszxStatisticsStore = useBszxStatisticsStore();
|
||||||
|
|
||||||
|
// 从 store 中获取响应式数据
|
||||||
|
const { siteInfo, loading: siteLoading } = storeToRefs(siteStore);
|
||||||
|
const { loading: statisticsLoading } = storeToRefs(statisticsStore);
|
||||||
|
const { loading: bszxLoading } = storeToRefs(bszxStatisticsStore);
|
||||||
|
|
||||||
|
// 系统信息
|
||||||
|
const systemInfo = ref({
|
||||||
|
name: '小程序开发',
|
||||||
|
description: '基于Spring、SpringBoot、SpringMVC等技术栈构建的前后端分离开发平台',
|
||||||
|
version: '2.0.0',
|
||||||
|
status: '运行中',
|
||||||
|
logo: '/logo.png',
|
||||||
|
environment: '生产环境',
|
||||||
|
database: 'MySQL 8.0',
|
||||||
|
server: 'Linux CentOS 7.9',
|
||||||
|
expirationTime: '2024-01-01 09:00:00'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const runDays = computed(() => siteStore.runDays);
|
||||||
|
const userCount = computed(() => statisticsStore.userCount);
|
||||||
|
const orderCount = computed(() => statisticsStore.orderCount);
|
||||||
|
const totalBszxPrice = computed(() => bszxStatisticsStore.bszxTotalPrice);
|
||||||
|
|
||||||
|
// 加载状态
|
||||||
|
const loading = computed(() => siteLoading.value || statisticsLoading.value || bszxLoading.value);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// 加载网站信息和统计数据
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
siteStore.fetchSiteInfo(),
|
||||||
|
statisticsStore.fetchStatistics(),
|
||||||
|
bszxStatisticsStore.fetchBszxStatistics() // 加载百色中学统计数据
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 开始自动刷新统计数据(每5分钟)
|
||||||
|
statisticsStore.startAutoRefresh();
|
||||||
|
bszxStatisticsStore.startAutoRefresh();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 组件卸载时停止自动刷新
|
||||||
|
statisticsStore.stopAutoRefresh();
|
||||||
|
bszxStatisticsStore.stopAutoRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.system-info h2 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card :deep(.ant-statistic-title) {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card :deep(.ant-statistic-content) {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user