Browse Source

fix: parse with admin

ryanemax 22 hours ago
parent
commit
97bfe90d8b

+ 1 - 1
CLAUDE.md

@@ -4,7 +4,7 @@
     - schema/*.md 数据范式
     - agents.md 智能体开发
     - frontend.md 前端开发
-    - parse.md 数据服务
+    - parse.md 数据服务 FmodeParse
 
 # 项目文档管理
 - docs/ 文档目录

+ 7 - 1
docs/task/2025101701-admin.md

@@ -28,4 +28,10 @@
 
 ## 注意事项
 - 考虑到群组、员工,是严格从企业微信同步过来,有userid和chat_id对应关系
-    - 因此这两个数据不提供新增、删除功能,只提供编辑,禁用(isDisabled字段)
+    - 因此这两个数据不提供新增、删除功能,只提供编辑,禁用(isDisabled字段)
+
+
+
+# FAQ:迭代需求
+
+> 1.项目组管理,需要用FmodeParse实现増删查改功能,可以创建项目组并选择组长.leader<Profile>.2.项目管理,点击详情,应该引导的是./src/modules/project/pages/project-detail,并构建合理的app.routes.ts路由来跳转.编辑弹出简单窗口只允许修改名字和分配组员,分配组员请使用src/app/pages/designer/project-detail/components/designer-assignment/designer-assignment.component.ts组件(同时project-detail中对应的stage-order阶段的设计师分配也用该组件)(注意设计师分配组件需要根据数据范式和FmodeParse进行数据对接开发,同时需要优化移动端布局),隐藏删除. 3.客户管理,需要点击弹出客户面板组件(复用src/modules/project/pages/contact/contact.component.ts,后台是打开已有ContactInfo信息,可以直接通过wxwork.corp来获取客户详情,并且提供编辑功能,方便管理客户的更多信息).4.群组管理,详情查看,需要根据wxwork.corp获取群组信息详情和成员列表并展示出来,加载入群二维码等信息(参考projects/nova-crm/src/modules/lesson/page-lesson-group/page-lesson-group.component.ts)请您认真思考完成上述所有修改需求 

+ 16 - 34
src/app/pages/admin/admin-layout/admin-layout.html

@@ -45,14 +45,23 @@
             </svg>
             <span>项目管理</span>
           </a>
-          <a routerLink="/admin/designers" class="nav-item" routerLinkActive="active">
+          <a routerLink="/admin/departments" class="nav-item" routerLinkActive="active">
+            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+              <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+              <circle cx="9" cy="7" r="4"></circle>
+              <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
+              <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
+            </svg>
+            <span>项目组管理</span>
+          </a>
+          <a routerLink="/admin/employees" class="nav-item" routerLinkActive="active">
             <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
               <circle cx="9" cy="7" r="4"></circle>
               <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
               <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
             </svg>
-            <span>设计师管理</span>
+            <span>员工管理</span>
           </a>
           <a routerLink="/admin/customers" class="nav-item" routerLinkActive="active">
             <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
@@ -61,42 +70,15 @@
             </svg>
             <span>客户管理</span>
           </a>
-          <a routerLink="/admin/finance" class="nav-item" routerLinkActive="active">
-            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <line x1="12" y1="1" x2="12" y2="23"></line>
-              <path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
-            </svg>
-            <span>财务管理</span>
-          </a>
-        </div>
-        
-        <div class="nav-section">
-          <h3 class="section-title">系统设置</h3>
-          <a routerLink="/admin/system-settings" class="nav-item" routerLinkActive="active">
-            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <circle cx="12" cy="12" r="3"></circle>
-              <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
-            </svg>
-            <span>系统设置</span>
-          </a>
-          <a routerLink="/admin/logs" class="nav-item" routerLinkActive="active">
+          <a routerLink="/admin/groupchats" class="nav-item" routerLinkActive="active">
             <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
-              <polyline points="14 2 14 8 20 8"></polyline>
-              <line x1="16" y1="13" x2="8" y2="13"></line>
-              <line x1="16" y1="17" x2="8" y2="17"></line>
-              <polyline points="10 9 9 9 8 9"></polyline>
+              <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
             </svg>
-            <span>系统日志</span>
-          </a>
-          <a routerLink="/admin/api-integrations" class="nav-item" routerLinkActive="active">
-            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
-              <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
-            </svg>
-            <span>API集成管理</span>
+            <span>群组管理</span>
           </a>
         </div>
+
+        <!-- 财务管理和系统设置已隐藏 -->
       </nav>
     </aside>
 

+ 17 - 94
src/app/pages/admin/customers/customers.html

@@ -2,10 +2,9 @@
   <div class="page-header">
     <div class="header-left">
       <h2 class="page-title">客户管理</h2>
-      <p class="page-description">管理客户档案、分级与成交数据</p>
+      <p class="page-description">管理客户档案</p>
     </div>
     <div class="header-actions">
-      <button class="btn primary" (click)="addCustomer()">+ 新建客户</button>
       <button class="btn" (click)="exportCustomers()">导出</button>
     </div>
   </div>
@@ -15,36 +14,13 @@
       <div class="stat-label">客户总数</div>
       <div class="stat-value">{{ total() }}</div>
     </div>
-    <div class="stat-card">
-      <div class="stat-label">活跃客户</div>
-      <div class="stat-value">{{ active() }}</div>
-    </div>
-    <div class="stat-card">
-      <div class="stat-label">VIP客户</div>
-      <div class="stat-value">{{ vip() }}</div>
-    </div>
-    <div class="stat-card">
-      <div class="stat-label">累计成交额</div>
-      <div class="stat-value">{{ formatAmount(amount()) }}</div>
-    </div>
   </div>
 
   <div class="toolbar">
     <div class="search">
-      <input placeholder="搜索客户名称/联系人/电话" (input)="keyword.set($any($event.target).value)" [value]="keyword()"/>
+      <input placeholder="搜索客户名称/手机号" (input)="keyword.set($any($event.target).value)" [value]="keyword()"/>
     </div>
     <div class="filters">
-      <select (change)="status.set($any($event.target).value)">
-        <option value="all">全部状态</option>
-        <option value="active">活跃</option>
-        <option value="inactive">沉默</option>
-      </select>
-      <select (change)="level.set($any($event.target).value)">
-        <option value="all">全部等级</option>
-        <option value="normal">普通</option>
-        <option value="vip">VIP</option>
-        <option value="svip">SVIP</option>
-      </select>
       <button class="btn" (click)="resetFilters()">重置</button>
     </div>
   </div>
@@ -52,79 +28,26 @@
   <div class="table-card">
     <div class="table header">
       <div>客户名称</div>
-      <div>联系人</div>
-      <div>电话</div>
-      <div>等级</div>
-      <div>状态</div>
-      <div>项目数</div>
-      <div>累计成交</div>
-      <div>加入日期</div>
-      <div>操作</div>
+      <div>手机号</div>
+      <div>企微ID</div>
+      <div>来源</div>
+      <div>创建时间</div>
     </div>
     <div class="table row" *ngFor="let c of filtered">
       <div class="name">
         <div class="title">{{ c.name }}</div>
       </div>
-      <div>{{ c.contact }}</div>
-      <div>{{ c.phone }}</div>
-      <div>
-        <span class="tag" [class.vip]="c.level==='vip'" [class.svip]="c.level==='svip'">{{ c.level.toUpperCase() }}</span>
-      </div>
-      <div>
-        <span class="dot" [class.green]="c.status==='active'" [class.gray]="c.status==='inactive'"></span>
-        {{ c.status==='active' ? '活跃' : '沉默' }}
-      </div>
-      <div>{{ c.projects }}</div>
-      <div>{{ formatAmount(c.totalAmount) }}</div>
-      <div>{{ c.joinDate }}</div>
-      <div class="actions">
-        <button class="icon" (click)="viewCustomer(c)">详</button>
-        <button class="icon" (click)="editCustomer(c)">编</button>
-        <button class="icon danger">删</button>
-      </div>
-    </div>
-    <div class="empty" *ngIf="filtered.length === 0">暂无数据</div>
-  </div>
-
-  <!-- 侧边面板 -->
-  <div class="panel-overlay" *ngIf="showPanel" (click)="closePanel()">
-    <div class="side-panel" (click)="$event.stopPropagation()">
-      <div class="panel-header">
-        <h3>{{ panelMode==='add' ? '新建客户' : panelMode==='detail' ? '客户详情' : '编辑客户' }}</h3>
-        <button class="close-btn" (click)="closePanel()">×</button>
-      </div>
-
-      <!-- 详情 -->
-      <div class="panel-content" *ngIf="panelMode==='detail' && currentCustomer">
-        <div class="detail-section"><label>客户名称</label><span>{{ currentCustomer.name }}</span></div>
-        <div class="detail-section"><label>联系人</label><span>{{ currentCustomer.contact }}</span></div>
-        <div class="detail-section"><label>电话</label><span>{{ currentCustomer.phone }}</span></div>
-        <div class="detail-section"><label>等级</label><span class="tag" [class.vip]="currentCustomer.level==='vip'" [class.svip]="currentCustomer.level==='svip'">{{ currentCustomer.level.toUpperCase() }}</span></div>
-        <div class="detail-section"><label>状态</label><span>{{ currentCustomer.status==='active' ? '活跃' : '沉默' }}</span></div>
-        <div class="detail-section"><label>项目数</label><span>{{ currentCustomer.projects }}</span></div>
-        <div class="detail-section"><label>累计成交</label><span>{{ formatAmount(currentCustomer.totalAmount) }}</span></div>
-        <div class="detail-section"><label>加入日期</label><span>{{ currentCustomer.joinDate }}</span></div>
-      </div>
-
-      <!-- 新增/编辑表单 -->
-      <div class="panel-content" *ngIf="panelMode==='add' || panelMode==='edit'">
-        <form class="customer-form">
-          <div class="form-group"><label>客户名称 *</label><input type="text" [(ngModel)]="formModel.name" name="name" placeholder="请输入客户名称" required></div>
-          <div class="form-group"><label>联系人</label><input type="text" [(ngModel)]="formModel.contact" name="contact" placeholder="请输入联系人"></div>
-          <div class="form-group"><label>电话</label><input type="text" [(ngModel)]="formModel.phone" name="phone" placeholder="请输入电话"></div>
-          <div class="form-group"><label>等级</label><select [(ngModel)]="formModel.level" name="level"><option value="normal">普通</option><option value="vip">VIP</option><option value="svip">SVIP</option></select></div>
-          <div class="form-group"><label>状态</label><select [(ngModel)]="formModel.status" name="status"><option value="active">活跃</option><option value="inactive">沉默</option></select></div>
-          <div class="form-group"><label>项目数</label><input type="number" [(ngModel)]="formModel.projects" name="projects" min="0" placeholder="0"></div>
-          <div class="form-group"><label>累计成交(¥)</label><input type="number" [(ngModel)]="formModel.totalAmount" name="totalAmount" min="0" step="100" placeholder="0"></div>
-          <div class="form-group"><label>加入日期</label><input type="date" [(ngModel)]="formModel.joinDate" name="joinDate"></div>
-        </form>
-      </div>
-
-      <div class="panel-footer">
-        <button class="btn" (click)="closePanel()">取消</button>
-        <button class="btn primary" *ngIf="panelMode==='add'" (click)="saveCustomer()">保存</button>
-        <button class="btn primary" *ngIf="panelMode==='edit'" (click)="updateCustomer()">更新</button>
-      </div>
+      <div>{{ c.mobile }}</div>
+      <div>{{ c.external_userid || '-' }}</div>
+      <div>{{ c.source || '-' }}</div>
+      <div>{{ c.createdAt ? (c.createdAt | date:'yyyy-MM-dd') : '-' }}</div>
+    </div>
+    <div class="empty" *ngIf="filtered.length === 0">
+      @if (loading()) {
+        <span>加载中...</span>
+      } @else {
+        <span>暂无数据</span>
+      }
     </div>
   </div>
 </div>

+ 7 - 101
src/app/pages/admin/customers/customers.ts

@@ -71,55 +71,25 @@ export class Customers implements OnInit {
 
   get filtered() {
     const kw = this.keyword().trim().toLowerCase();
-    const st = this.status();
-    const lv = this.level();
     return this.customers().filter(c => {
-      const m1 = !kw || c.name.toLowerCase().includes(kw) || c.contact.toLowerCase().includes(kw) || c.phone.includes(kw);
-      const m2 = st === 'all' || c.status === st;
-      const m3 = lv === 'all' || c.level === lv;
-      return m1 && m2 && m3;
+      const m1 = !kw || c.name.toLowerCase().includes(kw) || c.mobile.includes(kw);
+      return m1;
     });
   }
 
   resetFilters() {
     this.keyword.set('');
-    this.status.set('all');
-    this.level.set('all');
-  }
-
-  formatAmount(n: number) {
-    return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', maximumFractionDigits: 0 }).format(n);
-  }
-
-  // 新建客户 -> 打开侧边面板填写
-  addCustomer() {
-    this.formModel = {
-      name: '',
-      contact: '',
-      phone: '',
-      level: 'normal',
-      status: 'active',
-      projects: 0,
-      totalAmount: 0,
-      joinDate: new Date().toISOString().slice(0, 10)
-    } as Partial<Customer>;
-    this.currentCustomer = null;
-    this.panelMode = 'add';
-    this.showPanel = true;
   }
 
   // 导出当前筛选客户为 CSV
   exportCustomers() {
-    const header = ['客户名称','联系人','电话','等级','状态','项目数','累计成交','加入日期'];
+    const header = ['客户名称','手机号','企微ID','来源','创建时间'];
     const rows = this.filtered.map(c => [
       c.name,
-      c.contact,
-      c.phone,
-      c.level.toUpperCase(),
-      c.status === 'active' ? '活跃' : '沉默',
-      String(c.projects),
-      String(c.totalAmount),
-      c.joinDate
+      c.mobile,
+      c.external_userid || '',
+      c.source || '',
+      c.createdAt instanceof Date ? c.createdAt.toISOString().slice(0, 10) : String(c.createdAt || '')
     ]);
     this.downloadCSV('客户列表.csv', [header, ...rows]);
   }
@@ -140,68 +110,4 @@ export class Customers implements OnInit {
     URL.revokeObjectURL(url);
   }
 
-  // 查看详情
-  viewCustomer(c: Customer) {
-    this.currentCustomer = c;
-    this.panelMode = 'detail';
-    this.showPanel = true;
-  }
-
-  // 编辑
-  editCustomer(c: Customer) {
-    this.currentCustomer = c;
-    this.formModel = { ...c };
-    this.panelMode = 'edit';
-    this.showPanel = true;
-  }
-
-  // 关闭
-  closePanel() {
-    this.showPanel = false;
-    this.panelMode = 'add';
-    this.currentCustomer = null;
-    this.formModel = {};
-  }
-
-  // 保存
-  saveCustomer() {
-    const name = (this.formModel.name || '').trim();
-    if (!name) { alert('请输入客户名称'); return; }
-    const item: Customer = {
-      id: Date.now().toString(),
-      name,
-      contact: this.formModel.contact || '',
-      phone: this.formModel.phone || '',
-      level: (this.formModel.level as Customer['level']) || 'normal',
-      status: (this.formModel.status as Customer['status']) || 'active',
-      projects: Number(this.formModel.projects || 0),
-      totalAmount: Number(this.formModel.totalAmount || 0),
-      joinDate: this.formModel.joinDate || new Date().toISOString().slice(0,10)
-    };
-    this.customers.set([item, ...this.customers()]);
-    this.total.set(this.total() + 1);
-    if (item.status === 'active') this.active.set(this.active() + 1);
-    if (item.level === 'vip') this.vip.set(this.vip() + 1);
-    this.amount.set(this.amount() + item.totalAmount);
-    this.closePanel();
-  }
-
-  // 更新
-  updateCustomer() {
-    if (!this.currentCustomer) return;
-    const updated: Customer = {
-      ...this.currentCustomer,
-      name: this.formModel.name ?? this.currentCustomer.name,
-      contact: this.formModel.contact ?? this.currentCustomer.contact,
-      phone: this.formModel.phone ?? this.currentCustomer.phone,
-      level: (this.formModel.level as Customer['level']) ?? this.currentCustomer.level,
-      status: (this.formModel.status as Customer['status']) ?? this.currentCustomer.status,
-      projects: Number(this.formModel.projects ?? this.currentCustomer.projects),
-      totalAmount: Number(this.formModel.totalAmount ?? this.currentCustomer.totalAmount),
-      joinDate: this.formModel.joinDate ?? this.currentCustomer.joinDate
-    };
-    // 简化:不做复杂统计差异,直接替换
-    this.customers.set(this.customers().map(c => c.id === updated.id ? updated : c));
-    this.closePanel();
-  }
 }

+ 2 - 2
src/app/pages/admin/employees/employees.ts

@@ -55,7 +55,7 @@ export class Employees implements OnInit {
   };
 
   // 角色列表
-  roles = ['客服', '组员', '组长', '人事', '财务'];
+  roles = ['客服', '组员', '组长', '人事', '财务','管理员'];
 
   constructor(
     private employeeService: EmployeeService,
@@ -79,7 +79,7 @@ export class Employees implements OnInit {
           name: json.name || '未知',
           mobile: json.mobile || '',
           userId: json.userId || '',
-          roleName: json.roleName || '组员',
+          roleName: json.roleName || '未分配',
           department: json.departmentName || '未分配',
           departmentId: json.departmentId,
           isDisabled: json.isDisabled || false,

+ 25 - 74
src/app/pages/admin/project-management/project-management.html

@@ -61,7 +61,7 @@
   <!-- 项目统计卡片 -->
   <div class="stats-cards">
     <div class="stat-card">
-      <div class="stat-value">{{ projects().length }}</div>
+      <div class="stat-value">{{ totalProjectsCount }}</div>
       <div class="stat-label">总项目数</div>
     </div>
     <div class="stat-card">
@@ -74,11 +74,7 @@
     </div>
     <div class="stat-card">
       <div class="stat-value">{{ pendingProjectsCount }}</div>
-      <div class="stat-label">待开始</div>
-    </div>
-    <div class="stat-card">
-      <div class="stat-value">{{ formatCurrency(totalProjectsBudget) }}</div>
-      <div class="stat-label">总预算</div>
+      <div class="stat-label">待分配</div>
     </div>
   </div>
 
@@ -86,13 +82,13 @@
   <div class="project-table-container">
     <table mat-table [dataSource]="paginatedProjects" class="project-table">
       <!-- 项目名称列 -->
-      <ng-container matColumnDef="name">
+      <ng-container matColumnDef="title">
         <th mat-header-cell *matHeaderCellDef class="table-header"
-            (click)="onSort('name')">
+            (click)="onSort('title')">
           <div class="header-content">
             <span>项目名称</span>
             <div class="sort-icon">
-              <svg *ngIf="sortColumn === 'name'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <svg *ngIf="sortColumn === 'title'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                 <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
               </svg>
             </div>
@@ -100,7 +96,7 @@
         </th>
         <td mat-cell *matCellDef="let project" class="table-cell">
           <div class="project-name">
-            <a [routerLink]="['/admin/project-detail', project.id]">{{ project.name }}</a>
+            <a [routerLink]="['/admin/project-detail', project.id]">{{ project.title }}</a>
           </div>
         </td>
       </ng-container>
@@ -123,21 +119,21 @@
         </td>
       </ng-container>
 
-      <!-- 设计师列 -->
-      <ng-container matColumnDef="designer">
+      <!-- 负责人列 -->
+      <ng-container matColumnDef="assignee">
         <th mat-header-cell *matHeaderCellDef class="table-header"
-            (click)="onSort('designer')">
+            (click)="onSort('assignee')">
           <div class="header-content">
-            <span>设计师</span>
+            <span>负责人</span>
             <div class="sort-icon">
-              <svg *ngIf="sortColumn === 'designer'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <svg *ngIf="sortColumn === 'assignee'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                 <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
               </svg>
             </div>
           </div>
         </th>
         <td mat-cell *matCellDef="let project" class="table-cell">
-          {{ project.designer }}
+          {{ project.assignee }}
         </td>
       </ng-container>
 
@@ -161,78 +157,33 @@
         </td>
       </ng-container>
 
-      <!-- 进度列 -->
-      <ng-container matColumnDef="progress">
-        <th mat-header-cell *matHeaderCellDef class="table-header"
-            (click)="onSort('progress')">
-          <div class="header-content">
-            <span>进度</span>
-            <div class="sort-icon">
-              <svg *ngIf="sortColumn === 'progress'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
-              </svg>
-            </div>
-          </div>
-        </th>
-        <td mat-cell *matCellDef="let project" class="table-cell">
-          <div class="progress-container">
-            <div class="progress-bar" [style.width]="project.progress + '%'" [style.backgroundColor]="statusColors[project.status]"></div>
-            <span class="progress-text">{{ project.progress }}%</span>
-          </div>
-        </td>
-      </ng-container>
-
-      <!-- 预算列 -->
-      <ng-container matColumnDef="budget">
-        <th mat-header-cell *matHeaderCellDef class="table-header"
-            (click)="onSort('budget')">
-          <div class="header-content">
-            <span>预算</span>
-            <div class="sort-icon">
-              <svg *ngIf="sortColumn === 'budget'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
-              </svg>
-            </div>
-          </div>
-        </th>
-        <td mat-cell *matCellDef="let project" class="table-cell">
-          {{ formatCurrency(project.budget) }}
-        </td>
-      </ng-container>
-
-      <!-- 开始日期列 -->
-      <ng-container matColumnDef="startDate">
-        <th mat-header-cell *matHeaderCellDef class="table-header"
-            (click)="onSort('startDate')">
+      <!-- 当前阶段列 -->
+      <ng-container matColumnDef="currentStage">
+        <th mat-header-cell *matHeaderCellDef class="table-header">
           <div class="header-content">
-            <span>开始日期</span>
-            <div class="sort-icon">
-              <svg *ngIf="sortColumn === 'startDate'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
-              </svg>
-            </div>
+            <span>当前阶段</span>
           </div>
         </th>
         <td mat-cell *matCellDef="let project" class="table-cell">
-          {{ project.startDate }}
+          {{ project.currentStage || '-' }}
         </td>
       </ng-container>
 
-      <!-- 结束日期列 -->
-      <ng-container matColumnDef="endDate">
+      <!-- 更新时间列 -->
+      <ng-container matColumnDef="updatedAt">
         <th mat-header-cell *matHeaderCellDef class="table-header"
-            (click)="onSort('endDate')">
+            (click)="onSort('updatedAt')">
           <div class="header-content">
-            <span>结束日期</span>
+            <span>更新时间</span>
             <div class="sort-icon">
-              <svg *ngIf="sortColumn === 'endDate'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <svg *ngIf="sortColumn === 'updatedAt'" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                 <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
               </svg>
             </div>
           </div>
         </th>
         <td mat-cell *matCellDef="let project" class="table-cell">
-          {{ project.endDate }}
+          {{ project.updatedAt ? (project.updatedAt | date:'yyyy-MM-dd HH:mm') : '-' }}
         </td>
       </ng-container>
 
@@ -269,8 +220,8 @@
         </td>
       </ng-container>
 
-      <tr mat-header-row *matHeaderRowDef="['name', 'customer', 'designer', 'status', 'progress', 'budget', 'startDate', 'endDate', 'actions']"></tr>
-      <tr mat-row *matRowDef="let row; columns: ['name', 'customer', 'designer', 'status', 'progress', 'budget', 'startDate', 'endDate', 'actions']"></tr>
+      <tr mat-header-row *matHeaderRowDef="['title', 'customer', 'assignee', 'status', 'currentStage', 'updatedAt', 'actions']"></tr>
+      <tr mat-row *matRowDef="let row; columns: ['title', 'customer', 'assignee', 'status', 'currentStage', 'updatedAt', 'actions']"></tr>
     </table>
 
     <!-- 无数据状态 -->

+ 16 - 137
src/app/pages/admin/project-management/project-management.ts

@@ -123,135 +123,16 @@ export class ProjectManagement implements OnInit {
     }
   }
 
-  // 旧的模拟数据方法已移除,如需测试可临时使用
-  loadProjectsMock(): void {
-    // 模拟项目数据
-    this.projects.set([
-      {
-        id: '1',
-        name: '现代简约风格三居室设计',
-        customer: '王先生',
-        status: 'in-progress',
-        designer: '张设计师',
-        startDate: '2025-09-01',
-        endDate: '2025-10-15',
-        budget: 85000,
-        progress: 65
-      },
-      {
-        id: '2',
-        name: '北欧风格两居室设计',
-        customer: '李女士',
-        status: 'completed',
-        designer: '刘设计师',
-        startDate: '2025-08-15',
-        endDate: '2025-09-30',
-        budget: 68000,
-        progress: 100
-      },
-      {
-        id: '3',
-        name: '工业风办公室设计',
-        customer: '赵先生',
-        status: 'pending',
-        designer: '王设计师',
-        startDate: '2025-09-15',
-        endDate: '2025-11-05',
-        budget: 120000,
-        progress: 0
-      },
-      {
-        id: '4',
-        name: '新中式别墅设计',
-        customer: '钱女士',
-        status: 'on-hold',
-        designer: '张设计师',
-        startDate: '2025-08-20',
-        endDate: '2025-11-20',
-        budget: 250000,
-        progress: 40
-      },
-      {
-        id: '5',
-        name: '日式小户型设计',
-        customer: '孙先生',
-        status: 'in-progress',
-        designer: '刘设计师',
-        startDate: '2025-09-05',
-        endDate: '2025-10-20',
-        budget: 55000,
-        progress: 35
-      },
-      {
-        id: '6',
-        name: '美式乡村风格设计',
-        customer: '周女士',
-        status: 'in-progress',
-        designer: '王设计师',
-        startDate: '2025-09-08',
-        endDate: '2025-10-30',
-        budget: 75000,
-        progress: 25
-      },
-      {
-        id: '7',
-        name: '极简主义公寓设计',
-        customer: '吴先生',
-        status: 'pending',
-        designer: '张设计师',
-        startDate: '2025-09-20',
-        endDate: '2025-11-10',
-        budget: 60000,
-        progress: 0
-      },
-      {
-        id: '8',
-        name: '地中海风格别墅设计',
-        customer: '郑女士',
-        status: 'completed',
-        designer: '刘设计师',
-        startDate: '2025-08-01',
-        endDate: '2025-09-15',
-        budget: 180000,
-        progress: 100
-      },
-      {
-        id: '9',
-        name: '后现代风格办公室设计',
-        customer: '王总',
-        status: 'in-progress',
-        designer: '王设计师',
-        startDate: '2025-08-25',
-        endDate: '2025-10-15',
-        budget: 95000,
-        progress: 50
-      },
-      {
-        id: '10',
-        name: '新古典主义住宅设计',
-        customer: '陈女士',
-        status: 'on-hold',
-        designer: '张设计师',
-        startDate: '2025-08-10',
-        endDate: '2025-11-01',
-        budget: 130000,
-        progress: 30
-      }
-    ]);
-    
-    this.applyFilters();
-  }
-
   applyFilters(): void {
     let result = [...this.projects()];
-    
+
     // 搜索过滤
     if (this.searchTerm) {
       const term = this.searchTerm.toLowerCase();
-      result = result.filter(project => 
-        project.name.toLowerCase().includes(term) ||
+      result = result.filter(project =>
+        project.title.toLowerCase().includes(term) ||
         project.customer.toLowerCase().includes(term) ||
-        project.designer.toLowerCase().includes(term)
+        project.assignee.toLowerCase().includes(term)
       );
     }
     
@@ -260,17 +141,15 @@ export class ProjectManagement implements OnInit {
       result = result.filter(project => project.status === this.statusFilter);
     }
     
-    // 排序
+    // 排序 - 按更新时间或创建时间
     result.sort((a, b) => {
-      if (this.sortColumn === 'startDate' || this.sortColumn === 'endDate') {
-        const dateA = new Date(a[this.sortColumn]).getTime();
-        const dateB = new Date(b[this.sortColumn]).getTime();
+      if (this.sortColumn === 'updatedAt' || this.sortColumn === 'createdAt') {
+        const dateA = a[this.sortColumn] ? new Date(a[this.sortColumn] as Date).getTime() : 0;
+        const dateB = b[this.sortColumn] ? new Date(b[this.sortColumn] as Date).getTime() : 0;
         return this.sortDirection === 'asc' ? dateA - dateB : dateB - dateA;
-      } else if (this.sortColumn === 'budget' || this.sortColumn === 'progress') {
-        return this.sortDirection === 'asc' ? a[this.sortColumn] - b[this.sortColumn] : b[this.sortColumn] - a[this.sortColumn];
       } else {
-        const valueA = a[this.sortColumn as keyof Project]?.toString().toLowerCase() || '';
-        const valueB = b[this.sortColumn as keyof Project]?.toString().toLowerCase() || '';
+        const valueA = String((a as any)[this.sortColumn] || '').toLowerCase();
+        const valueB = String((b as any)[this.sortColumn] || '').toLowerCase();
         return this.sortDirection === 'asc' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA);
       }
     });
@@ -371,20 +250,20 @@ export class ProjectManagement implements OnInit {
 
   // 项目状态统计计算属性
   get inProgressProjectsCount(): number {
-    return this.projects().filter(p => p.status === 'in-progress').length;
+    return this.projects().filter(p => p.status === '进行中').length;
   }
 
   get completedProjectsCount(): number {
-    return this.projects().filter(p => p.status === 'completed').length;
+    return this.projects().filter(p => p.status === '已完成').length;
   }
 
   get pendingProjectsCount(): number {
-    return this.projects().filter(p => p.status === 'pending').length;
+    return this.projects().filter(p => p.status === '待分配').length;
   }
 
-  // 项目总预算计算属性
-  get totalProjectsBudget(): number {
-    return this.projects().reduce((sum, p) => sum + p.budget, 0);
+  // 项目总
+  get totalProjectsCount(): number {
+    return this.projects().length;
   }
 
   onPageChange(page: number): void {

+ 4 - 19
src/app/pages/admin/services/admin-data.service.ts

@@ -1,5 +1,5 @@
 import { Injectable } from '@angular/core';
-import { FmodeParse, FmodeObject, FmodeQuery } from 'fmode-ng/core';
+import { FmodeObject, FmodeParse, FmodeQuery } from 'fmode-ng/core';
 
 /**
  * 管理系统数据服务基类
@@ -10,10 +10,10 @@ import { FmodeParse, FmodeObject, FmodeQuery } from 'fmode-ng/core';
 })
 export class AdminDataService {
   private readonly COMPANY_ID = 'cDL6R1hgSi'; // 映三色帐套ID
-  private Parse: FmodeParse;
+  private Parse: any;
 
   constructor() {
-    this.Parse = (window as any).Parse || FmodeParse.with('nova');
+    this.Parse = FmodeParse.with("nova")
   }
 
   /**
@@ -159,22 +159,7 @@ export class AdminDataService {
    * 将Parse对象转换为普通对象
    */
   toJSON(obj: FmodeObject): any {
-    const json: any = {
-      objectId: obj.id,
-      createdAt: obj.get('createdAt'),
-      updatedAt: obj.get('updatedAt')
-    };
-
-    // 获取所有属性
-    obj.attributes && Object.keys(obj.attributes).forEach(key => {
-      const value = obj.get(key);
-      if (value && typeof value === 'object' && value.toJSON) {
-        json[key] = value.toJSON();
-      } else {
-        json[key] = value;
-      }
-    });
-
+    let json = obj.toJSON();
     return json;
   }
 

