|
@@ -1,4 +1,4 @@
|
|
-import { Component, OnInit, Input, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core';
|
|
|
|
|
|
+import { Component, OnInit, Input, ViewChild, ElementRef, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
@@ -17,6 +17,7 @@ import {
|
|
getDefaultProcesses
|
|
getDefaultProcesses
|
|
} from '../../../config/quotation-rules';
|
|
} from '../../../config/quotation-rules';
|
|
import { QuotationEditorComponent } from '../../../components/quotation-editor.component';
|
|
import { QuotationEditorComponent } from '../../../components/quotation-editor.component';
|
|
|
|
+import { TeamAssignComponent } from '../../../components/team-assign/team-assign.component';
|
|
|
|
|
|
const Parse = FmodeParse.with('nova');
|
|
const Parse = FmodeParse.with('nova');
|
|
|
|
|
|
@@ -34,7 +35,7 @@ const Parse = FmodeParse.with('nova');
|
|
@Component({
|
|
@Component({
|
|
selector: 'app-stage-order',
|
|
selector: 'app-stage-order',
|
|
standalone: true,
|
|
standalone: true,
|
|
- imports: [CommonModule, FormsModule, QuotationEditorComponent],
|
|
|
|
|
|
+ imports: [CommonModule, FormsModule, QuotationEditorComponent, TeamAssignComponent],
|
|
templateUrl: './stage-order.component.html',
|
|
templateUrl: './stage-order.component.html',
|
|
styleUrls: ['./stage-order.component.scss'],
|
|
styleUrls: ['./stage-order.component.scss'],
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
@@ -143,28 +144,9 @@ export class StageOrderComponent implements OnInit {
|
|
{ key: 'postProcess', name: '后期', color: 'success' }
|
|
{ key: 'postProcess', name: '后期', color: 'success' }
|
|
];
|
|
];
|
|
|
|
|
|
- // 项目组(Department)列表
|
|
|
|
- departments: FmodeObject[] = [];
|
|
|
|
- selectedDepartment: FmodeObject | null = null;
|
|
|
|
-
|
|
|
|
- // 项目组成员(Profile)列表
|
|
|
|
- departmentMembers: FmodeObject[] = [];
|
|
|
|
- selectedDesigner: FmodeObject | null = null;
|
|
|
|
-
|
|
|
|
- // 已分配的项目团队成员
|
|
|
|
- projectTeams: FmodeObject[] = [];
|
|
|
|
-
|
|
|
|
- // 设计师分配对话框
|
|
|
|
- showAssignDialog: boolean = false;
|
|
|
|
- assigningDesigner: FmodeObject | null = null;
|
|
|
|
- selectedSpaces: string[] = [];
|
|
|
|
- editingTeam: FmodeObject | null = null; // 当前正在编辑的团队对象
|
|
|
|
-
|
|
|
|
// 加载状态
|
|
// 加载状态
|
|
loading: boolean = true;
|
|
loading: boolean = true;
|
|
saving: boolean = false;
|
|
saving: boolean = false;
|
|
- loadingMembers: boolean = false;
|
|
|
|
- loadingTeams: boolean = false;
|
|
|
|
loadingSpaces: boolean = false;
|
|
loadingSpaces: boolean = false;
|
|
|
|
|
|
// 路由参数
|
|
// 路由参数
|
|
@@ -204,7 +186,8 @@ export class StageOrderComponent implements OnInit {
|
|
constructor(
|
|
constructor(
|
|
private route: ActivatedRoute,
|
|
private route: ActivatedRoute,
|
|
private projectFileService: ProjectFileService,
|
|
private projectFileService: ProjectFileService,
|
|
- private productSpaceService: ProductSpaceService
|
|
|
|
|
|
+ private productSpaceService: ProductSpaceService,
|
|
|
|
+ private cdr: ChangeDetectorRef
|
|
) {
|
|
) {
|
|
this.checkWxWorkSupport();
|
|
this.checkWxWorkSupport();
|
|
}
|
|
}
|
|
@@ -299,7 +282,6 @@ export class StageOrderComponent implements OnInit {
|
|
query.include('customer', 'assignee', 'department');
|
|
query.include('customer', 'assignee', 'department');
|
|
this.project = await query.get(this.projectId);
|
|
this.project = await query.get(this.projectId);
|
|
this.customer = this.project.get('customer');
|
|
this.customer = this.project.get('customer');
|
|
- this.selectedDesigner = this.project.get('assignee');
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (!this.currentUser && this.cid) {
|
|
if (!this.currentUser && this.cid) {
|
|
@@ -346,31 +328,9 @@ export class StageOrderComponent implements OnInit {
|
|
this.commercialScenes = data.commercialScenes;
|
|
this.commercialScenes = data.commercialScenes;
|
|
}
|
|
}
|
|
|
|
|
|
- // 加载已分配的项目组和设计师
|
|
|
|
- const department = this.project.get('department');
|
|
|
|
- if (department) {
|
|
|
|
- this.selectedDepartment = department;
|
|
|
|
- await this.loadDepartmentMembers(department.id);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const assignee = this.project.get('assignee');
|
|
|
|
- if (assignee) {
|
|
|
|
- this.selectedDesigner = assignee;
|
|
|
|
- }
|
|
|
|
|
|
+ // 团队分配由 TeamAssign 组件管理
|
|
}
|
|
}
|
|
|
|
|
|
- // 使用FmodeParse加载项目组列表(Department表)
|
|
|
|
- const deptQuery = new Parse.Query('Department');
|
|
|
|
- deptQuery.include("leader");
|
|
|
|
- deptQuery.equalTo('type', 'project');
|
|
|
|
- deptQuery.equalTo('company', localStorage.getItem('company'));
|
|
|
|
- deptQuery.notEqualTo('isDeleted', true);
|
|
|
|
- deptQuery.ascending('name');
|
|
|
|
- this.departments = await deptQuery.find();
|
|
|
|
-
|
|
|
|
- // 加载已分配的项目团队
|
|
|
|
- await this.loadProjectTeams();
|
|
|
|
-
|
|
|
|
// 加载项目文件
|
|
// 加载项目文件
|
|
await this.loadProjectFiles();
|
|
await this.loadProjectFiles();
|
|
|
|
|
|
@@ -378,6 +338,7 @@ export class StageOrderComponent implements OnInit {
|
|
console.error('加载失败:', err);
|
|
console.error('加载失败:', err);
|
|
} finally {
|
|
} finally {
|
|
this.loading = false;
|
|
this.loading = false;
|
|
|
|
+ this.cdr.markForCheck();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -410,6 +371,7 @@ export class StageOrderComponent implements OnInit {
|
|
console.error('加载项目空间失败:', error);
|
|
console.error('加载项目空间失败:', error);
|
|
} finally {
|
|
} finally {
|
|
this.loadingSpaces = false;
|
|
this.loadingSpaces = false;
|
|
|
|
+ this.cdr.markForCheck();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -682,20 +644,6 @@ export class StageOrderComponent implements OnInit {
|
|
this.quotation.total = total;
|
|
this.quotation.total = total;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * 空方法保持兼容性
|
|
|
|
- */
|
|
|
|
- onHomeSceneChange() {
|
|
|
|
- // 空实现,功能已移至quotation-editor组件
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- onCommercialTypeChange() {
|
|
|
|
- // 空实现,功能已移至quotation-editor组件
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- generateQuotation() {
|
|
|
|
- // 空实现,功能已移至quotation-editor组件
|
|
|
|
- }
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
* 获取空间进度
|
|
* 获取空间进度
|
|
@@ -746,234 +694,7 @@ export class StageOrderComponent implements OnInit {
|
|
/**
|
|
/**
|
|
* 选择项目组(Department)
|
|
* 选择项目组(Department)
|
|
*/
|
|
*/
|
|
- async selectDepartment(department: FmodeObject) {
|
|
|
|
- if (this.canEdit || this.project?.get('assignee')?.id) {
|
|
|
|
- this.selectedDepartment = department;
|
|
|
|
- this.selectedDesigner = null;
|
|
|
|
- this.departmentMembers = [];
|
|
|
|
-
|
|
|
|
- await this.loadDepartmentMembers(department);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 加载项目组成员
|
|
|
|
- */
|
|
|
|
- async loadDepartmentMembers(department: FmodeObject) {
|
|
|
|
- let departmentId = department.id
|
|
|
|
- if (!departmentId) return []
|
|
|
|
- try {
|
|
|
|
- this.loadingMembers = true;
|
|
|
|
-
|
|
|
|
- // 使用FmodeParse查询项目组的组员(Profile表,roleName为"组员")
|
|
|
|
- const query = new Parse.Query('Profile');
|
|
|
|
- query.equalTo('department', departmentId);
|
|
|
|
- query.equalTo('roleName', '组员');
|
|
|
|
- query.notEqualTo('isDeleted', true);
|
|
|
|
- query.ascending('name');
|
|
|
|
-
|
|
|
|
- this.departmentMembers = await query.find();
|
|
|
|
- this.departmentMembers.unshift(department?.get("leader"))
|
|
|
|
- return this.departmentMembers
|
|
|
|
- } catch (err) {
|
|
|
|
- console.error('加载项目组成员失败:', err);
|
|
|
|
- } finally {
|
|
|
|
- this.loadingMembers = false;
|
|
|
|
- }
|
|
|
|
- return []
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 加载已分配的ProjectTeam
|
|
|
|
- */
|
|
|
|
- async loadProjectTeams() {
|
|
|
|
- if (!this.project) return;
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- this.loadingTeams = true;
|
|
|
|
-
|
|
|
|
- const query = new Parse.Query('ProjectTeam');
|
|
|
|
- query.equalTo('project', this.project.toPointer());
|
|
|
|
- query.include('profile');
|
|
|
|
- query.notEqualTo('isDeleted', true);
|
|
|
|
-
|
|
|
|
- this.projectTeams = await query.find();
|
|
|
|
- } catch (err) {
|
|
|
|
- console.error('加载项目团队失败:', err);
|
|
|
|
- } finally {
|
|
|
|
- this.loadingTeams = false;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 选择设计师 - 弹出分配对话框
|
|
|
|
- */
|
|
|
|
- selectDesigner(designer: FmodeObject) {
|
|
|
|
- // 检查是否已分配
|
|
|
|
- const isAssigned = this.projectTeams.some(team => team.get('profile')?.id === designer.id);
|
|
|
|
- if (isAssigned) {
|
|
|
|
- alert('该设计师已分配到此项目');
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this.assigningDesigner = designer;
|
|
|
|
- this.selectedSpaces = [];
|
|
|
|
-
|
|
|
|
- // 如果只有一个空间,默认选中
|
|
|
|
- if (this.quotation.spaces.length === 1) {
|
|
|
|
- this.selectedSpaces = [this.quotation.spaces[0].name];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this.showAssignDialog = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 编辑已分配的设计师 - 重用分配对话框
|
|
|
|
- */
|
|
|
|
- editAssignedDesigner(team: FmodeObject) {
|
|
|
|
- if (!this.canEdit) return;
|
|
|
|
-
|
|
|
|
- const designer = team.get('profile');
|
|
|
|
- if (!designer) return;
|
|
|
|
-
|
|
|
|
- // 设置当前编辑的设计师和团队对象
|
|
|
|
- this.assigningDesigner = designer;
|
|
|
|
- this.editingTeam = team;
|
|
|
|
-
|
|
|
|
- // 预选当前已分配的空间
|
|
|
|
- const currentSpaces = team.get('data')?.spaces || [];
|
|
|
|
- this.selectedSpaces = [...currentSpaces];
|
|
|
|
-
|
|
|
|
- this.showAssignDialog = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 切换空间选择
|
|
|
|
- */
|
|
|
|
- toggleSpaceSelection(spaceName: string) {
|
|
|
|
- const index = this.selectedSpaces.indexOf(spaceName);
|
|
|
|
- if (index > -1) {
|
|
|
|
- this.selectedSpaces.splice(index, 1);
|
|
|
|
- } else {
|
|
|
|
- this.selectedSpaces.push(spaceName);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 确认分配设计师(支持创建和更新)
|
|
|
|
- */
|
|
|
|
- async confirmAssignDesigner() {
|
|
|
|
- if (!this.assigningDesigner || !this.project) return;
|
|
|
|
-
|
|
|
|
- if (this.selectedSpaces.length === 0) {
|
|
|
|
- alert('请至少选择一个空间场景');
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- this.saving = true;
|
|
|
|
-
|
|
|
|
- if (this.editingTeam) {
|
|
|
|
- // 更新现有团队成员的空间分配
|
|
|
|
- const data = this.editingTeam.get('data') || {};
|
|
|
|
- data.spaces = this.selectedSpaces;
|
|
|
|
- data.updatedAt = new Date();
|
|
|
|
- data.updatedBy = this.currentUser?.id;
|
|
|
|
- this.editingTeam.set('data', data);
|
|
|
|
-
|
|
|
|
- await this.editingTeam.save();
|
|
|
|
-
|
|
|
|
- alert('更新成功');
|
|
|
|
- } else {
|
|
|
|
- // 创建新的 ProjectTeam
|
|
|
|
- const ProjectTeam = Parse.Object.extend('ProjectTeam');
|
|
|
|
- const team = new ProjectTeam();
|
|
|
|
- team.set('project', this.project.toPointer());
|
|
|
|
- team.set('profile', this.assigningDesigner.toPointer());
|
|
|
|
- team.set('role', '组员');
|
|
|
|
- team.set('data', {
|
|
|
|
- spaces: this.selectedSpaces,
|
|
|
|
- assignedAt: new Date(),
|
|
|
|
- assignedBy: this.currentUser?.id
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- await team.save();
|
|
|
|
-
|
|
|
|
- // 加入群聊(静默执行)
|
|
|
|
- await this.addMemberToGroupChat(this.assigningDesigner.get('userId'));
|
|
|
|
-
|
|
|
|
- alert('分配成功');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 重新加载团队列表
|
|
|
|
- await this.loadProjectTeams();
|
|
|
|
-
|
|
|
|
- this.showAssignDialog = false;
|
|
|
|
- this.assigningDesigner = null;
|
|
|
|
- this.selectedSpaces = [];
|
|
|
|
- this.editingTeam = null;
|
|
|
|
-
|
|
|
|
- } catch (err) {
|
|
|
|
- console.error(this.editingTeam ? '更新失败:' : '分配设计师失败:', err);
|
|
|
|
- alert(this.editingTeam ? '更新失败' : '分配失败');
|
|
|
|
- } finally {
|
|
|
|
- this.saving = false;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 取消分配对话框
|
|
|
|
- */
|
|
|
|
- cancelAssignDialog() {
|
|
|
|
- this.showAssignDialog = false;
|
|
|
|
- this.assigningDesigner = null;
|
|
|
|
- this.selectedSpaces = [];
|
|
|
|
- this.editingTeam = null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 添加成员到群聊(静默执行)
|
|
|
|
- */
|
|
|
|
- async addMemberToGroupChat(userId: string) {
|
|
|
|
- if (!userId) return;
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- // 从父组件获取groupChat和chatId
|
|
|
|
- const groupChat = (this as any).groupChat;
|
|
|
|
- if (!groupChat) return;
|
|
|
|
-
|
|
|
|
- const chatId = groupChat.get('chat_id');
|
|
|
|
- if (!chatId) return;
|
|
|
|
-
|
|
|
|
- // 调用企微SDK添加成员(需要在前端通过ww对象调用)
|
|
|
|
- // 这部分需要在浏览器环境中通过企微JSSDK执行
|
|
|
|
- if (typeof (window as any).ww !== 'undefined') {
|
|
|
|
- await (window as any).ww.updateEnterpriseChat({
|
|
|
|
- chatId: chatId,
|
|
|
|
- userIdsToAdd: [userId]
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- } catch (err) {
|
|
|
|
- // 静默失败,不影响主流程
|
|
|
|
- console.warn('添加群成员失败:', err);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 获取团队成员负责的空间
|
|
|
|
- */
|
|
|
|
- getMemberSpaces(team: FmodeObject): string {
|
|
|
|
- const spaces = team.get('data')?.spaces || [];
|
|
|
|
- return spaces.join('、') || '未分配';
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 获取设计师工作量
|
|
|
|
- */
|
|
|
|
- getDesignerWorkload(designer: FmodeObject): string {
|
|
|
|
- // TODO: 查询该设计师当前进行中的项目数量
|
|
|
|
- return '3个项目';
|
|
|
|
- }
|
|
|
|
|
|
+ // 团队分配相关逻辑已迁移至 TeamAssignComponent
|
|
|
|
|
|
/**
|
|
/**
|
|
* 报价数据变化回调
|
|
* 报价数据变化回调
|
|
@@ -1029,13 +750,7 @@ export class StageOrderComponent implements OnInit {
|
|
data.commercialScenes = this.commercialScenes;
|
|
data.commercialScenes = this.commercialScenes;
|
|
this.project.set('data', data);
|
|
this.project.set('data', data);
|
|
|
|
|
|
- if (this.selectedDepartment) {
|
|
|
|
- this.project.set('department', this.selectedDepartment.toPointer());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (this.selectedDesigner) {
|
|
|
|
- this.project.set('assignee', this.selectedDesigner.toPointer());
|
|
|
|
- }
|
|
|
|
|
|
+ // 团队分配由 TeamAssign 组件负责,不在此保存 department/assignee
|
|
|
|
|
|
await this.project.save();
|
|
await this.project.save();
|
|
|
|
|
|
@@ -1054,47 +769,58 @@ export class StageOrderComponent implements OnInit {
|
|
async submitForOrder() {
|
|
async submitForOrder() {
|
|
if (!this.project || !this.canEdit) return;
|
|
if (!this.project || !this.canEdit) return;
|
|
|
|
|
|
- // 验证
|
|
|
|
|
|
+ // 基础验证
|
|
if (!this.projectInfo.title.trim()) {
|
|
if (!this.projectInfo.title.trim()) {
|
|
alert('请填写项目名称');
|
|
alert('请填写项目名称');
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
if (!this.projectInfo.projectType) {
|
|
if (!this.projectInfo.projectType) {
|
|
alert('请选择项目类型');
|
|
alert('请选择项目类型');
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
if (!this.projectInfo.deadline) {
|
|
if (!this.projectInfo.deadline) {
|
|
alert('请选择交付期限');
|
|
alert('请选择交付期限');
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
if (this.quotation.total === 0) {
|
|
if (this.quotation.total === 0) {
|
|
alert('请配置报价明细');
|
|
alert('请配置报价明细');
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!this.selectedDepartment) {
|
|
|
|
- alert('请选择项目组');
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!this.selectedDesigner) {
|
|
|
|
- alert('请选择设计师');
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
try {
|
|
try {
|
|
this.saving = true;
|
|
this.saving = true;
|
|
|
|
|
|
|
|
+ // 校验是否已在 TeamAssign 中分配至少一位组员
|
|
|
|
+ const query = new Parse.Query('ProjectTeam');
|
|
|
|
+ query.equalTo('project', this.project.toPointer());
|
|
|
|
+ query.include('profile');
|
|
|
|
+ query.notEqualTo('isDeleted', true);
|
|
|
|
+ const assignedTeams = await query.find();
|
|
|
|
+ if (assignedTeams.length === 0) {
|
|
|
|
+ alert('请在“设计师分配”中分配至少一位组员');
|
|
|
|
+ this.saving = false;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
await this.saveDraft();
|
|
await this.saveDraft();
|
|
|
|
|
|
|
|
+ // 推进阶段
|
|
this.project.set('currentStage', '确认需求');
|
|
this.project.set('currentStage', '确认需求');
|
|
|
|
|
|
|
|
+ // 记录审批历史(包含团队快照)
|
|
const data = this.project.get('data') || {};
|
|
const data = this.project.get('data') || {};
|
|
const approvalHistory = data.approvalHistory || [];
|
|
const approvalHistory = data.approvalHistory || [];
|
|
|
|
|
|
|
|
+ const teamSnapshot = assignedTeams.map(team => {
|
|
|
|
+ const profile = team.get('profile');
|
|
|
|
+ const spaces = team.get('data')?.spaces || [];
|
|
|
|
+ return {
|
|
|
|
+ id: profile?.id,
|
|
|
|
+ name: profile?.get('name'),
|
|
|
|
+ spaces
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+
|
|
approvalHistory.push({
|
|
approvalHistory.push({
|
|
stage: '订单分配',
|
|
stage: '订单分配',
|
|
submitter: {
|
|
submitter: {
|
|
@@ -1105,14 +831,7 @@ export class StageOrderComponent implements OnInit {
|
|
submitTime: new Date(),
|
|
submitTime: new Date(),
|
|
status: 'confirm',
|
|
status: 'confirm',
|
|
quotationTotal: this.quotation.total,
|
|
quotationTotal: this.quotation.total,
|
|
- department: {
|
|
|
|
- id: this.selectedDepartment.id,
|
|
|
|
- name: this.selectedDepartment.get('name')
|
|
|
|
- },
|
|
|
|
- assignee: {
|
|
|
|
- id: this.selectedDesigner.id,
|
|
|
|
- name: this.selectedDesigner.get('name')
|
|
|
|
- }
|
|
|
|
|
|
+ teams: teamSnapshot
|
|
});
|
|
});
|
|
|
|
|
|
data.approvalHistory = approvalHistory;
|
|
data.approvalHistory = approvalHistory;
|