|
|
@@ -217,10 +217,13 @@ export class AdminDashboard implements OnInit, AfterViewInit, OnDestroy {
|
|
|
// 加载项目统计数据
|
|
|
private async loadProjectStats(): Promise<void> {
|
|
|
try {
|
|
|
+ const companyId = this.company?.id || 'cDL6R1hgSi';
|
|
|
+ console.log(`🔍 [项目统计] 使用公司ID: ${companyId}`);
|
|
|
+
|
|
|
const companyPointer = {
|
|
|
__type: 'Pointer',
|
|
|
className: 'Company',
|
|
|
- objectId: this.company?.id || 'cDL6R1hgSi'
|
|
|
+ objectId: companyId
|
|
|
};
|
|
|
|
|
|
// 总项目数
|
|
|
@@ -229,24 +232,7 @@ export class AdminDashboard implements OnInit, AfterViewInit, OnDestroy {
|
|
|
totalProjectQuery.notEqualTo('isDeleted', true);
|
|
|
const totalProjects = await totalProjectQuery.count();
|
|
|
this.stats.totalProjects.set(totalProjects);
|
|
|
-
|
|
|
- // 进行中项目数
|
|
|
- // 参考客服仪表板:排除待分配、已完成、已取消状态
|
|
|
- const activeProjectQuery = new Parse.Query('Project');
|
|
|
- activeProjectQuery.equalTo('company', companyPointer);
|
|
|
- activeProjectQuery.notEqualTo('isDeleted', true);
|
|
|
-
|
|
|
- // 排除状态
|
|
|
- activeProjectQuery.notEqualTo('status', '待分配');
|
|
|
- activeProjectQuery.notEqualTo('status', '已完成');
|
|
|
- activeProjectQuery.notEqualTo('status', '已取消');
|
|
|
-
|
|
|
- // 排除订单分配阶段的项目
|
|
|
- activeProjectQuery.notEqualTo('currentStage', '订单分配');
|
|
|
- activeProjectQuery.notEqualTo('stage', '订单分配');
|
|
|
-
|
|
|
- const activeProjects = await activeProjectQuery.count();
|
|
|
- this.stats.activeProjects.set(activeProjects);
|
|
|
+ console.log(`📊 [项目统计] 总项目数: ${totalProjects}`);
|
|
|
|
|
|
// 已完成项目数
|
|
|
// 状态为"已完成"或者阶段为"售后归档"
|
|
|
@@ -254,17 +240,37 @@ export class AdminDashboard implements OnInit, AfterViewInit, OnDestroy {
|
|
|
completedQuery1.equalTo('company', companyPointer);
|
|
|
completedQuery1.notEqualTo('isDeleted', true);
|
|
|
completedQuery1.equalTo('status', '已完成');
|
|
|
+ const completedByStatus = await completedQuery1.count();
|
|
|
+ console.log(` - 状态为'已完成'的项目: ${completedByStatus}`);
|
|
|
|
|
|
const completedQuery2 = new Parse.Query('Project');
|
|
|
completedQuery2.equalTo('company', companyPointer);
|
|
|
completedQuery2.notEqualTo('isDeleted', true);
|
|
|
completedQuery2.equalTo('currentStage', '售后归档');
|
|
|
+ const completedByStage = await completedQuery2.count();
|
|
|
+ console.log(` - 阶段为'售后归档'的项目: ${completedByStage}`);
|
|
|
|
|
|
const completedProjectQuery = Parse.Query.or(completedQuery1, completedQuery2);
|
|
|
const completedProjects = await completedProjectQuery.count();
|
|
|
this.stats.completedProjects.set(completedProjects);
|
|
|
+ console.log(`📊 [项目统计] 已完成项目数: ${completedProjects}`);
|
|
|
|
|
|
- console.log(`✅ 项目统计: 总计${totalProjects}, 进行中${activeProjects}, 已完成${completedProjects}`);
|
|
|
+ // 进行中项目数
|
|
|
+ // 🔥 修复:进行中 = 总项目数 - 已完成项目数 - 已取消项目数
|
|
|
+ const cancelledQuery = new Parse.Query('Project');
|
|
|
+ cancelledQuery.equalTo('company', companyPointer);
|
|
|
+ cancelledQuery.notEqualTo('isDeleted', true);
|
|
|
+ cancelledQuery.equalTo('status', '已取消');
|
|
|
+
|
|
|
+ const cancelledProjects = await cancelledQuery.count();
|
|
|
+ console.log(` - 已取消项目数: ${cancelledProjects}`);
|
|
|
+
|
|
|
+ // 进行中 = 总数 - 已完成 - 已取消
|
|
|
+ const activeProjects = totalProjects - completedProjects - cancelledProjects;
|
|
|
+ this.stats.activeProjects.set(Math.max(0, activeProjects));
|
|
|
+ console.log(`📊 [项目统计] 进行中项目数: ${activeProjects} (${totalProjects} - ${completedProjects} - ${cancelledProjects})`);
|
|
|
+
|
|
|
+ console.log(`✅ 项目统计完成: 总计${totalProjects}, 进行中${activeProjects}, 已完成${completedProjects}`);
|
|
|
} catch (error) {
|
|
|
console.error('❌ 项目统计加载失败:', error);
|
|
|
throw error;
|
|
|
@@ -343,69 +349,110 @@ export class AdminDashboard implements OnInit, AfterViewInit, OnDestroy {
|
|
|
// 加载收入统计数据
|
|
|
private async loadRevenueStats(): Promise<void> {
|
|
|
try {
|
|
|
+ const companyId = this.company?.id || 'cDL6R1hgSi';
|
|
|
+ console.log(`🔍 [收入统计] 使用公司ID: ${companyId}`);
|
|
|
+
|
|
|
const companyPointer = {
|
|
|
__type: 'Pointer',
|
|
|
className: 'Company',
|
|
|
- objectId: this.company?.id || 'cDL6R1hgSi'
|
|
|
+ objectId: companyId
|
|
|
};
|
|
|
|
|
|
- // 🔥 修复:从ProjectPayment表获取已收到的尾款(真实收入)
|
|
|
- // 而不是从Project.data.pricing获取报价金额
|
|
|
+ // 🔥 修复:从售后归档阶段的项目中获取已支付的尾款
|
|
|
let totalRevenue = 0;
|
|
|
|
|
|
try {
|
|
|
- // 查询所有ProjectPayment记录(已收到的支付)
|
|
|
- const paymentQuery = new Parse.Query('ProjectPayment');
|
|
|
- paymentQuery.equalTo('company', companyPointer);
|
|
|
- paymentQuery.notEqualTo('isDeleted', true);
|
|
|
- paymentQuery.limit(1000);
|
|
|
+ // 1️⃣ 查询所有在"售后归档"阶段的项目
|
|
|
+ const projectQuery = new Parse.Query('Project');
|
|
|
+ projectQuery.equalTo('company', companyPointer);
|
|
|
+ projectQuery.notEqualTo('isDeleted', true);
|
|
|
+ projectQuery.equalTo('currentStage', '售后归档');
|
|
|
+ projectQuery.limit(1000);
|
|
|
|
|
|
- const payments = await paymentQuery.find();
|
|
|
+ const aftercareProjects = await projectQuery.find();
|
|
|
+ console.log(`📊 [收入统计] 找到 ${aftercareProjects.length} 个售后归档项目`);
|
|
|
|
|
|
- console.log(`📊 找到 ${payments.length} 条支付记录`);
|
|
|
-
|
|
|
- // 统计所有已收到的支付金额
|
|
|
- payments.forEach(payment => {
|
|
|
- const amount = payment.get('amount') || 0;
|
|
|
- if (amount > 0) {
|
|
|
- totalRevenue += amount;
|
|
|
- const projectId = payment.get('project')?.id || '未知项目';
|
|
|
- console.log(` 支付记录 ${projectId}: ¥${amount.toLocaleString()}`);
|
|
|
+ // 2️⃣ 从每个项目的data字段中获取已支付的尾款
|
|
|
+ let projectsWithPayment = 0;
|
|
|
+ for (const project of aftercareProjects) {
|
|
|
+ const data = project.get('data') || {};
|
|
|
+ const projectTitle = project.get('title') || '未命名项目';
|
|
|
+
|
|
|
+ // 检查多个可能的尾款字段
|
|
|
+ const finalPayment =
|
|
|
+ data.finalPaymentReceived || // 已收到的尾款
|
|
|
+ data.finalPayment || // 尾款
|
|
|
+ data.paidFinalPayment || // 已支付尾款
|
|
|
+ data.receivedFinalPayment || // 已收尾款
|
|
|
+ 0;
|
|
|
+
|
|
|
+ if (finalPayment > 0) {
|
|
|
+ totalRevenue += finalPayment;
|
|
|
+ projectsWithPayment++;
|
|
|
+ console.log(` ✅ 项目 "${projectTitle}": ¥${finalPayment.toLocaleString()}`);
|
|
|
+ } else {
|
|
|
+ console.log(` ⚠️ 项目 "${projectTitle}": 没有尾款数据`);
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
|
|
|
- console.log(`✅ 收入统计: 已收到尾款 ¥${totalRevenue.toLocaleString()} (来自${payments.length}条支付记录)`);
|
|
|
- } catch (paymentError) {
|
|
|
- console.warn('⚠️ ProjectPayment查询失败,尝试从ProjectFile(payment_voucher)获取:', paymentError);
|
|
|
+ console.log(`✅ 收入统计: 已收到尾款 ¥${totalRevenue.toLocaleString()} (${projectsWithPayment}/${aftercareProjects.length}个项目有支付)`);
|
|
|
+ } catch (projectError) {
|
|
|
+ console.warn('⚠️ 项目查询失败,尝试从ProjectPayment表获取:', projectError);
|
|
|
|
|
|
- // 降级方案:从ProjectFile表的payment_voucher类型获取
|
|
|
+ // 降级方案1:从ProjectPayment表获取
|
|
|
try {
|
|
|
- const fileQuery = new Parse.Query('ProjectFile');
|
|
|
- fileQuery.equalTo('company', companyPointer);
|
|
|
- fileQuery.equalTo('fileType', 'payment_voucher');
|
|
|
- fileQuery.notEqualTo('isDeleted', true);
|
|
|
- fileQuery.limit(1000);
|
|
|
+ const paymentQuery = new Parse.Query('ProjectPayment');
|
|
|
+ paymentQuery.equalTo('company', companyPointer);
|
|
|
+ paymentQuery.notEqualTo('isDeleted', true);
|
|
|
+ paymentQuery.limit(1000);
|
|
|
|
|
|
- const voucherFiles = await fileQuery.find();
|
|
|
+ const payments = await paymentQuery.find();
|
|
|
+ console.log(`📊 [收入统计] 找到 ${payments.length} 条支付记录`);
|
|
|
|
|
|
- voucherFiles.forEach(file => {
|
|
|
- const data = file.get('data') || {};
|
|
|
- const aiAnalysis = data.aiAnalysis || {};
|
|
|
- const amount = aiAnalysis.amount || 0;
|
|
|
-
|
|
|
+ payments.forEach(payment => {
|
|
|
+ const amount = payment.get('amount') || 0;
|
|
|
if (amount > 0) {
|
|
|
totalRevenue += amount;
|
|
|
+ console.log(` ✅ 支付记录: ¥${amount.toLocaleString()}`);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- console.log(`✅ 收入统计(降级): 已收到尾款 ¥${totalRevenue.toLocaleString()} (来自${voucherFiles.length}张凭证)`);
|
|
|
- } catch (fallbackError) {
|
|
|
- console.error('❌ 降级方案也失败:', fallbackError);
|
|
|
- // 继续使用totalRevenue = 0
|
|
|
+ console.log(`✅ 收入统计(降级1): 已收到尾款 ¥${totalRevenue.toLocaleString()} (来自${payments.length}条支付记录)`);
|
|
|
+ } catch (paymentError) {
|
|
|
+ console.warn('⚠️ ProjectPayment查询失败,尝试从ProjectFile(payment_voucher)获取:', paymentError);
|
|
|
+
|
|
|
+ // 降级方案2:从ProjectFile表的payment_voucher类型获取
|
|
|
+ try {
|
|
|
+ const fileQuery = new Parse.Query('ProjectFile');
|
|
|
+ fileQuery.equalTo('company', companyPointer);
|
|
|
+ fileQuery.equalTo('fileType', 'payment_voucher');
|
|
|
+ fileQuery.notEqualTo('isDeleted', true);
|
|
|
+ fileQuery.limit(1000);
|
|
|
+
|
|
|
+ const voucherFiles = await fileQuery.find();
|
|
|
+ console.log(`📊 [收入统计] 找到 ${voucherFiles.length} 张支付凭证`);
|
|
|
+
|
|
|
+ voucherFiles.forEach(file => {
|
|
|
+ const data = file.get('data') || {};
|
|
|
+ const aiAnalysis = data.aiAnalysis || {};
|
|
|
+ const amount = aiAnalysis.amount || 0;
|
|
|
+
|
|
|
+ if (amount > 0) {
|
|
|
+ totalRevenue += amount;
|
|
|
+ console.log(` ✅ 凭证: ¥${amount.toLocaleString()}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log(`✅ 收入统计(降级2): 已收到尾款 ¥${totalRevenue.toLocaleString()} (来自${voucherFiles.length}张凭证)`);
|
|
|
+ } catch (fallbackError) {
|
|
|
+ console.error('❌ 降级方案也失败:', fallbackError);
|
|
|
+ // 继续使用totalRevenue
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this.stats.totalRevenue.set(totalRevenue);
|
|
|
+ console.log(`💰 [收入统计] 最终总收入: ¥${totalRevenue.toLocaleString()}`);
|
|
|
} catch (error) {
|
|
|
console.error('❌ 收入统计加载失败:', error);
|
|
|
throw error;
|