+ 1 - 1
src/app/pages/admin/services/customer.service.ts

@@ -31,7 +31,7 @@ export class CustomerService {
         if (options?.keyword) {
           const kw = options.keyword.trim();
           if (kw) {
-            query.matches('name', kw, 'i');
+            query.matches('name', new RegExp(kw, 'i'));
           }
         }
       }

+ 1 - 1
src/app/pages/admin/services/department.service.ts

@@ -32,7 +32,7 @@ export class DepartmentService {
         if (options?.keyword) {
           const kw = options.keyword.trim();
           if (kw) {
-            query.matches('name', kw, 'i');
+            query.matches('name', new RegExp(kw, 'i'));
           }
         }
       }

+ 1 - 1
src/app/pages/admin/services/employee.service.ts

@@ -40,7 +40,7 @@ export class EmployeeService {
         if (options?.keyword) {
           const kw = options.keyword.trim();
           if (kw) {
-            query.matches('name', kw, 'i');
+            query.matches('name', new RegExp(kw, 'i'));
           }
         }
       }

+ 1 - 1
src/app/pages/admin/services/groupchat.service.ts

@@ -29,7 +29,7 @@ export class GroupChatService {
         if (options?.keyword) {
           const kw = options.keyword.trim();
           if (kw) {
-            query.matches('name', kw, 'i');
+            query.matches('name', new RegExp(kw, 'i'));
           }
         }
 

+ 1 - 1
src/app/pages/admin/services/project.service.ts

@@ -33,7 +33,7 @@ export class ProjectService {
           const kw = options.keyword.trim();
           if (kw) {
             // 搜索项目标题
-            query.matches('title', kw, 'i');
+            query.matches('title', new RegExp(kw, 'i'));
           }
         }
       }