Sfoglia il codice sorgente

update:page-crm-data

0235664 1 giorno fa
parent
commit
9b4b71454a

+ 137 - 5
ai-assisant/src/modules/crm/mobile/page-crm-data/page-crm-data.html

@@ -29,19 +29,151 @@
       <div *ngFor="let tag of sourceTags" 
            class="source-tag" 
            [class.active]="tag.active"
-           (click)="selectTag(tag)">
+           (click)="handleTagClick(tag)"
+           (mouseenter)="tag.hover = true"
+           (mouseleave)="tag.hover = false">
+        <i *ngIf="tag.icon" class="fas fa-{{tag.icon}}"></i>
         {{tag.name}}
       </div>
     </div>
+    <!-- 新增四个可点击标签 -->
+    <!-- <div class="source-tag clickable-tag" (click)="openModal('meeting')">
+      <i class="fas fa-calendar-alt"></i> 会议记录
+    </div>
+    <div class="source-tag clickable-tag" (click)="openModal('contract')">
+      <i class="fas fa-file-signature"></i> 客户合同
+    </div>
+    <div class="source-tag clickable-tag" (click)="openModal('feedback')">
+      <i class="fas fa-comment-dots"></i> 反馈表
+    </div>
+    <div class="source-tag clickable-tag" (click)="openModal('training')">
+      <i class="fas fa-chalkboard-teacher"></i> 培训资料
+    </div> -->
+  </div>
+  <!-- 新增模态框结构 -->
+<div class="modal-overlay" *ngIf="showModal" (click)="closeModal()">
+  <div class="modal-content" (click)="$event.stopPropagation()">
+    <button class="modal-close" (click)="closeModal()">
+      <i class="fas fa-times"></i>
+    </button>
+    <h3 class="modal-title">{{modalTitle}}</h3>
+    <div class="modal-body">
+      <ng-container [ngSwitch]="modalType">
+        <div *ngSwitchCase="'meeting'">
+          <p id="meeting-desc">请上传会议记录文件或输入会议内容:</p>
+          <label for="meeting-content" class="sr-only">会议内容</label>
+          <textarea id="meeting-content" class="modal-textarea" 
+                    placeholder="输入会议内容..." 
+                    aria-describedby="meeting-desc"></textarea>
+          <label for="meeting-file" class="file-upload-label">
+            <span class="sr-only">会议记录文件</span>
+            <input id="meeting-file" type="file" class="modal-file-input" 
+                   aria-describedby="meeting-desc">
+          </label>
+        </div>
+        <div *ngSwitchCase="'contract'">
+          <p id="contract-desc">请上传客户合同文件:</p>
+          <label for="contract-file" class="file-upload-label">
+            <span class="sr-only">客户合同文件</span>
+            <input id="contract-file" type="file" class="modal-file-input" 
+                   aria-describedby="contract-desc">
+          </label>
+          <div class="contract-info">
+            <label for="contract-number">合同编号</label>
+            <input id="contract-number" type="text" 
+                   placeholder="输入合同编号" 
+                   class="modal-input">
+            <label for="client-name">客户名称</label>
+            <input id="client-name" type="text" 
+                   placeholder="输入客户名称" 
+                   class="modal-input">
+          </div>
+        </div>
+        <div *ngSwitchCase="'feedback'">
+          <p id="feedback-desc">请选择反馈类型并上传文件:</p>
+          <label for="feedback-type">反馈类型</label>
+          <select id="feedback-type" class="modal-select" 
+                  aria-describedby="feedback-desc">
+            <option value="customer">客户反馈</option>
+            <option value="internal">内部反馈</option>
+            <option value="product">产品反馈</option>
+          </select>
+          <label for="feedback-file" class="file-upload-label">
+            <span class="sr-only">反馈文件</span>
+            <input id="feedback-file" type="file" class="modal-file-input" 
+                   aria-describedby="feedback-desc">
+          </label>
+        </div>
+        <div *ngSwitchCase="'training'">
+          <p id="training-desc">请上传培训资料:</p>
+          <label for="training-file" class="file-upload-label">
+            <span class="sr-only">培训资料文件</span>
+            <input id="training-file" type="file" class="modal-file-input" 
+                   aria-describedby="training-desc">
+          </label>
+          <div class="training-info">
+            <label for="training-topic">培训主题</label>
+            <input id="training-topic" type="text" 
+                   placeholder="输入培训主题" 
+                   class="modal-input">
+            <label for="training-date">培训日期</label>
+            <input id="training-date" type="date" 
+                   class="modal-input"
+                   aria-label="选择培训日期">
+          </div>
+        </div>
+      </ng-container>
+    </div>
+    <!-- 添加到modal-overlay结构后面 -->
+<div class="tag-selector-overlay" *ngIf="showTagSelector" (click)="closeTagSelector()">
+  <div class="tag-selector-container" (click)="$event.stopPropagation()">
+    <div class="tag-selector-header">
+      <h3>选择标签类型</h3>
+      <button class="tag-selector-close" (click)="closeTagSelector()">
+        <i class="fas fa-times"></i>
+      </button>
+    </div>
+    
+    <div class="tag-selector-body">
+      <div class="tag-scroll-container">
+        <div *ngFor="let tag of availableTags" 
+             class="tag-option"
+             [class.selected]="isTagSelected(tag)"
+             (click)="toggleTagSelection(tag)">
+          <i class="fas fa-{{tag.icon}}"></i>
+          {{tag.name}}
+          <span class="checkmark" *ngIf="isTagSelected(tag)">
+            <i class="fas fa-check"></i>
+          </span>
+        </div>
+      </div>
+    </div>
+    
+    <div class="tag-selector-footer">
+      <button class="tag-selector-btn cancel" (click)="closeTagSelector()">
+        取消
+      </button>
+      <button class="tag-selector-btn confirm" (click)="confirmTagSelection()">
+        确认选择
+      </button>
+    </div>
+  </div>
+</div>
+    <div class="modal-footer">
+      <button class="modal-button cancel" (click)="closeModal()">取消</button>
+      <button class="modal-button confirm" (click)="confirmUpload()">确认上传</button>
+    </div>
+  </div>
+</div>
     
     <div class="custom-tag-form">
       <input type="text" class="custom-tag-input" 
              [(ngModel)]="newTag" 
              placeholder="输入新数据类型..."
              (keyup.enter)="addTag()">
