project-list.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. import { Component, OnInit, OnDestroy, signal, computed, Inject } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { Router, RouterModule, ActivatedRoute } from '@angular/router';
  5. import { MatDialog, MatDialogModule } from '@angular/material/dialog';
  6. import { ProjectService } from '../../../services/project.service';
  7. import { ConsultationOrderDialogComponent } from '../consultation-order/consultation-order-dialog.component';
  8. import { Project, ProjectStatus, ProjectStage } from '../../../models/project.model';
  9. import { FmodeParse, FmodeObject } from 'fmode-ng/parse';
  10. import { ProfileService } from '../../../services/profile.service';
  11. import { normalizeStage, getProjectStatusByStage } from '../../../utils/project-stage-mapper';
  12. const Parse = FmodeParse.with('nova');
  13. // 定义项目列表项接口,包含计算后的属性
  14. interface ProjectListItem extends Project {
  15. progress: number;
  16. daysUntilDeadline: number;
  17. isUrgent: boolean;
  18. tagDisplayText: string;
  19. }
  20. @Component({
  21. selector: 'app-project-list',
  22. standalone: true,
  23. imports: [CommonModule, FormsModule, RouterModule, MatDialogModule],
  24. templateUrl: './project-list.html',
  25. styleUrls: ['./project-list.scss', '../customer-service-styles.scss']
  26. })
  27. export class ProjectList implements OnInit, OnDestroy {
  28. // 项目列表数据
  29. projects = signal<ProjectListItem[]>([]);
  30. // 原始项目数据(用于筛选)
  31. allProjects = signal<Project[]>([]);
  32. // 视图模式:卡片 / 列表 / 监控大盘(默认卡片)
  33. viewMode = signal<'card' | 'list' | 'dashboard'>('card');
  34. // 看板列配置 - 按照订单分配、确认需求、交付执行、售后四个阶段
  35. columns = [
  36. { id: 'order', name: '订单分配' },
  37. { id: 'requirements', name: '确认需求' },
  38. { id: 'delivery', name: '交付执行' },
  39. { id: 'aftercare', name: '售后' }
  40. ] as const;
  41. // 基础项目集合(服务端返回 + 本地生成),用于二次处理
  42. private baseProjects: Project[] = [];
  43. // 消息监听器
  44. private messageListener?: (event: MessageEvent) => void;
  45. // 添加toggleSidebar方法
  46. toggleSidebar(): void {
  47. // 侧边栏切换逻辑
  48. console.log('Toggle sidebar');
  49. }
  50. // 筛选和排序状态
  51. searchTerm = signal('');
  52. statusFilter = signal<string>('all');
  53. stageFilter = signal<string>('all');
  54. sortBy = signal<string>('deadline');
  55. // 当前页码
  56. currentPage = signal(1);
  57. // 每页显示数量
  58. pageSize = 8;
  59. // 分页后的项目列表(列表模式下可用)
  60. paginatedProjects = computed(() => {
  61. const filteredProjects = this.projects();
  62. const startIndex = (this.currentPage() - 1) * this.pageSize;
  63. return filteredProjects.slice(startIndex, startIndex + this.pageSize);
  64. });
  65. // 总页数
  66. totalPages = computed(() => {
  67. return Math.ceil(this.projects().length / this.pageSize);
  68. });
  69. // 筛选和排序选项
  70. statusOptions = [
  71. { value: 'all', label: '全部' },
  72. { value: 'order', label: '订单分配' },
  73. { value: 'requirements', label: '确认需求' },
  74. { value: 'delivery', label: '交付执行' },
  75. { value: 'aftercare', label: '售后' }
  76. ];
  77. stageOptions = [
  78. { value: 'all', label: '全部阶段' },
  79. { value: '需求沟通', label: '需求沟通' },
  80. { value: '建模', label: '建模' },
  81. { value: '软装', label: '软装' },
  82. { value: '渲染', label: '渲染' },
  83. { value: '尾款结算', label: '尾款结算' },
  84. { value: '投诉处理', label: '投诉处理' }
  85. ];
  86. sortOptions = [
  87. { value: 'deadline', label: '截止日期' },
  88. { value: 'createdAt', label: '创建时间' },
  89. { value: 'name', label: '项目名称' }
  90. ];
  91. // Parse相关
  92. company: FmodeObject | null = null;
  93. currentProfile: FmodeObject | null = null;
  94. isLoading = signal(false);
  95. loadError = signal<string | null>(null);
  96. constructor(
  97. private projectService: ProjectService,
  98. private router: Router,
  99. private route: ActivatedRoute,
  100. private dialog: MatDialog,
  101. private profileService: ProfileService
  102. ) {}
  103. async ngOnInit(): Promise<void> {
  104. // 读取上次的视图记忆
  105. const saved = localStorage.getItem('cs.viewMode');
  106. if (saved === 'card' || saved === 'list' || saved === 'dashboard') {
  107. this.viewMode.set(saved as 'card' | 'list' | 'dashboard');
  108. }
  109. // 初始化用户和公司信息
  110. await this.initializeUserAndCompany();
  111. // 加载真实项目数据
  112. await this.loadProjects();
  113. // 处理来自dashboard的查询参数
  114. this.route.queryParams.subscribe(params => {
  115. const filter = params['filter'];
  116. if (filter === 'all') {
  117. // 显示所有项目 - 重置筛选
  118. this.statusFilter.set('all');
  119. console.log('✅ 显示所有项目');
  120. } else if (filter === 'pending') {
  121. // 筛选待分配项目 - 使用'order'列ID
  122. this.statusFilter.set('order');
  123. console.log('✅ 筛选待分配项目(订单分配阶段)');
  124. }
  125. });
  126. // 添加消息监听器,处理来自iframe的导航请求
  127. this.messageListener = (event: MessageEvent) => {
  128. // 验证消息来源(可以根据需要添加更严格的验证)
  129. if (event.data && event.data.type === 'navigate' && event.data.route) {
  130. this.router.navigate([event.data.route]);
  131. }
  132. };
  133. window.addEventListener('message', this.messageListener);
  134. }
  135. ngOnDestroy(): void {
  136. // 清理消息监听器
  137. if (this.messageListener) {
  138. window.removeEventListener('message', this.messageListener);
  139. }
  140. }
  141. // 视图切换
  142. toggleView(mode: 'card' | 'list' | 'dashboard') {
  143. if (this.viewMode() !== mode) {
  144. this.viewMode.set(mode);
  145. localStorage.setItem('cs.viewMode', mode);
  146. }
  147. }
  148. // 初始化用户和公司信息
  149. private async initializeUserAndCompany(): Promise<void> {
  150. try {
  151. // 方法1: 从localStorage获取公司ID(参考team-leader的实现)
  152. const companyId = localStorage.getItem('company');
  153. if (companyId) {
  154. // 创建公司指针对象
  155. const CompanyClass = Parse.Object.extend('Company');
  156. this.company = new CompanyClass();
  157. this.company.id = companyId;
  158. console.log('✅ 从localStorage加载公司ID:', companyId);
  159. } else {
  160. // 方法2: 从Profile获取公司信息
  161. this.currentProfile = await this.profileService.getCurrentProfile();
  162. if (!this.currentProfile) {
  163. throw new Error('无法获取用户信息');
  164. }
  165. // 获取公司信息
  166. this.company = this.currentProfile.get('company');
  167. if (!this.company) {
  168. throw new Error('无法获取公司信息');
  169. }
  170. console.log('✅ 从Profile加载公司信息:', this.company.get('name'));
  171. }
  172. } catch (error) {
  173. console.error('❌ 初始化用户和公司信息失败:', error);
  174. this.loadError.set('加载用户信息失败,请刷新页面重试');
  175. }
  176. }
  177. // 获取公司指针
  178. private getCompanyPointer() {
  179. if (!this.company) {
  180. throw new Error('公司信息未初始化');
  181. }
  182. return {
  183. __type: 'Pointer',
  184. className: 'Company',
  185. objectId: this.company.id
  186. };
  187. }
  188. // 加载项目列表(从Parse Server)
  189. async loadProjects(): Promise<void> {
  190. if (!this.company) {
  191. console.warn('公司信息未加载,跳过项目加载');
  192. return;
  193. }
  194. this.isLoading.set(true);
  195. this.loadError.set(null);
  196. try {
  197. const ProjectQuery = new Parse.Query('Project');
  198. ProjectQuery.equalTo('company', this.getCompanyPointer());
  199. // 不强制要求isDeleted字段,兼容没有该字段的数据
  200. ProjectQuery.notEqualTo('isDeleted', true);
  201. ProjectQuery.include('contact', 'assignee', 'owner');
  202. ProjectQuery.descending('updatedAt');
  203. ProjectQuery.limit(500); // 获取最多500个项目
  204. const projectObjects = await ProjectQuery.find();
  205. console.log(`✅ 从Parse Server加载了 ${projectObjects.length} 个项目`);
  206. // 如果没有数据,打印调试信息
  207. if (projectObjects.length === 0) {
  208. console.warn('⚠️ 未找到项目数据,请检查:');
  209. console.warn('1. Parse Server中是否有Project数据');
  210. console.warn('2. 当前公司ID:', this.company.id);
  211. console.warn('3. 数据是否正确关联到当前公司');
  212. }
  213. // 转换为Project接口格式(并从Product表同步最新阶段)
  214. const projects: Project[] = await Promise.all(projectObjects.map(async (obj: FmodeObject) => {
  215. const contact = obj.get('contact');
  216. const assignee = obj.get('assignee');
  217. // 🔄 从Product表读取最新阶段(与组长端保持一致)
  218. let rawStage = obj.get('currentStage') || obj.get('stage') || '订单分配';
  219. try {
  220. const ProductQuery = new Parse.Query('Product');
  221. ProductQuery.equalTo('project', { __type: 'Pointer', className: 'Project', objectId: obj.id });
  222. ProductQuery.notEqualTo('isDeleted', true);
  223. ProductQuery.descending('updatedAt');
  224. ProductQuery.limit(1);
  225. const latestProduct = await ProductQuery.first();
  226. if (latestProduct) {
  227. const productStage = latestProduct.get('stage');
  228. if (productStage) {
  229. rawStage = productStage;
  230. console.log(`📦 项目 ${obj.get('title')} 从Product同步阶段: ${productStage}`);
  231. }
  232. }
  233. } catch (error) {
  234. console.warn(`⚠️ 查询项目 ${obj.id} 的Product失败:`, error);
  235. }
  236. // 🔄 规范化阶段名称(统一为四大核心阶段)
  237. const normalizedStage = normalizeStage(rawStage);
  238. // 🔄 根据阶段自动判断状态(与组长端、管理端保持一致)
  239. const projectStatus = obj.get('status');
  240. const autoStatus = getProjectStatusByStage(rawStage, projectStatus);
  241. console.log(`📊 客服项目 "${obj.get('title')}": 原始阶段=${rawStage}, 规范化阶段=${normalizedStage}, 原状态=${projectStatus}, 自动状态=${autoStatus}`);
  242. // 确保updatedAt是Date对象
  243. const updatedAt = obj.get('updatedAt');
  244. const createdAt = obj.get('createdAt');
  245. return {
  246. id: obj.id,
  247. name: obj.get('title') || '未命名项目',
  248. customerName: contact?.get('name') || '未知客户',
  249. customerId: contact?.id || '',
  250. status: autoStatus as ProjectStatus, // 使用根据阶段自动判断的状态
  251. currentStage: normalizedStage as ProjectStage,
  252. stage: normalizedStage as ProjectStage, // stage和currentStage保持一致
  253. assigneeId: assignee?.id || '',
  254. assigneeName: assignee?.get('name') || '未分配',
  255. deadline: obj.get('deadline') || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
  256. createdAt: createdAt instanceof Date ? createdAt : (createdAt ? new Date(createdAt) : new Date()),
  257. updatedAt: updatedAt instanceof Date ? updatedAt : (updatedAt ? new Date(updatedAt) : new Date()),
  258. description: obj.get('description') || '',
  259. priority: obj.get('priority') || 'medium',
  260. customerTags: [],
  261. highPriorityNeeds: [],
  262. skillsRequired: [],
  263. contact: contact
  264. };
  265. }));
  266. this.allProjects.set(projects);
  267. this.baseProjects = projects;
  268. this.processProjects(projects);
  269. console.log('项目数据处理完成');
  270. } catch (error) {
  271. console.error('加载项目列表失败:', error);
  272. this.loadError.set('加载项目列表失败,请刷新页面重试');
  273. this.projects.set([]);
  274. } finally {
  275. this.isLoading.set(false);
  276. }
  277. }
  278. // 映射Parse Server状态到前端状态
  279. private mapStatus(parseStatus: string): ProjectStatus {
  280. const statusMap: Record<string, ProjectStatus> = {
  281. '进行中': '进行中',
  282. '已完成': '已完成',
  283. '已暂停': '已暂停',
  284. '已延期': '已延期'
  285. };
  286. return statusMap[parseStatus] || '进行中';
  287. }
  288. // 映射Parse Server阶段到前端阶段
  289. private mapStage(parseStage: string): ProjectStage {
  290. // 直接返回Parse Server的阶段,不做转换
  291. // Parse Server的currentStage字段包含:订单分配、需求沟通、建模、软装、渲染、后期、尾款结算、投诉处理等
  292. if (!parseStage) {
  293. return '需求沟通'; // 默认阶段
  294. }
  295. return parseStage as ProjectStage;
  296. }
  297. // 处理项目数据,添加计算属性
  298. processProjects(projects: Project[]): void {
  299. const processedProjects = projects.map(project => {
  300. // 计算项目进度(模拟)
  301. const progress = this.calculateProjectProgress(project);
  302. // 计算距离截止日期的天数
  303. const daysUntilDeadline = this.calculateDaysUntilDeadline(project.deadline);
  304. // 判断是否紧急(截止日期前3天或已逾期)
  305. const isUrgent = daysUntilDeadline <= 3 && project.status === '进行中';
  306. // 生成标签显示文本
  307. const tagDisplayText = this.generateTagDisplayText(project);
  308. return {
  309. ...project,
  310. progress,
  311. daysUntilDeadline,
  312. isUrgent,
  313. tagDisplayText
  314. };
  315. });
  316. this.projects.set(this.applyFiltersAndSorting(processedProjects));
  317. }
  318. // 应用筛选和排序
  319. applyFiltersAndSorting(projects: ProjectListItem[]): ProjectListItem[] {
  320. let filteredProjects = [...projects];
  321. // 搜索筛选
  322. if (this.searchTerm().trim()) {
  323. const searchLower = this.searchTerm().toLowerCase().trim();
  324. filteredProjects = filteredProjects.filter(project =>
  325. project.name.toLowerCase().includes(searchLower) ||
  326. project.customerName.toLowerCase().includes(searchLower)
  327. );
  328. }
  329. // 状态筛选(按看板列映射)
  330. if (this.statusFilter() !== 'all') {
  331. const col = this.statusFilter() as 'order' | 'requirements' | 'delivery' | 'aftercare';
  332. filteredProjects = filteredProjects.filter(project =>
  333. this.getColumnIdForProject(project) === col
  334. );
  335. }
  336. // 阶段筛选
  337. if (this.stageFilter() !== 'all') {
  338. filteredProjects = filteredProjects.filter(project =>
  339. project.currentStage === this.stageFilter()
  340. );
  341. }
  342. // 排序
  343. filteredProjects.sort((a, b) => {
  344. switch (this.sortBy()) {
  345. case 'deadline':
  346. return new Date(a.deadline).getTime() - new Date(b.deadline).getTime();
  347. case 'createdAt':
  348. return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
  349. case 'name':
  350. return a.name.localeCompare(b.name);
  351. default:
  352. return 0;
  353. }
  354. });
  355. return filteredProjects;
  356. }
  357. // 生成标签显示文本
  358. generateTagDisplayText(project: Project): string {
  359. if (!project.customerTags || project.customerTags.length === 0) {
  360. return '普通项目';
  361. }
  362. const tag = project.customerTags[0];
  363. return `${tag.preference}${tag.needType}`;
  364. }
  365. // 计算项目进度(模拟)
  366. calculateProjectProgress(project: Project): number {
  367. if (project.status === '已完成') return 100;
  368. if (project.status === '已暂停' || project.status === '已延期') return 0;
  369. // 基于当前阶段计算进度(包含四大核心阶段和细分阶段)
  370. const stageProgress: Record<ProjectStage, number> = {
  371. // 四大核心阶段
  372. '订单分配': 0,
  373. '确认需求': 25,
  374. '交付执行': 60,
  375. '售后归档': 95,
  376. // 细分阶段(向后兼容)
  377. '需求沟通': 20,
  378. '方案确认': 30,
  379. '建模': 40,
  380. '软装': 50,
  381. '渲染': 70,
  382. '后期': 85,
  383. '尾款结算': 90,
  384. '客户评价': 100,
  385. '投诉处理': 100
  386. };
  387. return stageProgress[project.currentStage] || 0;
  388. }
  389. // 计算距离截止日期的天数
  390. calculateDaysUntilDeadline(deadline: Date): number {
  391. const now = new Date();
  392. const deadlineDate = new Date(deadline);
  393. const diffTime = deadlineDate.getTime() - now.getTime();
  394. return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  395. }
  396. // 列表/筛选交互(保留已有实现)
  397. onSearch(): void {
  398. // 搜索后重算
  399. this.processProjects(this.baseProjects);
  400. }
  401. onStatusChange(event: Event): void {
  402. const value = (event.target as HTMLSelectElement).value;
  403. this.statusFilter.set(value);
  404. this.processProjects(this.baseProjects);
  405. }
  406. onStageChange(event: Event): void {
  407. const value = (event.target as HTMLSelectElement).value;
  408. this.stageFilter.set(value);
  409. this.processProjects(this.baseProjects);
  410. }
  411. onSortChange(event: Event): void {
  412. const value = (event.target as HTMLSelectElement).value;
  413. this.sortBy.set(value);
  414. this.processProjects(this.baseProjects);
  415. }
  416. goToPage(page: number): void {
  417. if (page >= 1 && page <= this.totalPages()) {
  418. this.currentPage.set(page);
  419. }
  420. }
  421. prevPage(): void {
  422. if (this.currentPage() > 1) {
  423. this.currentPage.update(v => v - 1);
  424. }
  425. }
  426. nextPage(): void {
  427. if (this.currentPage() < this.totalPages()) {
  428. this.currentPage.update(v => v + 1);
  429. }
  430. }
  431. pageNumbers = computed(() => {
  432. const total = this.totalPages();
  433. const pages: number[] = [];
  434. const maxToShow = Math.min(total, 5);
  435. for (let i = 1; i <= maxToShow; i++) pages.push(i);
  436. return pages;
  437. });
  438. getAbsValue(value: number): number {
  439. return Math.abs(value);
  440. }
  441. formatDate(date: Date): string {
  442. const d = new Date(date);
  443. const y = d.getFullYear();
  444. const m = String(d.getMonth() + 1).padStart(2, '0');
  445. const day = String(d.getDate()).padStart(2, '0');
  446. return `${y}-${m}-${day}`;
  447. }
  448. getStatusClass(status: string): string {
  449. switch (status) {
  450. case '进行中': return 'status-in-progress';
  451. case '已完成': return 'status-completed';
  452. case '已暂停': return 'status-paused';
  453. case '已延期': return 'status-overdue';
  454. default: return '';
  455. }
  456. }
  457. getStageClass(stage: string): string {
  458. switch (stage) {
  459. case '需求沟通': return 'stage-communication';
  460. case '建模': return 'stage-modeling';
  461. case '软装': return 'stage-decoration';
  462. case '渲染': return 'stage-rendering';
  463. case '投诉处理': return 'stage-completed';
  464. case '订单分配': return 'stage-active';
  465. case '方案确认': return 'stage-active';
  466. case '尾款结算': return 'stage-completed';
  467. case '客户评价': return 'stage-completed';
  468. default: return '';
  469. }
  470. }
  471. // 看板分组逻辑 - 按照订单分配、确认需求、交付执行、售后四个阶段
  472. // 🔄 使用规范化后的四大核心阶段名称进行匹配
  473. private isOrderAssignment(p: Project): boolean {
  474. // 订单分配阶段:currentStage为"订单分配"
  475. const stage = p.currentStage as string;
  476. return stage === '订单分配';
  477. }
  478. private isRequirementsConfirmation(p: Project): boolean {
  479. // 确认需求阶段:currentStage为"确认需求"
  480. // 注意:阶段已经通过normalizeStage规范化为四大核心阶段
  481. const stage = p.currentStage as string;
  482. return stage === '确认需求';
  483. }
  484. private isDeliveryExecution(p: Project): boolean {
  485. // 交付执行阶段:currentStage为"交付执行"
  486. const stage = p.currentStage as string;
  487. return stage === '交付执行';
  488. }
  489. private isAftercare(p: Project): boolean {
  490. // 售后归档阶段:currentStage为"售后归档" 或 状态为"已完成"
  491. const stage = p.currentStage as string;
  492. return stage === '售后归档' || p.status === '已完成';
  493. }
  494. getProjectsByColumn(columnId: 'order' | 'requirements' | 'delivery' | 'aftercare'): ProjectListItem[] {
  495. const list = this.projects();
  496. switch (columnId) {
  497. case 'order':
  498. return list.filter(p => this.isOrderAssignment(p));
  499. case 'requirements':
  500. return list.filter(p => this.isRequirementsConfirmation(p));
  501. case 'delivery':
  502. return list.filter(p => this.isDeliveryExecution(p));
  503. case 'aftercare':
  504. return list.filter(p => this.isAftercare(p));
  505. }
  506. }
  507. // 新增:根据项目状态与阶段推断所在看板列
  508. getColumnIdForProject(project: ProjectListItem): 'order' | 'requirements' | 'delivery' | 'aftercare' {
  509. if (this.isOrderAssignment(project)) return 'order';
  510. if (this.isRequirementsConfirmation(project)) return 'requirements';
  511. if (this.isDeliveryExecution(project)) return 'delivery';
  512. if (this.isAftercare(project)) return 'aftercare';
  513. return 'requirements'; // 默认为确认需求阶段
  514. }
  515. // 详情跳转到wxwork项目详情页面(与组长、管理员保持一致)
  516. navigateToProject(project: ProjectListItem, columnId: 'order' | 'requirements' | 'delivery' | 'aftercare') {
  517. // 获取公司ID
  518. const cid = localStorage.getItem('company') || '';
  519. if (!cid) {
  520. console.error('未找到公司ID,无法跳转到项目详情页');
  521. return;
  522. }
  523. // 根据columnId映射到wxwork路由的阶段路径
  524. // wxwork路由支持的阶段:order, requirements, delivery, aftercare, issues
  525. const stagePathMapping = {
  526. 'order': 'order', // 订单分配
  527. 'requirements': 'requirements', // 确认需求
  528. 'delivery': 'delivery', // 交付执行
  529. 'aftercare': 'aftercare' // 售后归档
  530. };
  531. const stagePath = stagePathMapping[columnId];
  532. // 跳转到wxwork路由的项目详情页(纯净页面,无管理端侧边栏)
  533. // 路由格式:/wxwork/:cid/project/:projectId/:stage
  534. this.router.navigate(['/wxwork', cid, 'project', project.id, stagePath]);
  535. }
  536. // 新增:直接进入沟通管理(消息)标签
  537. navigateToMessages(project: ProjectListItem) {
  538. this.router.navigate(['/customer-service/messages'], { queryParams: { projectId: project.id } });
  539. }
  540. // 导航到创建订单页面
  541. navigateToCreateOrder() {
  542. // 打开咨询订单弹窗
  543. const dialogRef = this.dialog.open(ConsultationOrderDialogComponent, {
  544. width: '900px',
  545. maxWidth: '95vw',
  546. maxHeight: '90vh',
  547. panelClass: 'consultation-order-dialog'
  548. });
  549. // 监听订单分配成功事件
  550. dialogRef.componentInstance.orderCreated.subscribe((orderData: any) => {
  551. // 关闭弹窗
  552. dialogRef.close();
  553. // 准备同步数据
  554. const syncData = {
  555. customerInfo: orderData.customerInfo,
  556. requirementInfo: orderData.requirementInfo,
  557. preferenceTags: orderData.preferenceTags,
  558. assignedDesigner: orderData.assignedDesigner
  559. };
  560. // 跳转到新创建的项目详情页面,传递同步数据
  561. this.router.navigate([
  562. '/designer/project-detail',
  563. orderData.orderId
  564. ], {
  565. queryParams: {
  566. role: 'customer-service',
  567. activeTab: 'overview',
  568. syncData: JSON.stringify(syncData)
  569. }
  570. });
  571. });
  572. }
  573. }