完成客服项目列表页面与Parse Server真实数据的集成,实现项目数据的正确显示和渲染,并优化UI样式。
localStorage.getItem('company')获取公司IDProfileService.getCurrentProfile()获取公司信息参考了team-leader和admin的实现方式,确保数据加载的稳定性
// 初始化用户和公司信息
private async initializeUserAndCompany(): Promise<void> {
try {
// 方法1: 从localStorage获取公司ID(参考team-leader的实现)
const companyId = localStorage.getItem('company');
if (companyId) {
const CompanyClass = Parse.Object.extend('Company');
this.company = new CompanyClass();
this.company.id = companyId;
console.log('✅ 从localStorage加载公司ID:', companyId);
} else {
// 方法2: 从Profile获取公司信息
this.currentProfile = await this.profileService.getCurrentProfile();
// ...
}
} catch (error) {
console.error('❌ 初始化用户和公司信息失败:', error);
this.loadError.set('加载用户信息失败,请刷新页面重试');
}
}
company: 等于当前公司指针isDeleted: 不等于true(兼容没有该字段的数据)contact, assignee, ownerupdatedAt降序限制:最多500个项目
const ProjectQuery = new Parse.Query('Project');
ProjectQuery.equalTo('company', this.getCompanyPointer());
ProjectQuery.notEqualTo('isDeleted', true);
ProjectQuery.include('contact', 'assignee', 'owner');
ProjectQuery.descending('updatedAt');
ProjectQuery.limit(500);
const projectObjects = await ProjectQuery.find();
将Parse Server的Project对象转换为前端ProjectListItem格式:
id: 项目IDname: 项目标题(title)customerName: 联系人姓名(contact.name)status: 项目状态(进行中、已完成、已暂停、已延期)currentStage: 当前阶段(订单分配、需求沟通、建模、软装、渲染、后期、尾款结算、投诉处理等)assigneeName: 负责人姓名(assignee.name)deadline: 截止日期按照业务需求,将项目分为四个阶段:
!assigneeId || assigneeId.trim() === '')currentStage === '订单分配'currentStage为:需求沟通、方案确认currentStage为:建模、软装、渲染、后期、尾款结算status === '已完成'或currentStage为:投诉处理、客户评价
// 看板分组逻辑
private isOrderAssignment(p: Project): boolean {
return !p.assigneeId || p.assigneeId.trim() === '' || p.currentStage === '订单分配';
}
private isRequirementsConfirmation(p: Project): boolean {
const requirementStages: ProjectStage[] = ['需求沟通', '方案确认'];
return !this.isAftercare(p) && !this.isOrderAssignment(p) && requirementStages.includes(p.currentStage);
}
private isDeliveryExecution(p: Project): boolean {
const deliveryStages: ProjectStage[] = ['建模', '软装', '渲染', '后期', '尾款结算'];
return !this.isAftercare(p) && !this.isOrderAssignment(p) && deliveryStages.includes(p.currentStage);
}
private isAftercare(p: Project): boolean {
const aftercareStages: ProjectStage[] = ['投诉处理', '客户评价'];
return p.status === '已完成' || aftercareStages.includes(p.currentStage);
}
添加了加载动画和提示:
@if (isLoading()) {
<div class="loading-container">
<div class="loading-spinner"></div>
<p>正在加载项目数据...</p>
</div>
}
样式特点:
添加了错误提示和重试按钮:
@if (loadError()) {
<div class="error-container">
<svg>...</svg>
<p>{{ loadError() }}</p>
<button class="retry-btn" (click)="loadProjects()">重试</button>
</div>
}
每个看板列都有空状态提示:
@if (getProjectsByColumn(col.id).length === 0) {
<div class="empty-column">
<svg>...</svg>
<p>暂无项目</p>
</div>
}
现代化设计:
交互动画:
装饰元素:
悬停时显示,增强视觉反馈
.project-content .kanban-card {
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
padding: 18px;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, $primary-color, lighten($primary-color, 15%));
opacity: 0;
transition: opacity 0.25s ease;
}
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
&::before {
opacity: 1;
}
}
}
"待分配"徽章采用渐变背景和脉动动画:
.project-content .pending-badge {
background: linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%);
border-radius: 14px;
box-shadow: 0 2px 4px rgba($warning-color, 0.1);
animation: pulse-badge 2s ease-in-out infinite;
}
@keyframes pulse-badge {
0%, 100% {
box-shadow: 0 2px 4px rgba($warning-color, 0.1);
}
50% {
box-shadow: 0 2px 8px rgba($warning-color, 0.25);
}
}
添加了详细的控制台日志,便于调试:
❌ 错误信息的详细输出
console.log(`✅ 从Parse Server加载了 ${projectObjects.length} 个项目`);
if (projectObjects.length === 0) {
console.warn('⚠️ 未找到项目数据,请检查:');
console.warn('1. Parse Server中是否有Project数据');
console.warn('2. 当前公司ID:', this.company.id);
console.warn('3. 数据是否正确关联到当前公司');
}
yss-project/src/app/pages/customer-service/project-list/project-list.ts
yss-project/src/app/pages/customer-service/project-list/project-list.html
yss-project/src/app/pages/customer-service/project-list/project-list.scss
本实现参考了以下文件的数据查询方式:
yss-project/src/app/pages/team-leader/services/designer.service.tsyss-project/src/app/pages/team-leader/services/dashboard-data.service.tsyss-project/src/app/pages/admin/dashboard/dashboard.service.tsyss-project/src/app/pages/admin/project-management/project-management.tscompany: 公司指针(用于筛选)title: 项目标题status: 项目状态currentStage: 当前阶段contact: 联系人指针assignee: 负责人指针deadline: 截止日期isDeleted: 是否删除name: 联系人姓名name: 用户姓名检查localStorage中是否有company字段:
console.log('公司ID:', localStorage.getItem('company'));
http://localhost:4200/customer-service/project-list
打开浏览器开发者工具,查看控制台输出:
如果没有数据显示,请检查:
company字段是否正确关联到当前公司localStorage.getItem('company')是否有值1. 页面初始化
↓
2. initializeUserAndCompany()
├─ 从localStorage获取company ID
└─ 或从ProfileService获取company
↓
3. loadProjects()
├─ 创建Parse Query
├─ 设置查询条件(company, isDeleted)
├─ Include关联数据(contact, assignee)
└─ 执行查询
↓
4. 数据转换
├─ 将Parse Object转换为ProjectListItem
└─ 映射status和stage
↓
5. 项目分组
├─ 订单分配
├─ 确认需求
├─ 交付执行
└─ 售后
↓
6. UI渲染
├─ 看板视图(卡片)
├─ 列表视图
└─ 监控大盘
company字段正确关联WxworkAuthGuard完成时间: 2024-10-24
开发者: AI Assistant
状态: ✅ 已完成