-      <button class="add-tag-btn" (click)="addTag()">
-        添加标签
-      </button>
+             <button class="add-tag-btn" (click)="openTagSelector()">
+              添加标签
+            </button>
     </div>
     
     <div class="upload-method-selector">
@@ -197,4 +329,4 @@
       </div>
     </div>
   </div>
-</div>
+

+ 451 - 1
ai-assisant/src/modules/crm/mobile/page-crm-data/page-crm-data.scss

@@ -760,4 +760,454 @@
     border-radius: 3px;
     transition: width 0.3s ease;
   }
-  
+  //7.1
+  /* 原有样式保持不变... */
+
+/* 新增可点击标签样式 */
+.clickable-tag {
+  cursor: pointer;
+  transition: all 0.3s ease;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  
+  &:hover {
+    background-color: rgba(0, 123, 255, 0.1);
+    transform: translateY(-2px);
+  }
+  
+  i {
+    font-size: 14px;
+  }
+}
+
+/* 模态框样式 */
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+  backdrop-filter: blur(3px);
+}
+
+.modal-content {
+  background-color: white;
+  border-radius: 12px;
+  width: 90%;
+  max-width: 500px;
+  max-height: 80vh;
+  overflow-y: auto;
+  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
+  padding: 20px;
+  position: relative;
+  animation: modalFadeIn 0.3s ease;
+}
+
+@keyframes modalFadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.modal-close {
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  background: none;
+  border: none;
+  font-size: 20px;
+  cursor: pointer;
+  color: #666;
+  
+  &:hover {
+    color: #333;
+  }
+}
+
+.modal-title {
+  margin-top: 0;
+  margin-bottom: 20px;
+  color: #2c3e50;
+  font-size: 20px;
+}
+
+.modal-body {
+  margin-bottom: 20px;
+  
+  p {
+    margin-top: 0;
+    color: #555;
+  }
+}
+
+.modal-input, .modal-select, .modal-textarea, .modal-file-input {
+  width: 100%;
+  padding: 10px;
+  margin-bottom: 15px;
+  border: 1px solid #ddd;
+  border-radius: 6px;
+  font-size: 14px;
+  
+  &:focus {
+    outline: none;
+    border-color: #007bff;
+  }
+}
+
+.modal-textarea {
+  min-height: 100px;
+  resize: vertical;
+}
+
+.modal-file-input {
+  padding: 8px;
+}
+
+.modal-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+.modal-button {
+  padding: 10px 20px;
+  border-radius: 6px;
+  border: none;
+  cursor: pointer;
+  font-weight: 500;
+  transition: all 0.2s ease;
+  
+  &.cancel {
+    background-color: #f1f1f1;
+    color: #333;
+    
+    &:hover {
+      background-color: #e0e0e0;
+    }
+  }
+  
+  &.confirm {
+    background-color: #007bff;
+    color: white;
+    
+    &:hover {
+      background-color: #0069d9;
+    }
+  }
+}
+
+.contract-info, .training-info {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 10px;
+  margin-bottom: 15px;
+}
+/* 添加在原有样式之后 */
+
+/* 可访问性相关样式 */
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
+.file-upload-label {
+  display: block;
+  margin-bottom: 15px;
+  cursor: pointer;
+  
+  input[type="file"] {
+    width: 100%;
+  }
+}
+
+label:not(.sr-only):not(.file-upload-label) {
+  display: block;
+  margin-bottom: 8px;
+  font-size: 14px;
+  color: #555;
+  font-weight: 500;
+}
+/* 修复source-tag的悬停效果 */
+.source-tag {
+  /* 原有基础样式保持不变 */
+  background: #f0f4f8;
+  padding: 8px 16px;
+  border-radius: 20px;
+  font-size: 14px;
+  color: $text-dark;
+  transition: all 0.2s ease;
+  border: 1px solid transparent;
+  cursor: pointer;
+  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
+  display: flex;
+  align-items: center;
+  gap: 8px;
+
+  /* 确保悬停效果优先级 */
+  &:hover {
+    background: #e0e0e0 !important;
+    transform: translateY(-2px) !important;
+    box-shadow: 0 4px 8px rgba(0,0,0,0.1) !important;
+  }
+
+  /* 活动状态 */
+  &.active {
+    border-color: $primary;
+    box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.3);
+    color: $primary;
+    font-weight: 600;
+    background: rgba(66, 133, 244, 0.08);
+    
+    /* 活动状态下的悬停效果 */
+    &:hover {
+      background: rgba(66, 133, 244, 0.15) !important;
+    }
+  }
+
+  /* 图标样式 */
+  i {
+    margin-right: 0; /* 改为使用gap布局 */
+    font-size: 14px;
+  }
+}
+.source-tag {
+  /* 原有基础样式保持不变 */
+  background: #f0f4f8;
+  padding: 8px 16px;
+  border-radius: 20px;
+  font-size: 14px;
+  color: $text-dark;
+  transition: all 0.3s ease; /* 增加过渡时间使效果更平滑 */
+  border: 1px solid transparent; /* 初始透明边框 */
+  cursor: pointer;
+  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  position: relative; /* 为伪元素定位做准备 */
+
+  /* 新增悬停蓝色边框效果 */
+  &:hover {
+    border-color: $primary; /* 蓝色边框 */
+    box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.3); /* 外发光效果 */
+    
+    /* 如果想要更明显的发光效果,可以使用以下替代方案 */
+    /*
+    box-shadow: 0 0 0 2px $primary-light, 
+                0 4px 8px rgba(0,0,0,0.1);
+    */
+  }
+
+  /* 活动状态 */
+  &.active {
+    border-color: $primary;
+    box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.3);
+    color: $primary;
+    font-weight: 600;
+    background: rgba(66, 133, 244, 0.08);
+    
+    /* 活动状态下的悬停效果 */
+    &:hover {
+      background: rgba(66, 133, 244, 0.15);
+      box-shadow: 0 0 0 3px rgba(66, 133, 244, 0.4); /* 悬停时加强效果 */
+    }
+  }
+
+  /* 图标样式 */
+  i {
+    font-size: 14px;
+  }
+}
+
+// 
+/* 标签选择器样式 */
+.tag-selector-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1001; /* 比模态框更高 */
+  backdrop-filter: blur(3px);
+}
+
+.tag-selector-container {
+  background-color: white;
+  border-radius: 16px;
+  width: 90%;
+  max-width: 400px;
+  max-height: 70vh;
+  overflow: hidden;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
+  animation: modalFadeIn 0.3s ease;
+}
+
+.tag-selector-header {
+  padding: 20px;
+  border-bottom: 1px solid #eee;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  
+  h3 {
+    margin: 0;
+    font-size: 18px;
+    color: $text-dark;
+  }
+}
+
+.tag-selector-close {
+  background: none;
+  border: none;
+  font-size: 20px;
+  color: $text-light;
+  cursor: pointer;
+  transition: color 0.2s;
+  
+  &:hover {
+    color: $primary;
+  }
+}
+
+.tag-selector-body {
+  padding: 10px 0;
+}
+
+.tag-scroll-container {
+  max-height: 50vh;
+  overflow-y: auto;
+  padding: 0 15px;
+}
+
+.tag-option {
+  padding: 12px 15px;
+  margin: 5px 0;
+  border-radius: 10px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  transition: all 0.2s;
+  position: relative;
+  border: 1px solid #eee;
+  
+  &:hover {
+    background-color: rgba(66, 133, 244, 0.1);
+    transform: translateX(5px);
+  }
+  
+  &.selected {
+    background-color: rgba(66, 133, 244, 0.15);
+    border-color: $primary;
+  }
+  
+  i {
+    margin-right: 10px;
+    color: $primary;
+    width: 20px;
+    text-align: center;
+  }
+}
+
+.checkmark {
+  margin-left: auto;
+  color: $primary;
+  font-size: 14px;
+}
+
+.tag-selector-footer {
+  display: flex;
+  justify-content: flex-end;
+  padding: 15px;
+  border-top: 1px solid #eee;
+  gap: 10px;
+}
+
+.tag-selector-btn {
+  padding: 10px 20px;
+  border-radius: 20px;
+  border: none;
+  cursor: pointer;
+  font-weight: 500;
+  transition: all 0.2s;
+  
+  &.cancel {
+    background-color: #f5f5f5;
+    color: $text-dark;
+    
+    &:hover {
+      background-color: #e0e0e0;
+    }
+  }
+  
+  &.confirm {
+    background-color: $primary;
+    color: white;
+    
+    &:hover {
+      background-color: $primary-dark;
+    }
+  }
+}
+.add-tag-btn {
+  position: relative; // 确保按钮在顶层
+  z-index: 1;
+}
+/* 在文件末尾添加以下内容 */
+
+/* 标签选择器样式 */
+.tag-selector-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1001;
+  backdrop-filter: blur(3px);
+}
+
+.tag-selector-container {
+  background-color: white;
+  border-radius: 16px;
+  width: 90%;
+  max-width: 400px;
+  max-height: 70vh;
+  overflow: hidden;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
+}
+
+/* 确保按钮层级 */
+.add-tag-btn {
+  position: relative;
+  z-index: 1;
+}
+
+/* 模态框层级 */
+.modal-overlay {
+  z-index: 1000;
+}

