0235664 пре 20 часа
родитељ
комит
e4eddd79a7

+ 432 - 0
ai-assisant/src/lib/cloud/ncloud.ts

@@ -0,0 +1,432 @@
+// CloudObject.ts
+
+let serverURL = `https://dev.fmode.cn/parse`;
+if (location.protocol == "http:") {
+    serverURL = `http://dev.fmode.cn:1337/parse`;
+}
+
+export class CloudObject {
+    
+    className: string;
+    id: string | undefined = undefined;
+    createdAt: any;
+    updatedAt: any;
+    data: Record<string, any> = {};
+
+    constructor(className: string) {
+        this.className = className;
+    }
+
+    toPointer() {
+        return { "__type": "Pointer", "className": this.className, "objectId": this.id };
+    }
+
+    set(json: Record<string, any>) {
+        Object.keys(json).forEach(key => {
+            if (["objectId", "id", "createdAt", "updatedAt"].indexOf(key) > -1) {
+                return;
+            }
+            this.data[key] = json[key];
+        });
+    }
+
+    get(key: string) {
+        return this.data[key] || null;
+    }
+
+    async save() {
+        let method = "POST";
+        let url = serverURL + `/classes/${this.className}`;
+
+        // 更新
+        if (this.id) {
+            url += `/${this.id}`;
+            method = "PUT";
+        }
+
+        const body = JSON.stringify(this.data);
+        const response = await fetch(url, {
+            headers: {
+                "content-type": "application/json;charset=UTF-8",
+                "x-parse-application-id": "dev"
+            },
+            body: body,
+            method: method,
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+        }
+        if (result?.objectId) {
+            this.id = result?.objectId;
+        }
+        return this;
+    }
+
+    async destroy() {
+        if (!this.id) return;
+        const response = await fetch(serverURL + `/classes/${this.className}/${this.id}`, {
+            headers: {
+                "x-parse-application-id": "dev"
+            },
+            body: null,
+            method: "DELETE",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result) {
+            this.id = undefined;
+        }
+        return true;
+    }
+}
+
+// CloudQuery.ts
+export class CloudQuery {
+    className: string;
+    queryParams: Record<string, any> = { where: {} };
+
+    constructor(className: string) {
+        this.className = className;
+    }
+
+    include(...fileds: string[]) {
+        this.queryParams["include"] = fileds;
+    }
+    greaterThan(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$gt"] = value;
+    }
+
+    greaterThanAndEqualTo(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$gte"] = value;
+    }
+
+    lessThan(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$lt"] = value;
+    }
+
+    lessThanAndEqualTo(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$lte"] = value;
+    }
+
+    equalTo(key: string, value: any) {
+        if (!this.queryParams["where"]) this.queryParams["where"] = {};
+        this.queryParams["where"][key] = value;
+    }
+
+    async get(id: string) {
+        const url = serverURL + `/classes/${this.className}/${id}?`;
+
+        const response = await fetch(url, {
+            headers: {
+                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+                "x-parse-application-id": "dev"
+            },
+            body: null,
+            method: "GET",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const json = await response?.json();
+        if (json) {
+            let existsObject = this.dataToObj(json)
+            return existsObject;
+        }
+        return null
+    }
+
+    async find(): Promise<Array<CloudObject>> {
+        let url = serverURL + `/classes/${this.className}?`;
+
+        let queryStr = ``
+        Object.keys(this.queryParams).forEach(key => {
+            let paramStr = JSON.stringify(this.queryParams[key]);
+            if (key == "include") {
+                paramStr = this.queryParams[key]?.join(",")
+            }
+            if (queryStr) {
+                url += `${key}=${paramStr}`;
+            } else {
+                url += `&${key}=${paramStr}`;
+            }
+        })
+        // if (Object.keys(this.queryParams["where"]).length) {
+
+        // }
+
+        const response = await fetch(url, {
+            headers: {
+                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+                "x-parse-application-id": "dev"
+            },
+            body: null,
+            method: "GET",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const json = await response?.json();
+        let list = json?.results || []
+        let objList = list.map((item: any) => this.dataToObj(item))
+        return objList || [];
+    }
+
+
+    async first() {
+        let url = serverURL + `/classes/${this.className}?`;
+
+        if (Object.keys(this.queryParams["where"]).length) {
+            const whereStr = JSON.stringify(this.queryParams["where"]);
+            url += `where=${whereStr}&limit=1`;
+        }
+
+        const response = await fetch(url, {
+            headers: {
+                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+                "x-parse-application-id": "dev"
+            },
+            body: null,
+            method: "GET",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const json = await response?.json();
+        const exists = json?.results?.[0] || null;
+        if (exists) {
+            let existsObject = this.dataToObj(exists)
+            return existsObject;
+        }
+        return null
+    }
+
+    dataToObj(exists: any): CloudObject {
+        let existsObject = new CloudObject(this.className);
+        Object.keys(exists).forEach(key => {
+            if (exists[key]?.__type == "Object") {
+                exists[key] = this.dataToObj(exists[key])
+            }
+        })
+        existsObject.set(exists);
+        existsObject.id = exists.objectId;
+        existsObject.createdAt = exists.createdAt;
+        existsObject.updatedAt = exists.updatedAt;
+        return existsObject;
+    }
+}
+
+// CloudUser.ts
+export class CloudUser extends CloudObject {
+    constructor() {
+        super("_User"); // 假设用户类在Parse中是"_User"
+        // 读取用户缓存信息
+        let userCacheStr = localStorage.getItem("NCloud/dev/User")
+        if (userCacheStr) {
+            let userData = JSON.parse(userCacheStr)
+            // 设置用户信息
+            this.id = userData?.objectId;
+            this.sessionToken = userData?.sessionToken;
+            this.data = userData; // 保存用户数据
+        }
+    }
+
+    sessionToken: string | null = ""
+    /** 获取当前用户信息 */
+    async current() {
+        if (!this.sessionToken) {
+            console.error("用户未登录");
+            return null;
+        }
+        return this;
+        // const response = await fetch(serverURL + `/users/me`, {
+        //     headers: {
+        //         "x-parse-application-id": "dev",
+        //         "x-parse-session-token": this.sessionToken // 使用sessionToken进行身份验证
+        //     },
+        //     method: "GET"
+        // });
+
+        // const result = await response?.json();
+        // if (result?.error) {
+        //     console.error(result?.error);
+        //     return null;
+        // }
+        // return result;
+    }
+
+    /** 登录 */
+    async login(username: string, password: string): Promise<CloudUser | null> {
+        const response = await fetch(serverURL + `/login`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "Content-Type": "application/json"
+            },
+            body: JSON.stringify({ username, password }),
+            method: "POST"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null;
+        }
+
+        // 设置用户信息
+        this.id = result?.objectId;
+        this.sessionToken = result?.sessionToken;
+        this.data = result; // 保存用户数据
+        // 缓存用户信息
+        console.log(result)
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(result))
+        return this;
+    }
+
+    /** 登出 */
+    async logout() {
+        if (!this.sessionToken) {
+            console.error("用户未登录");
+            return;
+        }
+
+        const response = await fetch(serverURL + `/logout`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "x-parse-session-token": this.sessionToken
+            },
+            method: "POST"
+        });
+
+        let result = await response?.json();
+
+        if (result?.error) {
+            console.error(result?.error);
+            if (result?.error == "Invalid session token") {
+                this.clearUserCache()
+                return true;
+            }
+            return false;
+        }
+
+        this.clearUserCache()
+        return true;
+    }
+    clearUserCache() {
+        // 清除用户信息
+        localStorage.removeItem("NCloud/dev/User")
+        this.id = undefined;
+        this.sessionToken = null;
+        this.data = {};
+    }
+
+    /** 注册 */
+    async signUp(username: string, password: string, additionalData: Record<string, any> = {}) {
+        const userData = {
+            username,
+            password,
+            ...additionalData // 合并额外的用户数据
+        };
+
+        const response = await fetch(serverURL + `/users`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "Content-Type": "application/json"
+            },
+            body: JSON.stringify(userData),
+            method: "POST"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null;
+        }
+
+        // 设置用户信息
+        // 缓存用户信息
+        console.log(result)
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(result))
+        this.id = result?.objectId;
+        this.sessionToken = result?.sessionToken;
+        this.data = result; // 保存用户数据
+        return this;
+    }
+
+    override async save() {
+        let method = "POST";
+        let url = serverURL + `/users`;
+
+        // 更新用户信息
+        if (this.id) {
+            url += `/${this.id}`;
+            method = "PUT";
+        }
+
+        let data: any = JSON.parse(JSON.stringify(this.data))
+        delete data.createdAt
+        delete data.updatedAt
+        delete data.ACL
+        delete data.objectId
+        const body = JSON.stringify(data);
+        let headersOptions: any = {
+            "content-type": "application/json;charset=UTF-8",
+            "x-parse-application-id": "dev",
+            "x-parse-session-token": this.sessionToken, // 添加sessionToken以进行身份验证
+        }
+        const response = await fetch(url, {
+            headers: headersOptions,
+            body: body,
+            method: method,
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+        }
+        if (result?.objectId) {
+            this.id = result?.objectId;
+        }
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(this.data))
+        return this;
+    }
+}
+
+export class CloudApi {
+    async fetch(path: string, body: any, options?: {
+        method: string
+        body: any
+    }) {
+
+        let reqOpts: any = {
+            headers: {
+                "x-parse-application-id": "dev",
+                "Content-Type": "application/json"
+            },
+            method: options?.method || "POST",
+            mode: "cors",
+            credentials: "omit"
+        }
+        if (body || options?.body) {
+            reqOpts.body = JSON.stringify(body || options?.body);
+            reqOpts.json = true;
+        }
+        let host = `https://dev.fmode.cn`
+        // host = `http://127.0.0.1:1337`
+        let url = `${host}/api/` + path
+        console.log(url, reqOpts)
+        const response = await fetch(url, reqOpts);
+        let json = await response.json();
+        return json
+    }
+}

+ 127 - 17
ai-assisant/src/modules/crm/mobile/page-crm-data/page-crm-data.html

@@ -235,29 +235,139 @@
       </div>
     </div>
     
-    <!-- 每日数据记录 -->
-    <div class="data-record">
-      <div class="record-header">
-        <div class="card-title">每日数据记录</div>
-        <a class="view-all">
-          查看全部 <i class="fas fa-arrow-right"></i>
-        </a>
+   <!-- 每日数据记录部分 - 保持不变 -->
+<div class="data-record">
+  <div class="record-header">
+    <div class="card-title">每日数据记录</div>
+    <a class="view-all" (click)="openTrainingDataModal()">
+      查看全部 <i class="fas fa-arrow-right"></i>
+    </a>
+  </div>
+  
+  <div class="record-list">
+    @for (stat of dashboardStats; track stat.label) {
+      <div class="record-item">
+        <div class="record-title">
+          <i class="fas fa-{{stat.icon}}"></i> {{stat.label}}
+        </div>
+        <div class="record-value">{{stat.value}}</div>
+        <div class="record-trend" [class.trend-up]="stat.trendUp" [class.trend-down]="!stat.trendUp">
+          <i class="fas fa-arrow-{{stat.trendUp ? 'up' : 'down'}} trend-icon"></i> {{stat.trend}}%
+        </div>
       </div>
-      
-      <div class="record-list">
-        <div *ngFor="let stat of dashboardStats" class="record-item">
-          <div class="record-title">
-            <i class="fas fa-{{stat.icon}}"></i> {{stat.label}}
-          </div>
-          <div class="record-value">{{stat.value}}</div>
-          <div class="record-trend" [class.trend-up]="stat.trendUp" [class.trend-down]="!stat.trendUp">
-            <i class="fas fa-arrow-{{stat.trendUp ? 'up' : 'down'}} trend-icon"></i> {{stat.trend}}%
-          </div>
+    }
+  </div>
+</div>
+
+<!-- 数据管理模态框 - 修改后的完整版本 -->
+@if (showDailyDataModal) {
+<div class="daily-data-overlay" (click)="closeDailyDataModal()"></div>
+
+<div class="daily-data-modal">
+  <button class="daily-data-close" (click)="closeTrainingDataModal()">
+    <i class="fas fa-times"></i>
+  </button>
+  
+  <div class="modal-header">
+    <h3>训练数据管理</h3>
+    <button class="add-btn" (click)="prepareNewItem()">
+      <i class="fas fa-plus"></i> 新增数据
+    </button>
+  </div>
+  
+  <div class="modal-search">
+    <input type="text" placeholder="搜索..." 
+           [(ngModel)]="searchQuery"
+           (input)="searchTrainingData()">
+    <button>
+      <i class="fas fa-search"></i>
+    </button>
+  </div>
+  
+  <div class="modal-content">
+    @for (item of filteredTrainingData; track item.id) {
+    <div class="data-item">
+      <div class="data-main">
+        <div class="data-title">{{ item.get('title') || '未命名数据' }}</div>
+        <div class="data-meta">
+          <span class="data-type">
+            <i class="fas fa-{{getTypeIcon(item.get('type'))}}"></i>
+            {{ getTypeName(item.get('type')) }}
+          </span>
+          <span class="data-date">
+            <i class="far fa-calendar"></i>
+            {{ item.createdAt | date:'yyyy-MM-dd' }}
+          </span>
         </div>
       </div>
+      <div class="data-actions">
+        <button (click)="prepareEditItem(item)"><i class="fas fa-edit"></i></button>
+        <button (click)="deleteTrainingItem(item)"><i class="fas fa-trash"></i></button>
+      </div>
     </div>
+    }
+    @empty {
+      <div class="no-data">暂无数据</div>
+    }
   </div>
   
+  <div class="modal-actions">
+    <button class="cancel-btn" (click)="closeDailyDataModal()">
+      <i class="fas fa-times"></i> 关闭
+    </button>
+  </div>
+</div>
+}
+
+<!-- 数据编辑模态框 -->
+@if (showDataModal) {
+<div class="modal-overlay" (click)="showDataModal = false">
+  <div class="modal-content" (click)="$event.stopPropagation()">
+    <button class="modal-close" (click)="showDataModal = false">
+      <i class="fas fa-times"></i>
+    </button>
+    <h3>{{ isEditing ? '编辑数据' : '新增数据' }}</h3>
+    
+    <div class="form-group">
+      <label>标题</label>
+      <input type="text" [(ngModel)]="currentTrainingItem.data['title']" 
+             placeholder="输入数据标题">
+    </div>
+    
+    <div class="form-group">
+      <label for="data-type">数据类型</label>
+      <select id="data-type" [(ngModel)]="currentTrainingItem.data['type']" aria-label="选择数据类型">
+        <option value="meeting">会议记录</option>
+        <option value="contract">客户合同</option>
+        <option value="feedback">反馈表</option>
+        <option value="training">培训资料</option>
+      </select>
+    </div>
+    
+    <div class="form-group">
+      <label>数据内容</label>
+      <textarea [(ngModel)]="currentTrainingItem.data['content']" 
+                placeholder="输入数据内容"></textarea>
+    </div>
+    
+    <div class="form-group">
+      <label>备注</label>
+      <textarea [(ngModel)]="currentTrainingItem.data['notes']" 
+                placeholder="输入备注信息"></textarea>
+    </div>
+    
+    <div class="form-footer">
+      <button class="cancel-btn" (click)="showDataModal = false">
+        <i class="fas fa-times"></i> 取消
+      </button>
+      <button class="confirm-btn" (click)="saveTrainingItem()">
+        <i class="fas fa-check"></i> 保存
+      </button>
+    </div>
+  </div>
+</div>
+}
+  
   <!-- 历史数据上传记录 -->
   <div class="card">
     <div class="card-header">

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

@@ -1210,4 +1210,488 @@ label:not(.sr-only):not(.file-upload-label) {
 /* 模态框层级 */
 .modal-overlay {
   z-index: 1000;
-}
+}
+
+
+//<!-- 每日数据记录 
+/* 横屏数据查看模态框样式 */
+/* 每日数据记录卡片样式 */
+.data-record {
+  background: #fff;
+  border-radius: 12px;
+  padding: 16px;
+  margin-bottom: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+  .record-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .card-title {
+      font-size: 18px;
+      font-weight: 600;
+      color: #333;
+    }
+
+    .view-all {
+      color: #1890ff;
+      font-size: 14px;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      gap: 4px;
+
+      &:hover {
+        color: #40a9ff;
+      }
+
+      i {
+        font-size: 12px;
+      }
+    }
+  }
+
+  .record-list {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+    gap: 12px;
+
+    .record-item {
+      padding: 12px;
+      background: #f9f9f9;
+      border-radius: 8px;
+
+      .record-title {
+        display: flex;
+        align-items: center;
+        gap: 6px;
+        font-size: 14px;
+        color: #666;
+        margin-bottom: 8px;
+
+        i {
+          color: #1890ff;
+        }
+      }
+
+      .record-value {
+        font-size: 20px;
+        font-weight: 600;
+        color: #333;
+        margin-bottom: 4px;
+      }
+
+      .record-trend {
+        font-size: 13px;
+        display: flex;
+        align-items: center;
+        gap: 4px;
+
+        &.trend-up {
+          color: #52c41a;
+        }
+
+        &.trend-down {
+          color: #f5222d;
+        }
+
+        .trend-icon {
+          font-size: 12px;
+        }
+      }
+    }
+  }
+}
+
+/* 每日数据模态框样式 */
+.daily-data-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.5);
+  z-index: 1000;
+  backdrop-filter: blur(3px);
+}
+
+.daily-data-modal {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 90%;
+  max-width: 800px;
+  max-height: 80vh;
+  background-color: #fff;
+  border-radius: 12px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+  z-index: 1001;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+
+  .daily-data-close {
+    position: absolute;
+    top: 16px;
+    right: 16px;
+    width: 32px;
+    height: 32px;
+    background: rgba(0, 0, 0, 0.05);
+    border: none;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    transition: all 0.2s;
+    z-index: 1;
+
+    &:hover {
+      background: rgba(0, 0, 0, 0.1);
+    }
+
+    i {
+      color: #666;
+      font-size: 16px;
+    }
+  }
+
+  .modal-header {
+    padding: 20px 24px;
+    border-bottom: 1px solid #f0f0f0;
+
+    h3 {
+      margin: 0;
+      font-size: 18px;
+      font-weight: 600;
+      color: #333;
+    }
+  }
+
+  .modal-search {
+    padding: 16px 24px;
+    display: flex;
+    border-bottom: 1px solid #f0f0f0;
+
+    input {
+      flex: 1;
+      padding: 8px 12px;
+      border: 1px solid #d9d9d9;
+      border-radius: 6px;
+      font-size: 14px;
+      transition: all 0.3s;
+
+      &:focus {
+        outline: none;
+        border-color: #1890ff;
+        box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+      }
+    }
+
+    button {
+      margin-left: 12px;
+      padding: 0 16px;
+      background-color: #1890ff;
+      color: white;
+      border: none;
+      border-radius: 6px;
+      cursor: pointer;
+      transition: all 0.3s;
+
+      &:hover {
+        background-color: #40a9ff;
+      }
+
+      i {
+        font-size: 14px;
+      }
+    }
+  }
+
+  .modal-content {
+    flex: 1;
+    overflow-y: auto;
+    padding: 16px 0;
+
+    .modal-item {
+      padding: 16px 24px;
+      border-bottom: 1px solid #f5f5f5;
+      transition: background-color 0.2s;
+
+      &:hover {
+        background-color: #fafafa;
+      }
+
+      &:last-child {
+        border-bottom: none;
+      }
+
+      .item-date {
+        font-weight: 600;
+        margin-bottom: 12px;
+        color: #333;
+        font-size: 15px;
+      }
+
+      .item-details {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+        gap: 12px;
+
+        .detail {
+          display: flex;
+          align-items: center;
+          font-size: 14px;
+
+          .label {
+            margin-right: 6px;
+            color: #666;
+            min-width: 60px;
+          }
+
+          .value {
+            font-weight: 500;
+            margin-right: 8px;
+            color: #333;
+          }
+
+          .trend {
+            font-size: 12px;
+            padding: 2px 6px;
+            border-radius: 10px;
+            background-color: rgba(24, 144, 255, 0.1);
+
+            &.up {
+              color: #52c41a;
+              background-color: rgba(82, 196, 26, 0.1);
+            }
+
+            &.down {
+              color: #f5222d;
+              background-color: rgba(245, 34, 45, 0.1);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/* 响应式调整 */
+@media (max-width: 768px) {
+  .data-record .record-list {
+    grid-template-columns: 1fr 1fr;
+  }
+
+  .daily-data-modal {
+    width: 95%;
+    max-height: 85vh;
+
+    .modal-content .item-details {
+      grid-template-columns: 1fr;
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .data-record .record-list {
+    grid-template-columns: 1fr;
+  }
+}
+
+/* 更新关闭按钮样式 - 添加文字和图标组合 */
+.daily-data-close {
+  position: absolute;
+  top: 16px;
+  right: 16px;
+  padding: 6px 12px 6px 8px;
+  background: rgba(0, 0, 0, 0.05);
+  border: none;
+  border-radius: 16px;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+  z-index: 1;
+  font-size: 14px;
+  color: #666;
+
+  &:hover {
+    background: rgba(0, 0, 0, 0.1);
+    color: #333;
+    
+    &::after {
+      opacity: 1;
+    }
+  }
+
+  i {
+    font-size: 14px;
+    transition: transform 0.2s;
+  }
+
+  /* 添加文字标签(悬停时显示) */
+  &::after {
+    content: "关闭";
+    opacity: 0;
+    transition: opacity 0.2s;
+  }
+
+  &:hover i {
+    transform: rotate(90deg);
+  }
+}
+
+/* 添加确认按钮容器 */
+.modal-actions {
+  display: flex;
+  justify-content: flex-end;
+  padding: 16px 24px;
+  border-top: 1px solid #f0f0f0;
+  gap: 12px;
+
+  button {
+    padding: 8px 16px;
+    border-radius: 6px;
+    font-size: 14px;
+    cursor: pointer;
+    transition: all 0.2s;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+
+    &.cancel-btn {
+      background: #f5f5f5;
+      color: #666;
+      border: 1px solid #d9d9d9;
+
+      &:hover {
+        background: #eee;
+        color: #333;
+      }
+    }
+
+    &.confirm-btn {
+      background: #1890ff;
+      color: white;
+      border: none;
+
+      &:hover {
+        background: #40a9ff;
+      }
+    }
+
+    i {
+      font-size: 12px;
+    }
+  }
+}
+
+//数据
+/* 数据项样式 */
+.data-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 0;
+  border-bottom: 1px solid #f0f0f0;
+  transition: background-color 0.2s;
+
+  &:hover {
+    background-color: #f9f9f9;
+  }
+}
+
+.data-main {
+  flex: 1;
+}
+
+.data-title {
+  font-weight: 500;
+  margin-bottom: 4px;
+  color: #333;
+}
+
+.data-meta {
+  display: flex;
+  gap: 15px;
+  font-size: 12px;
+  color: #666;
+
+  i {
+    margin-right: 4px;
+  }
+}
+
+.data-actions {
+  display: flex;
+  gap: 8px;
+
+  button {
+    background: none;
+    border: none;
+    color: #999;
+    cursor: pointer;
+    font-size: 16px;
+    transition: color 0.2s;
+
+    &:first-child:hover {
+      color: #1890ff;
+    }
+
+    &:last-child:hover {
+      color: #ff4d4f;
+    }
+  }
+}
+
+.no-data {
+  text-align: center;
+  padding: 20px;
+  color: #999;
+}
+
+/* 表单样式 */
+.form-group {
+  margin-bottom: 16px;
+
+  label {
+    display: block;
+    margin-bottom: 8px;
+    font-weight: 500;
+    color: #333;
+  }
+
+  input, select {
+    width: 100%;
+    padding: 8px 12px;
+    border: 1px solid #d9d9d9;
+    border-radius: 4px;
+  }
+
+  textarea {
+    width: 100%;
+    min-height: 100px;
+    padding: 8px 12px;
+    border: 1px solid #d9d9d9;
+    border-radius: 4px;
+    resize: vertical;
+  }
+}
+
+.form-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+  margin-top: 24px;
+}
+
+//7.3
+
+
+
+//

+ 200 - 6
ai-assisant/src/modules/crm/mobile/page-crm-data/page-crm-data.ts

@@ -2,6 +2,7 @@
 import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
+import { CloudObject, CloudQuery } from '../../../../lib/cloud/ncloud';
 
 @Component({
   selector: 'app-page-crm-data',
@@ -216,7 +217,200 @@ confirmTagSelection() {
     message: '',
     success: false
   };
+
+  // 每日数据记录模态框相关
+// 每日数据模态框控制
+showDailyDataModal = false;  // 控制模态框显示
+searchQuery = '';           // 搜索关键词 (与模板中的[(ngModel)]="searchQuery"对应)
+
+// 每日详细数据 (与模板中的filteredDailyData对应)
+dailyData = [
+  {
+    date: '2023-10-01',
+    stats: [
+      { label: '新增客户', value: '24', trend: '12', trendUp: true },
+      { label: '订单量', value: '56', trend: '5', trendUp: true },
+      { label: '销售额', value: '¥12,450', trend: '8', trendUp: false },
+      { label: '转化率', value: '32%', trend: '2', trendUp: true }
+    ]
+  },
+  {
+    date: '2023-10-02',
+    stats: [
+      { label: '新增客户', value: '18', trend: '5', trendUp: false },
+      { label: '订单量', value: '42', trend: '3', trendUp: true },
+      { label: '销售额', value: '¥9,870', trend: '6', trendUp: true },
+      { label: '转化率', value: '28%', trend: '4', trendUp: false }
+    ]
+  },
+  // 可以添加更多数据...
+];
+
+// 获取过滤后的数据 (与模板中的*ngFor="let item of filteredDailyData"对应)
+get filteredDailyData() {
+  if (!this.searchQuery.trim()) {
+    return this.dailyData;
+  }
+  const query = this.searchQuery.toLowerCase();
+  return this.dailyData.filter(item => 
+    item.date.includes(query) || 
+    item.stats.some(stat => 
+      stat.label.toLowerCase().includes(query) || 
+      stat.value.toString().toLowerCase().includes(query)
+    )
+  );
+}
+
+// 打开模态框 (与模板中的(click)="openDailyDataModal()"对应)
+openDailyDataModal() {
+  this.showDailyDataModal = true;
+  this.searchQuery = ''; // 重置搜索条件
+}
+
+// 关闭模态框 (与模板中的(click)="closeDailyDataModal()"对应)
+closeDailyDataModal() {
+  this.showDailyDataModal = false;
+}
+
+// 搜索方法 (与模板中的(click)="searchDailyData()"对应)
+searchDailyData() {
+  // 搜索逻辑已经在getter中实现,这里可以添加额外逻辑
+  console.log('搜索关键词:', this.searchQuery);
+}
+
+
+// 在组件类中添加
+confirmDailyData() {
+  // 这里可以添加确认逻辑
+  console.log('数据已确认');
+  this.closeDailyDataModal();
+  
+  // 示例:显示确认提示
+  this.uploadStatus = {
+    visible: true,
+    success: true,
+    message: '每日数据已确认提交!',
+    progress: 100
+  };
   
+  setTimeout(() => {
+    this.uploadStatus.visible = false;
+  }, 3000);
+}
+
+//数据库
+// 在组件类中添加这些方法
+// 数据相关属性
+allTrainingData: any[] = [];
+filteredTrainingData: any[] = [];
+currentTrainingItem = new CloudObject('TrainingData');
+
+// 模态框控制
+
+showDataModal = false;
+isEditing = false;
+
+// 仪表板数据
+dashboardStats = [
+  { label: '新增数据', value: 42, trend: 12, trendUp: true, icon: 'file-medical' },
+  { label: '处理中', value: 18, trend: 5, trendUp: false, icon: 'cogs' },
+  { label: '训练时间', value: '1.8h', trend: 23, trendUp: true, icon: 'clock' },
+  { label: '完成率', value: '76%', trend: 8, trendUp: true, icon: 'check-circle' }
+];
+
+constructor() {
+  this.loadTrainingData();
+}
+
+// 加载训练数据
+async loadTrainingData() {
+  const query = new CloudQuery('TrainingData');
+  this.allTrainingData = await query.find();
+  this.filteredTrainingData = [...this.allTrainingData];
+}
+
+// 打开训练数据模态框
+openTrainingDataModal() {
+  this.showDailyDataModal = true;
+  this.loadTrainingData();
+}
+
+// 关闭训练数据模态框
+closeTrainingDataModal() {
+  this.showDailyDataModal = false;
+  this.searchQuery = '';
+  this.filteredTrainingData = [...this.allTrainingData];
+}
+
+// 搜索训练数据
+searchTrainingData() {
+  if (!this.searchQuery.trim()) {
+    this.filteredTrainingData = [...this.allTrainingData];
+    return;
+  }
+  
+  const query = this.searchQuery.toLowerCase();
+  this.filteredTrainingData = this.allTrainingData.filter(item => 
+    (item.get('title') || '').toLowerCase().includes(query) || 
+    (item.get('type') || '').toLowerCase().includes(query)
+  );
+}
+
+// 准备新增项目
+prepareNewItem() {
+  this.currentTrainingItem = new CloudObject('TrainingData');
+  this.isEditing = false;
+  this.showDataModal = true;
+}
+
+// 准备编辑项目
+prepareEditItem(item: any) {
+  this.currentTrainingItem = new CloudObject('TrainingData');
+  this.currentTrainingItem.id = item.id;
+  this.currentTrainingItem.set(item);
+  this.isEditing = true;
+  this.showDataModal = true;
+}
+
+// 保存训练数据
+async saveTrainingItem() {
+  await this.currentTrainingItem.save();
+  await this.loadTrainingData();
+  this.showDataModal = false;
+}
+
+// 删除训练数据
+async deleteTrainingItem(item: any) {
+  const obj = new CloudObject('TrainingData');
+  obj.id = item.id;
+  await obj.destroy();
+  await this.loadTrainingData();
+}
+
+// 获取类型名称
+getTypeName(type: string): string {
+  const typeMap: Record<string, string> = {
+    'meeting': '会议记录',
+    'contract': '客户合同',
+    'feedback': '反馈表',
+    'training': '培训资料'
+  };
+  return typeMap[type] || '未知类型';
+}
+
+// 获取类型图标
+getTypeIcon(type: string): string {
+  const iconMap: Record<string, string> = {
+    'meeting': 'calendar-alt',
+    'contract': 'file-signature',
+    'feedback': 'comment-dots',
+    'training': 'chalkboard-teacher'
+  };
+  return iconMap[type] || 'file-alt';
+}
+
+
+//
   // 历史记录
   historyItems = [
     { name: 'Q3销售报告.pdf', type: 'pdf', size: 4.2, date: '2023-10-15' },
@@ -237,12 +431,12 @@ confirmTagSelection() {
   ];
   
   // 数据看板
-  dashboardStats = [
-    { label: '新增数据', value: 42, trend: 12, trendUp: true, icon: 'file-medical' },
-    { label: '处理中', value: 18, trend: 5, trendUp: false, icon: 'cogs' },
-    { label: '训练时间', value: '1.8h', trend: 23, trendUp: true, icon: 'clock' },
-    { label: '完成率', value: '76%', trend: 8, trendUp: true, icon: 'check-circle' }
-  ];
+  // dashboardStats:any = [
+  //   { label: '新增数据', value: 42, trend: 12, trendUp: true, icon: 'file-medical' },
+  //   { label: '处理中', value: 18, trend: 5, trendUp: false, icon: 'cogs' },
+  //   { label: '训练时间', value: '1.8h', trend: 23, trendUp: true, icon: 'clock' },
+  //   { label: '完成率', value: '76%', trend: 8, trendUp: true, icon: 'check-circle' }
+  // ];
   
   // 选择标签
   selectTag(tag: any) {