+ 191 - 7
ai-assisant/src/modules/crm/mobile/page-crm-data/page-crm-data.ts

@@ -13,15 +13,194 @@ import { FormsModule } from '@angular/forms';
 
 export class PageCrmData implements AfterViewInit {
   // 标签相关数据
-  sourceTags = [
-    { name: '会议记录', active: true },
-    { name: '客户合同', active: false },
-    { name: '反馈表', active: false },
-    { name: '培训资料', active: false }
-  ];
+  // 标签相关数据
+sourceTags = [
+  { 
+    name: '会议记录', 
+    active: true, 
+    icon: 'calendar-alt',
+    type: 'meeting',
+    hover: false 
+  },
+  { 
+    name: '客户合同', 
+    active: false,
+    icon: 'file-signature',
+    type: 'contract',
+    hover: false 
+  },
+  { 
+    name: '反馈表', 
+    active: false,
+    icon: 'comment-dots',
+    type: 'feedback',
+    hover: false 
+  },
+  { 
+    name: '培训资料', 
+    active: false,
+    icon: 'chalkboard-teacher',
+    type: 'training',
+    hover: false 
+  }
+];
+
+// // 处理标签点击
+// handleTagClick(tag: any) {
+//   if (tag.type) {
+//     this.openModal(tag.type);
+//   } else {
+//     this.selectTag(tag); // 保持原有的selectTag行为
+//   }
+// }
+handleMouseEnter(tag: any) {
+  tag.hover = true;
+}
+
+handleMouseLeave(tag: any) {
+  tag.hover = false;
+}
+
+handleTagClick(tag: any) {
+  console.log('Tag clicked:', tag);  // 调试输出
+  
+  this.sourceTags.forEach(t => t.active = false);
+  tag.active = true;
+  
+  if (tag.type) {
+    console.log('Opening modal for type:', tag.type);  // 调试输出
+    this.openModal(tag.type);
+  }
+}
   
   newTag = '';
+  //7.1
+  // 新增模态框相关变量
+  showModal: boolean = false;
+  modalType: string = '';
+  modalTitle: string = '';
+  openModal(type: string): void {
+    this.modalType = type;
+    this.showModal = true;
+    
+    switch(type) {
+      case 'meeting':
+        this.modalTitle = '上传会议记录';
+        break;
+      case 'contract':
+        this.modalTitle = '上传客户合同';
+        break;
+      case 'feedback':
+        this.modalTitle = '上传反馈表';
+        break;
+      case 'training':
+        this.modalTitle = '上传培训资料';
+        break;
+    }
+  }
+  
+  closeModal(): void {
+    this.showModal = false;
+    this.modalType = '';
+    this.modalTitle = '';
+  }
   
+  confirmUpload(): void {
+    // 这里处理上传逻辑
+    console.log(`上传${this.modalTitle}确认`);
+    
+    // 可以添加上传逻辑,然后关闭模态框
+    this.closeModal();
+    
+    // 可以在这里添加上传状态提示
+    this.uploadStatus = {
+      visible: true,
+      success: true,
+      message: `${this.modalTitle}上传成功!`,
+      progress: 100
+    };
+    
+    // 3秒后隐藏上传状态
+    setTimeout(() => {
+      this.uploadStatus.visible = false;
+    }, 3000);
+  }
+
+  //
+  // 在PageCrmData类中添加这些属性和方法
+
+// 可用的标签列表
+availableTags = [
+  { id: 1, name: '会议记录', icon: 'calendar-alt' },
+  { id: 2, name: '客户合同', icon: 'file-signature' },
+  { id: 3, name: '反馈表', icon: 'comment-dots' },
+  { id: 4, name: '培训资料', icon: 'chalkboard-teacher' },
+  { id: 5, name: '销售报告', icon: 'chart-line' },
+  { id: 6, name: '产品文档', icon: 'file-alt' },
+  { id: 7, name: '市场分析', icon: 'chart-pie' },
+  { id: 8, name: '客户反馈', icon: 'comments' },
+  { id: 9, name: '财务数据', icon: 'money-bill-wave' },
+  { id: 10, name: '项目计划', icon: 'project-diagram' },
+  { id: 11, name: '技术文档', icon: 'file-code' },
+  { id: 12, name: '用户手册', icon: 'book' },
+  { id: 13, name: '测试报告', icon: 'vial' },
+  { id: 14, name: '设计稿', icon: 'palette' },
+  { id: 15, name: '会议音频', icon: 'microphone' },
+  // 可以继续添加更多标签...
+];
+
+// 选中的标签
+selectedTags: any[] = [];
+
+// 控制标签选择器显示
+showTagSelector = false;
+
+openTagSelector(event?: Event) {
+  if (event) event.stopPropagation();
+  this.showModal = false;
+  this.showTagSelector = true;
+}
+
+closeTagSelector() {
+  this.showTagSelector = false;
+}
+
+toggleTagSelection(tag: any) {
+  const index = this.selectedTags.findIndex(t => t.id === tag.id);
+  if (index >= 0) {
+    this.selectedTags.splice(index, 1);
+  } else {
+    this.selectedTags.push({...tag});
+  }
+}
+
+isTagSelected(tag: any): boolean {
+  return this.selectedTags.some(t => t.id === tag.id);
+}
+
+confirmTagSelection() {
+  if (this.selectedTags.length > 0) {
+    this.selectedTags.forEach(tag => {
+      if (!this.sourceTags.some(t => t.name === tag.name)) {
+        this.sourceTags.push({
+          name: tag.name,
+          icon: tag.icon,
+          active: false,
+          type: tag.name.replace(/\s+/g, '-').toLowerCase(),
+          hover: false
+        });
+      }
+    });
+    this.selectedTags = [];
+    this.showTagSelector = false;
+  }
+}
+
+
+
+
+
+
   // 上传方式
   uploadMethods = [
     { name: '拍照', icon: 'camera', active: false },
@@ -74,7 +253,12 @@ export class PageCrmData implements AfterViewInit {
   // 添加新标签
   addTag() {
     if (this.newTag.trim()) {
-      this.sourceTags.push({ name: this.newTag.trim(), active: true });
+      this.sourceTags.push({
+        name: this.newTag.trim(), active: true,
+        icon: '',
+        type: '',
+        hover: false
+      });
       this.selectTag(this.sourceTags[this.sourceTags.length - 1]);
       this.newTag = '';
     }