fmode-ng 0.0.230 → 0.0.232

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/chat/chat-modal-input/modal-input.component.mjs
7
7
  */
8
- import{Component,Input,ViewChild}from"@angular/core";import{Router,RouterModule}from"@angular/router";import{AlertController,ToastController}from"@ionic/angular";import{FmodeChat}from"../../service-fmai/service-chat";import{ChatService}from"../../service-fmai/service-chat";import{FmodeParse,FmodeObject}from"../../../core/parse";import{ImagineService}from"../../service-fmai/service-imagine/imagine.service";import{IonButton,IonContent,IonIcon,IonInput,IonItem,IonList,IonModal,IonPopover,IonTextarea,IonToolbar,ModalController}from"@ionic/angular/standalone";import{CommonModule}from"@angular/common";import{FormsModule,ReactiveFormsModule}from"@angular/forms";import{ModalAudioMessageComponent}from"./modal-audio-message/modal-audio-message.component";import{FmChatMessageCard}from"../chat-message-card/comp-message-card.component";import{addIcons}from"ionicons";import{imageOutline,chevronBackOutline,ellipsisHorizontalOutline,chevronDownOutline,chatboxEllipsesOutline,micOutline,paperPlaneOutline,shareSocialOutline,settingsOutline,alertOutline,colorWandOutline,peopleOutline}from"ionicons/icons";import{AccountService}from"../../../user/account/account.service";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular";import*as i2 from"@ionic/angular/standalone";import*as i3 from"@angular/router";import*as i4 from"../../service-fmai/service-imagine/imagine.service";import*as i5 from"../../service-fmai/service-chat";import*as i6 from"../../../user/account/account.service";import*as i7 from"@angular/common";import*as i8 from"@angular/forms";const Parse=FmodeParse.with("nova");addIcons({colorWandOutline:colorWandOutline,peopleOutline:peopleOutline,alertOutline:alertOutline,imageOutline:imageOutline,chevronBackOutline:chevronBackOutline,ellipsisHorizontalOutline:ellipsisHorizontalOutline,chevronDownOutline:chevronDownOutline,chatboxEllipsesOutline:chatboxEllipsesOutline,micOutline:micOutline,paperPlaneOutline:paperPlaneOutline,shareSocialOutline:shareSocialOutline,settingsOutline:settingsOutline});export class FmChatModalInput{closeAudio(){this.audioComp?.cancel(),this.isAudioModal=!1}async startTalk(){if(this.isSending)return!1;let e,n=document.body.clientHeight||960;this.audioModalHeightPoint=Number((165/n).toFixed(2)),this.chat.stopPlayingVoice(),e=await this.modalCtrl.create({component:ModalAudioMessageComponent,componentProps:{chat:this.chat,modal:e,onBreakPointSet:()=>{e?.setCurrentBreakpoint(this.audioModalHeightPoint)}},breakpoints:[this.audioModalHeightPoint],initialBreakpoint:this.audioModalHeightPoint}),e.present()}constructor(e,n,t,o,i,a,s){this.toastCtrl=e,this.alertCtrl=n,this.modalCtrl=t,this.router=o,this.imagineServ=i,this.chatServ=a,this.account=s,this.errorText="",this.isAudioModal=!1,this.audioModalHeightPoint=.35,this.isSending=!1,this.lastMessageTimestamp=0,this.replyTimeout=15e3,this.isShare=!1,this.user=Parse.User.current()}ngOnInit(){this.loadModel();let e=this;this.chat.focusUserInput=()=>{e.chat.isVoiceInputMode=!1,e.userInputComp?.setFocus()}}async loadModel(){let e=this.chat?.role?.get("model");await this.chat.loadModelList(e)}async setMessageImage(){let e=await this.imagineServ.getimg();this.chat.userImage=e,console.log(this.chat?.userImage)}onInputFocus(){this.chat.isTexting=!0,this.chat.scrollToBottom&&this.chat.scrollToBottom()}onKeyDown(e){e.ctrlKey&&"Enter"===e.key&&(console.log("Ctrl+Enter 被按下"),this.sendMessage())}async sendMessage(){if(this.isSending)return!1;const e=Date.now();if(this.lastMessageTimestamp>0&&e-this.lastMessageTimestamp<this.replyTimeout){return this.errorText="请等待上一条消息的回复或稍后再试",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning",duration:1e3})).present(),!1}if(this.isSending=!0,this.lastMessageTimestamp=Date.now(),!await this.checkBalance())return this.isSending=!1,!1;if(!this.chat.userInput){return this.errorText="内容不能为空",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning-circle",duration:1e3})).present(),void(this.isSending=!1)}this.lastMessageTimeout&&clearTimeout(this.lastMessageTimeout),this.lastMessageTimeout=setTimeout((()=>{this.isSending=!1,this.lastMessageTimestamp=0}),this.replyTimeout),this.chat?.sendMessage(this.chat?.userInput,this.chat?.userImage,(e=>{}),{onMessageStart:e=>{clearTimeout(this.lastMessageTimeout),this.isSending=!1,this.lastMessageTimestamp=0},onSSMLComplete:e=>{console.log(e)}}),this.chat.userInput="",this.chat.userImage=""}async checkBalance(){let e=await this.account.getBilling();if(e?.credit?.balance>=10&&(this.chat.isDirect=!0),!this.chat?.currentModel?.get?.("payLimit"))return!0;if(e?.credit?.balance<10){return(await this.alertCtrl.create({header:"注意",subHeader:"您的余额不足,请充值后解锁高级模型",buttons:[{role:"cancel",text:"取消"},{role:"destructive",text:"充值",handler:()=>{this.router.navigateByUrl("/account/billing")}}]})).present(),!1}return!0}async getChatShare(){this.user=Parse.User.current();let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("session",this.chat?.sessionId);await e.first()}async toggleChatShare(){let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("role",this.chat?.role.id),e.equalTo("session",this.chat?.sessionId),e.select("objectId");let n=await e.first();if(n?.id)n.set("messageList",this.chat?.messageList);else{n=new(Parse.Object.extend("ChatShare")),n.set("user",{__type:"Pointer",className:"_User",objectId:Parse.User.current()?.id}),n.set("session",{__type:"Pointer",className:"ChatSession",objectId:this.chat?.sessionId}),n.set("role",{__type:"Pointer",className:"AvatarRole",objectId:this.chat?.role.id}),n.set("company",{__type:"Pointer",className:"Company",objectId:"E4KpGvTEto"}),n.set("messageList",this.chat?.messageList)}await n.save(),this.getChatShare()}async chatShareSuccessMessage(){(await this.toastCtrl.create({duration:1e3,message:"分享成功",color:"primary",icon:"information-circle",position:"top"})).present()}showShare(){this.isShare=!0}handleOkShare(){this.toggleChatShare(),this.chatShareSuccessMessage(),this.isShare=!1}handleCancelShare(){this.isShare=!1}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,deps:[{token:i1.ToastController},{token:i1.AlertController},{token:i2.ModalController},{token:i3.Router},{token:i4.ImagineService},{token:i5.ChatService},{token:i6.AccountService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"17.3.12",type:FmChatModalInput,isStandalone:!0,selector:"fm-chat-modal-input",inputs:{chat:"chat",message:"message",role:"role"},viewQueries:[{propertyName:"audioComp",first:!0,predicate:ModalAudioMessageComponent,descendants:!0},{propertyName:"userInputComp",first:!0,predicate:["userInput"],descendants:!0}],ngImport:i0,template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button?.show&&button?.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get?.(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get?.("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n'],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i7.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:i7.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:i7.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"pipe",type:i7.DatePipe,name:"date"},{kind:"ngmodule",type:FormsModule},{kind:"directive",type:i8.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i8.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]},{kind:"ngmodule",type:ReactiveFormsModule},{kind:"ngmodule",type:RouterModule},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonItem,selector:"ion-item",inputs:["button","color","detail","detailIcon","disabled","download","href","lines","mode","rel","routerAnimation","routerDirection","target","type"]},{kind:"component",type:IonButton,selector:"ion-button",inputs:["buttonType","color","disabled","download","expand","fill","form","href","mode","rel","routerAnimation","routerDirection","shape","size","strong","target","type"]},{kind:"component",type:IonList,selector:"ion-list",inputs:["inset","lines","mode"]},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonTextarea,selector:"ion-textarea",inputs:["autoGrow","autocapitalize","autofocus","clearOnEdit","color","cols","counter","counterFormatter","debounce","disabled","enterkeyhint","errorText","fill","helperText","inputmode","label","labelPlacement","maxlength","minlength","mode","name","placeholder","readonly","required","rows","shape","spellcheck","value","wrap"]},{kind:"component",type:IonPopover,selector:"ion-popover"},{kind:"component",type:IonContent,selector:"ion-content",inputs:["color","fixedSlotPlacement","forceOverscroll","fullscreen","scrollEvents","scrollX","scrollY"]},{kind:"component",type:FmChatMessageCard,selector:"fm-chat-message-card",inputs:["index","message","role","chat"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,decorators:[{type:Component,args:[{selector:"fm-chat-modal-input",standalone:!0,imports:[CommonModule,FormsModule,ReactiveFormsModule,RouterModule,IonToolbar,IonItem,IonButton,IonList,IonModal,IonInput,IonIcon,IonTextarea,IonPopover,IonContent,FmChatMessageCard,ModalAudioMessageComponent],template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button?.show&&button?.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get?.(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get?.("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n']}]}],ctorParameters:()=>[{type:i1.ToastController},{type:i1.AlertController},{type:i2.ModalController},{type:i3.Router},{type:i4.ImagineService},{type:i5.ChatService},{type:i6.AccountService}],propDecorators:{audioComp:[{type:ViewChild,args:[ModalAudioMessageComponent]}],userInputComp:[{type:ViewChild,args:["userInput"]}],chat:[{type:Input}],message:[{type:Input}],role:[{type:Input}]}});
8
+ import{Component,Input,ViewChild}from"@angular/core";import{Router,RouterModule}from"@angular/router";import{AlertController,ToastController}from"@ionic/angular";import{FmodeChat}from"../../service-fmai/service-chat";import{ChatService}from"../../service-fmai/service-chat";import{FmodeParse,FmodeObject}from"../../../core/parse";import{ImagineService}from"../../service-fmai/service-imagine/imagine.service";import{IonButton,IonContent,IonIcon,IonInput,IonItem,IonList,IonModal,IonPopover,IonTextarea,IonToolbar,ModalController}from"@ionic/angular/standalone";import{CommonModule}from"@angular/common";import{FormsModule,ReactiveFormsModule}from"@angular/forms";import{ModalAudioMessageComponent}from"./modal-audio-message/modal-audio-message.component";import{FmChatMessageCard}from"../chat-message-card/comp-message-card.component";import{addIcons}from"ionicons";import{imageOutline,chevronBackOutline,ellipsisHorizontalOutline,chevronDownOutline,chatboxEllipsesOutline,micOutline,paperPlaneOutline,shareSocialOutline,settingsOutline,alertOutline,colorWandOutline,peopleOutline}from"ionicons/icons";import{AccountService}from"../../../user/account/account.service";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular";import*as i2 from"@ionic/angular/standalone";import*as i3 from"@angular/router";import*as i4 from"../../service-fmai/service-imagine/imagine.service";import*as i5 from"../../service-fmai/service-chat";import*as i6 from"../../../user/account/account.service";import*as i7 from"@angular/common";import*as i8 from"@angular/forms";const Parse=FmodeParse.with("nova");addIcons({colorWandOutline:colorWandOutline,peopleOutline:peopleOutline,alertOutline:alertOutline,imageOutline:imageOutline,chevronBackOutline:chevronBackOutline,ellipsisHorizontalOutline:ellipsisHorizontalOutline,chevronDownOutline:chevronDownOutline,chatboxEllipsesOutline:chatboxEllipsesOutline,micOutline:micOutline,paperPlaneOutline:paperPlaneOutline,shareSocialOutline:shareSocialOutline,settingsOutline:settingsOutline});export class FmChatModalInput{closeAudio(){this.audioComp?.cancel(),this.isAudioModal=!1}async startTalk(){if(this.isSending)return!1;let e,n=document.body.clientHeight||960;this.audioModalHeightPoint=Number((165/n).toFixed(2)),this.chat.stopPlayingVoice(),e=await this.modalCtrl.create({component:ModalAudioMessageComponent,componentProps:{chat:this.chat,modal:e,onBreakPointSet:()=>{e?.setCurrentBreakpoint(this.audioModalHeightPoint)}},breakpoints:[this.audioModalHeightPoint],initialBreakpoint:this.audioModalHeightPoint}),e.present()}constructor(e,n,t,o,i,a,s){this.toastCtrl=e,this.alertCtrl=n,this.modalCtrl=t,this.router=o,this.imagineServ=i,this.chatServ=a,this.account=s,this.errorText="",this.isAudioModal=!1,this.audioModalHeightPoint=.35,this.isSending=!1,this.lastMessageTimestamp=0,this.replyTimeout=15e3,this.isShare=!1,this.user=Parse.User.current()}ngOnInit(){this.loadModel();let e=this;this.chat.focusUserInput=()=>{e.chat.isVoiceInputMode=!1,e.userInputComp?.setFocus()}}async loadModel(){let e=this.chat?.role?.get("model");await this.chat.loadModelList(e)}async setMessageImage(){let e=await this.imagineServ.getimg();this.chat.userImage=e,console.log(this.chat?.userImage)}onInputFocus(){this.chat.isTexting=!0,this.chat.scrollToBottom&&this.chat.scrollToBottom()}onKeyDown(e){e.ctrlKey&&"Enter"===e.key&&(console.log("Ctrl+Enter 被按下"),this.sendMessage())}async sendMessage(){if(this.isSending)return!1;const e=Date.now();if(this.lastMessageTimestamp>0&&e-this.lastMessageTimestamp<this.replyTimeout){return this.errorText="请等待上一条消息的回复或稍后再试",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning",duration:1e3})).present(),!1}if(this.isSending=!0,this.lastMessageTimestamp=Date.now(),!await this.checkBalance())return this.isSending=!1,!1;if(!this.chat.userInput){return this.errorText="内容不能为空",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning-circle",duration:1e3})).present(),void(this.isSending=!1)}this.lastMessageTimeout&&clearTimeout(this.lastMessageTimeout),this.lastMessageTimeout=setTimeout((()=>{this.isSending=!1,this.lastMessageTimestamp=0}),this.replyTimeout),this.chat?.sendMessage(this.chat?.userInput,this.chat?.userImage,(e=>{}),{onMessageStart:e=>{clearTimeout(this.lastMessageTimeout),this.isSending=!1,this.lastMessageTimestamp=0},onSSMLComplete:e=>{console.log(e)}}),this.chat.userInput="",this.chat.userImage=""}async checkBalance(){let e=await this.account.getBilling();if(e?.credit?.balance>=3&&(this.chat.isDirect=!0),!this.chat?.currentModel?.get?.("payLimit"))return!0;if(e?.credit?.balance<3){return(await this.alertCtrl.create({header:"注意",subHeader:"您的余额不足,请充值后解锁高级模型",buttons:[{role:"cancel",text:"取消"},{role:"destructive",text:"充值",handler:()=>{this.router.navigateByUrl("/account/billing")}}]})).present(),!1}return!0}async getChatShare(){this.user=Parse.User.current();let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("session",this.chat?.sessionId);await e.first()}async toggleChatShare(){let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("role",this.chat?.role.id),e.equalTo("session",this.chat?.sessionId),e.select("objectId");let n=await e.first();if(n?.id)n.set("messageList",this.chat?.messageList);else{n=new(Parse.Object.extend("ChatShare")),n.set("user",{__type:"Pointer",className:"_User",objectId:Parse.User.current()?.id}),n.set("session",{__type:"Pointer",className:"ChatSession",objectId:this.chat?.sessionId}),n.set("role",{__type:"Pointer",className:"AvatarRole",objectId:this.chat?.role.id}),n.set("company",{__type:"Pointer",className:"Company",objectId:"E4KpGvTEto"}),n.set("messageList",this.chat?.messageList)}await n.save(),this.getChatShare()}async chatShareSuccessMessage(){(await this.toastCtrl.create({duration:1e3,message:"分享成功",color:"primary",icon:"information-circle",position:"top"})).present()}showShare(){this.isShare=!0}handleOkShare(){this.toggleChatShare(),this.chatShareSuccessMessage(),this.isShare=!1}handleCancelShare(){this.isShare=!1}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,deps:[{token:i1.ToastController},{token:i1.AlertController},{token:i2.ModalController},{token:i3.Router},{token:i4.ImagineService},{token:i5.ChatService},{token:i6.AccountService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"17.3.12",type:FmChatModalInput,isStandalone:!0,selector:"fm-chat-modal-input",inputs:{chat:"chat",message:"message",role:"role"},viewQueries:[{propertyName:"audioComp",first:!0,predicate:ModalAudioMessageComponent,descendants:!0},{propertyName:"userInputComp",first:!0,predicate:["userInput"],descendants:!0}],ngImport:i0,template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button?.show&&button?.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get?.(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get?.("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n'],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i7.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:i7.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:i7.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"pipe",type:i7.DatePipe,name:"date"},{kind:"ngmodule",type:FormsModule},{kind:"directive",type:i8.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i8.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]},{kind:"ngmodule",type:ReactiveFormsModule},{kind:"ngmodule",type:RouterModule},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonItem,selector:"ion-item",inputs:["button","color","detail","detailIcon","disabled","download","href","lines","mode","rel","routerAnimation","routerDirection","target","type"]},{kind:"component",type:IonButton,selector:"ion-button",inputs:["buttonType","color","disabled","download","expand","fill","form","href","mode","rel","routerAnimation","routerDirection","shape","size","strong","target","type"]},{kind:"component",type:IonList,selector:"ion-list",inputs:["inset","lines","mode"]},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonTextarea,selector:"ion-textarea",inputs:["autoGrow","autocapitalize","autofocus","clearOnEdit","color","cols","counter","counterFormatter","debounce","disabled","enterkeyhint","errorText","fill","helperText","inputmode","label","labelPlacement","maxlength","minlength","mode","name","placeholder","readonly","required","rows","shape","spellcheck","value","wrap"]},{kind:"component",type:IonPopover,selector:"ion-popover"},{kind:"component",type:IonContent,selector:"ion-content",inputs:["color","fixedSlotPlacement","forceOverscroll","fullscreen","scrollEvents","scrollX","scrollY"]},{kind:"component",type:FmChatMessageCard,selector:"fm-chat-message-card",inputs:["index","message","role","chat"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,decorators:[{type:Component,args:[{selector:"fm-chat-modal-input",standalone:!0,imports:[CommonModule,FormsModule,ReactiveFormsModule,RouterModule,IonToolbar,IonItem,IonButton,IonList,IonModal,IonInput,IonIcon,IonTextarea,IonPopover,IonContent,FmChatMessageCard,ModalAudioMessageComponent],template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button?.show&&button?.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get?.(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get?.("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n']}]}],ctorParameters:()=>[{type:i1.ToastController},{type:i1.AlertController},{type:i2.ModalController},{type:i3.Router},{type:i4.ImagineService},{type:i5.ChatService},{type:i6.AccountService}],propDecorators:{audioComp:[{type:ViewChild,args:[ModalAudioMessageComponent]}],userInputComp:[{type:ViewChild,args:["userInput"]}],chat:[{type:Input}],message:[{type:Input}],role:[{type:Input}]}});
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2NoYXQvY2hhdC1tb2RhbC1pbnB1dC9tb2RhbC1pbnB1dC5jb21wb25lbnQubWpz`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/parse/fmode.cloud.mjs
7
7
  */
8
- export class FmodeCloud{static{this._instance=null}constructor(){this._config=null,this._cloudFunctions=new Map}static getInstance(){return this._instance||(this._instance=new FmodeCloud),this._instance}bindInstance(e){this._config=e}define(e,t){this._cloudFunctions.set(e,t)}async run(e,t={},n={}){if(!this._config)throw new Error("FmodeCloud not initialized. Call bindInstance first.");const i=`${this._config.serverURL}/functions/${e}`,o={...t,_ApplicationId:this._config.appId,_InstallationId:n.installationId||this.generateInstallationId()},s=this._config.sessionToken;s&&(o._SessionToken=s);try{const e=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-Parse-Application-Id":this._config.appId,...this._config.javascriptKey&&{"X-Parse-JavaScript-Key":this._config.javascriptKey},...this._config.masterKey&&{"X-Parse-Master-Key":this._config.masterKey},...s&&{"X-Parse-Session-Token":s}},body:JSON.stringify(o)});if(!e.ok)throw new Error(`HTTP error! status: ${e.status}`);const t=await e.json();if(t.error)throw new Error(t.error.message||"Cloud function error");return t.result}catch(t){throw console.error(`Cloud function ${e} failed:`,t),t}}async afterDelete(e,t){this.define(`afterDelete_${e}`,t)}async afterSave(e,t){this.define(`afterSave_${e}`,t)}async beforeDelete(e,t){this.define(`beforeDelete_${e}`,t)}async beforeSave(e,t){this.define(`beforeSave_${e}`,t)}async beforeFind(e,t){this.define(`beforeFind_${e}`,t)}async afterFind(e,t){this.define(`afterFind_${e}`,t)}async job(e,t){this.define(`job_${e}`,t)}generateInstallationId(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)}))}}export const Cloud=FmodeCloud.getInstance();export default Cloud;
8
+ export class FmodeCloud{static{this._singletonInstance=null}constructor(){this._cloudFunctions=new Map}static getInstance(){return this._singletonInstance||(this._singletonInstance=new FmodeCloud),this._singletonInstance}static{this._instance=null}static bindInstance(e){this._instance=e}get currentConfig(){const e=FmodeCloud._instance;if(!e)throw new Error("FmodeCloud not initialized. Call FmodeParse.initialize() first.");return e.config}static get instance(){return this._instance}define(e,t){this._cloudFunctions.set(e,t)}async run(e,t={},n={}){const s=this.currentConfig,o=`${s.serverURL}/functions/${e}`,i={...t,_ApplicationId:s.appId,_InstallationId:n.installationId||this.generateInstallationId()},r=s.sessionToken;r&&(i._SessionToken=r);try{const e=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json","X-Parse-Application-Id":s.appId,...s.javascriptKey&&{"X-Parse-JavaScript-Key":s.javascriptKey},...s.masterKey&&{"X-Parse-Master-Key":s.masterKey},...r&&{"X-Parse-Session-Token":r}},body:JSON.stringify(i)});if(!e.ok)throw new Error(`HTTP error! status: ${e.status}`);const t=await e.json();if(t.error)throw new Error(t.error.message||"Cloud function error");return t.result}catch(t){throw console.error(`Cloud function ${e} failed:`,t),t}}async afterDelete(e,t){this.define(`afterDelete_${e}`,t)}async afterSave(e,t){this.define(`afterSave_${e}`,t)}async beforeDelete(e,t){this.define(`beforeDelete_${e}`,t)}async beforeSave(e,t){this.define(`beforeSave_${e}`,t)}async beforeFind(e,t){this.define(`beforeFind_${e}`,t)}async afterFind(e,t){this.define(`afterFind_${e}`,t)}async job(e,t){this.define(`job_${e}`,t)}generateInstallationId(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)}))}}export const Cloud=FmodeCloud.getInstance();export default Cloud;
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL3BhcnNlL2Ztb2RlLmNsb3VkLm1qcw==`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/parse/fmode.parse.mjs
7
7
  */
8
- import{FmodeACL}from"./datatype/acl";import{FmodeFile}from"./datatype/file";import{FmodeGeoPoint}from"./datatype/geopoint";import{FmodeRelation}from"./datatype/relation";import{FmodeObject}from"./fmode.object";import{FmodeQuery}from"./fmode.query";import{FmodeUser}from"./fmode.user";import{FmodeCloud}from"./fmode.cloud";class FmodeParse{static{this._instances={}}static{this._defaultInstance=null}constructor(e){this.config=e,this.Object=this.createObjectFactory(),this.Query=this.createQueryFactory(),this.User=this.createUserFactory(),this.GeoPoint=FmodeGeoPoint,this.File=FmodeFile,this.ACL=FmodeACL,this.Relation=FmodeRelation,this.Cloud=FmodeCloud.getInstance(),FmodeObject._instance||FmodeObject.bindInstance(this),FmodeQuery._instance||FmodeQuery.bindInstance(this),FmodeUser._instance||FmodeUser.bindInstance(this),FmodeGeoPoint._instance||FmodeGeoPoint.bindInstance(this),FmodeRelation._instance||FmodeRelation.bindInstance(this),FmodeFile._instance||FmodeFile.bindInstance(this),FmodeACL._instance||FmodeACL.bindInstance(this),this.Cloud.bindInstance(this.config)}createObjectFactory(){const e=this;return class Object extends FmodeObject{constructor(t){super(t),this._parseInstance=e}static extend(e){const t=class extends Object{constructor(){super(e)}};return t.extend=Object.extend,t.create=Object.create,t.fetchAll=Object.fetchAll,t.fromFile=Object.fromFile,t}static async create(e,t,s){const n=new this(e);return n.set(t),n.save(s)}static async fetchAll(t,s){if(!e)throw new Error("Not initialized");if(0===t.length)return[];const n={"Content-Type":"application/json","X-Parse-Application-Id":e.appId};if(s?.useMasterKey&&e.config.masterKey)n["X-Parse-Master-Key"]=e.config.masterKey;else{const t=e.sessionToken;t&&(n["X-Parse-Session-Token"]=t)}const r=`${e.serverURL}/batch`,o=t.map((e=>({method:"GET",path:`/classes/${e.className}/${e.id}`}))),i=await fetch(r,{method:"POST",headers:n,body:JSON.stringify({requests:o})}),a=await i.json();if(a.error)throw new Error(a.error);return a.map(((e,s)=>{const n=t[s];return n.set(e.success),n}))}static async fromFile(t,s,n){if(!e)throw new Error("Not initialized");const r=new FormData;r.append("file",s,t);const o={"X-Parse-Application-Id":e.appId},i=e.sessionToken;i&&(o["X-Parse-Session-Token"]=i);const a=`${e.serverURL}/files/${t}`,c=await fetch(a,{method:"POST",headers:o,body:r}),d=await c.json();if(d.error)throw new Error(d.error);return{__type:"File",name:d.name,url:d.url}}}}createQueryFactory(){const e=this;return class Query extends FmodeQuery{constructor(t){super(t),this._parseInstance=e}static get instance(){return e}static bindInstance(e){}}}createUserFactory(){const e=this;return class User extends FmodeUser{constructor(t){super(t),this._parseInstance=e}static get instance(){return e}static{this.instanceKey=()=>"Parse/"+e.appId+"/currentUser"}static bindInstance(e){}static async logIn(t,s){const n=`${e.serverURL}/login`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","X-Parse-Application-Id":e.appId},body:JSON.stringify({username:t,password:s})}),o=await r.json();if(o.error)throw o;const i=new this({...o,username:t,password:s});return i._saveToCache(),i}static async become(t){const s=`${e.serverURL}/users/me`;let n={"X-Parse-Session-Token":t,"X-Parse-Application-Id":e.appId};const r=await fetch(s,{headers:n}),o=await r.json();if(o.error)throw o.error;const i=new this({...o,sessionToken:t});return i._saveToCache(),i}static async signUp(e,t,s={}){return new this({...s,username:e,password:t}).signUp()}static async logOut(){if(this.current())try{await fetch(`${e.serverURL}/logout`,{method:"POST",headers:{"X-Parse-Session-Token":this.current().sessionToken,"X-Parse-Application-Id":e.appId}})}finally{this._clearCurrentUser()}}static async requestPasswordReset(t){const s=`${e.serverURL}/requestPasswordReset`,n=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json","X-Parse-Application-Id":e.appId},body:JSON.stringify({email:t})}),r=await n.json();if(r.error)throw r}static{this._currentUser=null}static current(){if(this._currentUser)return this._currentUser;try{const t=localStorage.getItem(this.instanceKey());if(!t)return null;const s=JSON.parse(t);if(!s.sessionToken)return null;const n=new this(s);return this._currentUser=n,n.sessionToken&&e&&(e.sessionToken=n.sessionToken),n}catch(e){return console.warn("Failed to restore user from cache",e),null}}static _clearCurrentUser(){this._currentUser=null,e&&(e.sessionToken=void 0),localStorage.removeItem(this.instanceKey())}_saveToCache(){if(!this.sessionToken)return;this.constructor._currentUser=this;const e=this._parseInstance||this.constructor.instance;e&&(e.sessionToken=this.sessionToken),localStorage.setItem(this.constructor.instanceKey(),JSON.stringify(this.toJSONForCache()))}}}static initialize(e){let t;if("string"==typeof e){let s="http://dev.fmode.cn:1337/parse";"undefined"!=typeof location&&"https:"===location.protocol&&(s="https://dev.fmode.cn/parse"),t=new FmodeParse({appId:e,serverURL:s}),this._defaultInstance=t,this._instances.default=t}else t=new FmodeParse(e),e.config&&(this._instances[e.config]=t),e.config&&"default"!==e.config||(this._defaultInstance=t);return t}static with(e){if(!this._instances.nova){let e=new FmodeParse({appId:"ncloudmaster",serverURL:"https://server.fmode.cn/parse"});this._instances.nova=e}if(!this._instances.dev){let e=new FmodeParse({appId:"dev",serverURL:"https://dev.fmode.cn/parse"});this._instances.dev=e}return"dev"!=e||this._defaultInstance||(this._defaultInstance=this._instances.dev),"default"!=e||this._instances.default||(this._instances.default=this._instances.dev,this._defaultInstance=this._instances.dev),this._instances[e]||this._defaultInstance}static getAllInstances(){return{...this._instances}}static getInstanceConfigs(){const e={};return Object.entries(this._instances).forEach((([t,s])=>{e[t]=s.config})),e}get serverURL(){return this.config.serverURL}set serverURL(e){this.config.serverURL=e}get appId(){return this.config.appId}get sessionToken(){if(this.config.sessionToken)return this.config.sessionToken;const e=this.User.current();return e?.sessionToken}set sessionToken(e){this.config.sessionToken=e}}!function(e){e.Object=FmodeObject,e.Query=FmodeQuery,e.User=FmodeUser,e.File=FmodeFile,e.Relation=FmodeRelation,e.ACL=FmodeACL,e.GeoPoint=FmodeGeoPoint,e.Cloud=FmodeCloud.getInstance()}(FmodeParse||(FmodeParse={}));const defaultExport=new Proxy(FmodeParse,{apply:(e,t,s)=>FmodeParse.initialize.apply(e,s),get:(e,t)=>t in e?e[t]:t in FmodeParse?FmodeParse[t]:FmodeParse.with("default")[t]});export{FmodeParse as default,defaultExport as FmodeParse};
8
+ import{FmodeACL}from"./datatype/acl";import{FmodeFile}from"./datatype/file";import{FmodeGeoPoint}from"./datatype/geopoint";import{FmodeRelation}from"./datatype/relation";import{FmodeObject}from"./fmode.object";import{FmodeQuery}from"./fmode.query";import{FmodeUser}from"./fmode.user";import{FmodeCloud}from"./fmode.cloud";class FmodeParse{static{this._instances={}}static{this._defaultInstance=null}constructor(e){this.config=e,this.Object=this.createObjectFactory(),this.Query=this.createQueryFactory(),this.User=this.createUserFactory(),this.GeoPoint=FmodeGeoPoint,this.File=FmodeFile,this.ACL=FmodeACL,this.Relation=FmodeRelation,this.Cloud=FmodeCloud.getInstance(),FmodeObject._instance||FmodeObject.bindInstance(this),FmodeQuery._instance||FmodeQuery.bindInstance(this),FmodeUser._instance||FmodeUser.bindInstance(this),FmodeGeoPoint._instance||FmodeGeoPoint.bindInstance(this),FmodeRelation._instance||FmodeRelation.bindInstance(this),FmodeFile._instance||FmodeFile.bindInstance(this),FmodeACL._instance||FmodeACL.bindInstance(this),FmodeCloud._instance||FmodeCloud.bindInstance(this)}createObjectFactory(){const e=this;return class Object extends FmodeObject{constructor(t){super(t),this._parseInstance=e}static extend(e){const t=class extends Object{constructor(){super(e)}};return t.extend=Object.extend,t.create=Object.create,t.fetchAll=Object.fetchAll,t.fromFile=Object.fromFile,t}static async create(e,t,s){const n=new this(e);return n.set(t),n.save(s)}static async fetchAll(t,s){if(!e)throw new Error("Not initialized");if(0===t.length)return[];const n={"Content-Type":"application/json","X-Parse-Application-Id":e.appId};if(s?.useMasterKey&&e.config.masterKey)n["X-Parse-Master-Key"]=e.config.masterKey;else{const t=e.sessionToken;t&&(n["X-Parse-Session-Token"]=t)}const o=`${e.serverURL}/batch`,r=t.map((e=>({method:"GET",path:`/classes/${e.className}/${e.id}`}))),i=await fetch(o,{method:"POST",headers:n,body:JSON.stringify({requests:r})}),a=await i.json();if(a.error)throw new Error(a.error);return a.map(((e,s)=>{const n=t[s];return n.set(e.success),n}))}static async fromFile(t,s,n){if(!e)throw new Error("Not initialized");const o=new FormData;o.append("file",s,t);const r={"X-Parse-Application-Id":e.appId},i=e.sessionToken;i&&(r["X-Parse-Session-Token"]=i);const a=`${e.serverURL}/files/${t}`,c=await fetch(a,{method:"POST",headers:r,body:o}),d=await c.json();if(d.error)throw new Error(d.error);return{__type:"File",name:d.name,url:d.url}}}}createQueryFactory(){const e=this;return class Query extends FmodeQuery{constructor(t){super(t),this._parseInstance=e}static get instance(){return e}static bindInstance(e){}}}createUserFactory(){const e=this;return class User extends FmodeUser{constructor(t){super(t),this._parseInstance=e}static get instance(){return e}static{this.instanceKey=()=>"Parse/"+e.appId+"/currentUser"}static bindInstance(e){}static async logIn(t,s){const n=`${e.serverURL}/login`,o=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","X-Parse-Application-Id":e.appId},body:JSON.stringify({username:t,password:s})}),r=await o.json();if(r.error)throw r;const i=new this({...r,username:t,password:s});return i._saveToCache(),i}static async become(t){const s=`${e.serverURL}/users/me`;let n={"X-Parse-Session-Token":t,"X-Parse-Application-Id":e.appId};const o=await fetch(s,{headers:n}),r=await o.json();if(r.error)throw r.error;const i=new this({...r,sessionToken:t});return i._saveToCache(),i}static async signUp(e,t,s={}){return new this({...s,username:e,password:t}).signUp()}static async logOut(){if(this.current())try{await fetch(`${e.serverURL}/logout`,{method:"POST",headers:{"X-Parse-Session-Token":this.current().sessionToken,"X-Parse-Application-Id":e.appId}})}finally{this._clearCurrentUser()}}static async requestPasswordReset(t){const s=`${e.serverURL}/requestPasswordReset`,n=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json","X-Parse-Application-Id":e.appId},body:JSON.stringify({email:t})}),o=await n.json();if(o.error)throw o}static{this._currentUser=null}static current(){if(this._currentUser)return this._currentUser;try{const t=localStorage.getItem(this.instanceKey());if(!t)return null;const s=JSON.parse(t);if(!s.sessionToken)return null;const n=new this(s);return this._currentUser=n,n.sessionToken&&e&&(e.sessionToken=n.sessionToken),n}catch(e){return console.warn("Failed to restore user from cache",e),null}}static _clearCurrentUser(){this._currentUser=null,e&&(e.sessionToken=void 0),localStorage.removeItem(this.instanceKey())}_saveToCache(){if(!this.sessionToken)return;this.constructor._currentUser=this;const e=this._parseInstance||this.constructor.instance;e&&(e.sessionToken=this.sessionToken),localStorage.setItem(this.constructor.instanceKey(),JSON.stringify(this.toJSONForCache()))}}}static initialize(e){let t;if("string"==typeof e){let s="http://dev.fmode.cn:1337/parse";"undefined"!=typeof location&&"https:"===location.protocol&&(s="https://dev.fmode.cn/parse"),t=new FmodeParse({appId:e,serverURL:s}),this._defaultInstance=t,this._instances.default=t}else t=new FmodeParse(e),e.config&&(this._instances[e.config]=t),e.config&&"default"!==e.config||(this._defaultInstance=t);return t}static with(e){if(!this._instances.nova){let e=new FmodeParse({appId:"ncloudmaster",serverURL:"https://server.fmode.cn/parse"});this._instances.nova=e}if(!this._instances.dev){let e=new FmodeParse({appId:"dev",serverURL:"https://dev.fmode.cn/parse"});this._instances.dev=e}"dev"!=e||this._defaultInstance||(this._defaultInstance=this._instances.dev),"default"!=e||this._instances.default||(this._instances.default=this._instances.dev,this._defaultInstance=this._instances.dev);const t=this._instances[e]||this._defaultInstance;return FmodeObject.bindInstance(t),FmodeQuery.bindInstance(t),FmodeUser.bindInstance(t),FmodeFile.bindInstance(t),FmodeACL.bindInstance(t),FmodeGeoPoint.bindInstance(t),FmodeRelation.bindInstance(t),FmodeCloud.bindInstance(t),t}static getAllInstances(){return{...this._instances}}static getInstanceConfigs(){const e={};return Object.entries(this._instances).forEach((([t,s])=>{e[t]=s.config})),e}get serverURL(){return this.config.serverURL}set serverURL(e){this.config.serverURL=e}get appId(){return this.config.appId}get sessionToken(){if(this.config.sessionToken)return this.config.sessionToken;const e=this.User.current();return e?.sessionToken}set sessionToken(e){this.config.sessionToken=e}}!function(e){e.Object=FmodeObject,e.Query=FmodeQuery,e.User=FmodeUser,e.File=FmodeFile,e.Relation=FmodeRelation,e.ACL=FmodeACL,e.GeoPoint=FmodeGeoPoint,e.Cloud=FmodeCloud.getInstance()}(FmodeParse||(FmodeParse={}));const defaultExport=new Proxy(FmodeParse,{apply:(e,t,s)=>FmodeParse.initialize.apply(e,s),get:(e,t)=>t in e?e[t]:t in FmodeParse?FmodeParse[t]:FmodeParse.with("default")[t]});export{FmodeParse as default,defaultExport as FmodeParse};
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL3BhcnNlL2Ztb2RlLnBhcnNlLm1qcw==`
10
10
 
@@ -0,0 +1,10 @@
1
+
2
+ /**
3
+ * @copyright © 未来飞马 © 未来全栈 www.fmode.cn
4
+ * 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
5
+ * 保留所有权利 All Rights Reserved.
6
+ * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/social/dingtalk/dingtalk-auth.mjs
7
+ */
8
+ import*as dd from"dingtalk-jsapi";import{FmodeParse}from"../../parse";let PARSE_APP_ID=localStorage.getItem("PARSE_APP_ID")||"nova";const Parse=FmodeParse.with(PARSE_APP_ID);export class DingTalkAuth{constructor(e){this.config={clientId:e.clientId,corpId:e.corpId,redirectUri:e.redirectUri,agentId:e.agentId,apiOauthWeb:e.apiOauthWeb||"https://server.fmode.cn/api/dingtalk/oauth2/login",apiOauthDingtalk:e.apiOauthWeb||"https://server.fmode.cn/api/dingtalk/oauth2/dingtalk"},this.loadDingTalkSDK()}loadDingTalkSDK(){return new Promise(((e,o)=>{if(window.DTFrameLogin)return void e();const n=document.createElement("script");n.src="https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js",n.onload=()=>e(),n.onerror=()=>o(new Error("Failed to load DingTalk SDK")),document.head.appendChild(n)}))}isInDingTalkApp(){const e=navigator.userAgent.toLowerCase();return e.includes("dingtalk")||e.includes("dingtalk")}async getDingTalkAuthCode(){try{if(console.log("🔐 开始钉钉内免登录流程"),!this.isInDingTalkApp())return{success:!1,error:"不在钉钉环境内,请使用网页登录",loginType:"dingtalk"};await this.loadDingTalkSDK();const e=await this.getAuthCode();if(e){console.log("✅ 钉钉授权码获取成功:",e);return await this.completeDingTalkLogin(e,"dingtalk")}return{success:!1,error:"获取钉钉授权码失败",loginType:"dingtalk"}}catch(e){return console.error("❌ 钉钉免登录失败:",e),{success:!1,error:e.message||"钉钉免登录失败",loginType:"dingtalk"}}}async getAuthCode(){return new Promise(((e,o)=>{if(this.isInDingTalkApp())try{dd.requestAuthCode({corpId:this.config.corpId,clientId:this.config.clientId,onSuccess:o=>{console.log("📱 钉钉授权码获取成功:",o),e(o.code)},onFail:e=>{console.error("❌ 钉钉授权码获取失败:",e),o(new Error(e.errorMessage||"获取授权码失败"))}})}catch(n){if(console.error("❌ 钉钉JSAPI调用失败:",n),dd&&dd.ready)dd.ready((()=>{dd.getAuthCode({corpId:this.config.corpId,onSuccess:o=>{console.log("📱 钉钉授权码获取成功 (兼容模式):",o),e(o.code)},onFail:e=>{console.error("❌ 钉钉授权码获取失败 (兼容模式):",e),o(new Error(e.errorMessage||"获取授权码失败"))}})})),dd.error((e=>{console.error("❌ 钉钉SDK初始化失败:",e),o(new Error("钉钉SDK初始化失败"))}));else{const e=this.buildWebLoginUrl();console.log("📱 跳转到钉钉网页登录:",e),window.location.href=e,o(new Error("需要跳转到网页登录"))}}else o(new Error("不在钉钉环境内"))}))}async getUserInfoByDingTalkCode(e){console.log("🔄 使用钉钉免登授权码获取用户信息:",e);try{const o=await fetch(this.config.apiOauthDingtalk||"",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.clientId,code:e})}),n=await o.json();if(console.log("🔍 钉钉免登接口返回:",n),1===n.code){const{userInfo:e,sessionToken:o}=n.data;return{userId:e.objectId,username:e.username||e.nickname,nickname:e.nickname,openId:e.openId,unionId:e.unionId,avatar:e.avatar,email:e.email,mobile:e.mobile,corpId:this.config.corpId,sessionToken:o,rawUserInfo:e}}throw new Error(n.mess||"获取用户信息失败")}catch(e){throw console.error("❌ 调用钉钉免登接口失败:",e),e instanceof Error?e:new Error("网络请求失败")}}async getUserInfoByQRCode(e){console.log("🔄 使用二维码授权码获取用户信息:",e);try{const o=await fetch(this.config.apiOauthWeb||"",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.clientId,code:e,grantType:"authorization_code"})}),n=await o.json();if(console.log("🔍 OAuth2登录接口返回:",n),1===n.code){const{userInfo:e,tokenInfo:o,sessionToken:r}=n.data;return{userId:e.objectId,username:e.username||e.nick,nickname:e.nick||e.username,openId:e.openId,unionId:e.unionId,avatar:e.avatarUrl||e.avatar,email:e.email,mobile:e.mobile,corpId:this.config.corpId,sessionToken:r,tokenInfo:o,rawUserInfo:e}}throw new Error(n.mess||"获取用户信息失败")}catch(e){throw console.error("❌ 调用OAuth2登录接口失败:",e),e instanceof Error?e:new Error("网络请求失败")}}validateConfig(){const e=[];return this.config.clientId||e.push("Client ID 未配置"),this.config.corpId||e.push("Corp ID 未配置"),this.config.redirectUri?this.config.redirectUri.startsWith("http")||e.push("Redirect URI 格式不正确,应以 http 或 https 开头"):e.push("Redirect URI 未配置"),console.log("🔍 钉钉应用配置验证:",{clientId:this.config.clientId,corpId:this.config.corpId,redirectUri:this.config.redirectUri,agentId:this.config.agentId,errors:e,isValid:0===e.length}),{isValid:0===e.length,errors:e}}buildWebLoginUrl(e="nanchi_login"){const o=`https://login.dingtalk.com/oauth2/auth?${new URLSearchParams({client_id:this.config.clientId,response_type:"code",scope:"openid",state:e,redirect_uri:this.config.redirectUri,prompt:"consent"}).toString()}`;return console.log("🔗 构造钉钉网页登录URL:",o),o}initQRCodeLogin(e,o,n){console.log("📱 初始化二维码登录");const r=this.validateConfig();r.isValid?this.loadDingTalkSDK().then((()=>{const r={id:e,width:300,height:300},t={redirect_uri:encodeURIComponent(this.config.redirectUri),response_type:"code",client_id:this.config.clientId,scope:"openid",prompt:"consent",state:"nanchi_qr_login"};console.log("🔍 钉钉登录参数:",{clientId:this.config.clientId,corpId:this.config.corpId,redirectUri:this.config.redirectUri,redirectUriEncoded:encodeURIComponent(this.config.redirectUri),loginParams:t}),window.DTFrameLogin(r,t,(async e=>{console.log("✅ 二维码登录成功:",e);try{const r=await this.completeDingTalkLogin(e.authCode,"qrcode");r.success?o(r):n(r.error||"登录失败")}catch(e){console.error("❌ 二维码登录流程失败:",e),n(e.message||"登录失败")}}),(e=>{console.error("❌ 二维码登录失败:",e),n(e)}))})).catch((e=>{console.error("❌ 加载钉钉SDK失败:",e),n("加载钉钉SDK失败")})):n(`配置验证失败: ${r.errors.join(", ")}`)}async handleAuthCallback(e,o){try{if(console.log("🔄 处理网页登录回调:",{code:e,state:o}),!e)return{success:!1,error:"授权码缺失",loginType:"dingtalk"};return await this.completeDingTalkLogin(e,"dingtalk")}catch(e){return console.error("❌ 处理网页登录回调失败:",e),{success:!1,error:e.message||"处理登录回调失败",loginType:"dingtalk"}}}async webLogin(e,o){try{if(console.log("🔐 开始网页登录:",{username:e}),!e||!o)return{success:!1,error:"用户名或密码不能为空",loginType:"web"};console.log("🔄 调用Parse.User.logIn()进行用户登录...");const n=await Parse.User.logIn(e,o);console.log("✅ Parse用户登录成功:",n.id);const r=n.getSessionToken();return{success:!0,userInfo:{userId:n.id,username:n.get("username"),nickname:n.get("nickname")||n.get("username"),email:n.get("email"),mobile:n.get("mobile"),avatar:n.get("avatar"),sessionToken:r,parseUser:n,loginType:"web"},loginType:"web"}}catch(e){console.error("❌ 网页登录失败:",e);let o="登录失败,请重试";return 101===e.code?o="用户名或密码错误":200===e.code?o="连接服务器失败,请检查网络":e.message&&(o=e.message),{success:!1,error:o,loginType:"web"}}}setLogoutCallback(e){this.onLogoutCallback=e}async loginWithSessionToken(e){console.log("🔐 使用sessionToken完成Parse登录");try{const o=await Parse.User.become(e);return console.log("✅ Parse用户登录成功:",o.id),{parseUser:o,sessionToken:e,userId:o.id,username:o.get("username")}}catch(e){throw console.error("❌ Parse用户登录失败:",e),new Error("Parse用户登录失败: "+e.message)}}async completeDingTalkLogin(e,o="dingtalk"){try{let n;if(console.log("🚀 开始完整钉钉登录流程,类型:",o),"dingtalk"===o)n=await this.getUserInfoByDingTalkCode(e);else{if("qrcode"!==o)throw new Error("不支持的登录类型");n=await this.getUserInfoByQRCode(e)}if(n.sessionToken){const r=await this.loginWithSessionToken(n.sessionToken);return console.log("✅ 完整钉钉登录流程成功"),{success:!0,code:e,userInfo:{...n,parseUser:r.parseUser},loginType:o}}throw new Error("未获取到sessionToken,无法完成登录")}catch(e){return console.error("❌ 完整钉钉登录流程失败:",e),{success:!1,error:e.message,loginType:o}}}async logout(){console.log("🚪 用户退出登录"),Parse.User.logOut(),localStorage.removeItem("nanchi_user_info"),localStorage.removeItem("nanchi_auth_token"),localStorage.removeItem("nanchi_session_token"),localStorage.removeItem("nanchi_login_time"),console.log("🗑️ 本地存储已清除"),this.onLogoutCallback&&this.onLogoutCallback()}saveUserInfo(e){localStorage.setItem("nanchi_user_info",JSON.stringify(e)),localStorage.setItem("nanchi_login_time",Date.now().toString()),e.sessionToken&&localStorage.setItem("nanchi_session_token",e.sessionToken),console.log("💾 用户信息已保存到本地存储")}getUserInfo(){const e=localStorage.getItem("nanchi_user_info");return e?JSON.parse(e):null}isLoggedIn(){const e=this.getUserInfo(),o=localStorage.getItem("nanchi_login_time");if(!e||!o)return!1;return(Date.now()-parseInt(o))/36e5<24}async isParseUserLoggedIn(){try{return null!==Parse.User.current()}catch(e){return console.warn("检查Parse用户登录状态失败:",e),!1}}async getCurrentParseUser(){try{return Parse.User.current()}catch(e){return console.warn("获取当前Parse用户失败:",e),null}}}
9
+ var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL3NvY2lhbC9kaW5ndGFsay9kaW5ndGFsay1hdXRoLm1qcw==`
10
+
@@ -0,0 +1,10 @@
1
+
2
+ /**
3
+ * @copyright © 未来飞马 © 未来全栈 www.fmode.cn
4
+ * 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
5
+ * 保留所有权利 All Rights Reserved.
6
+ * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/social/dingtalk/index.mjs
7
+ */
8
+ export*from"./dingtalk-auth";
9
+ var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL3NvY2lhbC9kaW5ndGFsay9pbmRleC5tanM=`
10
+
@@ -0,0 +1,10 @@
1
+
2
+ /**
3
+ * @copyright © 未来飞马 © 未来全栈 www.fmode.cn
4
+ * 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
5
+ * 保留所有权利 All Rights Reserved.
6
+ * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/social/dingtalk/interface.mjs
7
+ */
8
+ export{};
9
+ var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL3NvY2lhbC9kaW5ndGFsay9pbnRlcmZhY2UubWpz`
10
+
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/social/index.mjs
7
7
  */
8
- export*from"./wxwork/wxwork.corp";export*from"./wxwork/wxwork.sdk";export*from"./wxwork/wxwork.auth";
8
+ export*from"./wxwork/wxwork.corp";export*from"./wxwork/wxwork.sdk";export*from"./wxwork/wxwork.auth";export*from"./dingtalk";
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL3NvY2lhbC9pbmRleC5tanM=`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/user/account/account.service.mjs
7
7
  */
8
- import{Injectable}from"@angular/core";import{NovaCloudService}from"../../nova-cloud/nova-cloud.service";import{FmodeParse}from"../../core/parse";import{AuthService}from"../login/auth.service";import{HttpClient}from"@angular/common/http";import*as i0 from"@angular/core";import*as i1 from"../../nova-cloud/nova-cloud.service";import*as i2 from"../login/auth.service";import*as i3 from"@angular/common/http";const Parse=FmodeParse.with("nova");export class AccountService{constructor(e,t,o){this.ncloud=e,this.authServ=t,this.http=o,this.company="",this.billing={credit:{usedDetail:{}}},this.wxAppId="",this.wxpayEnabled=!1,this.appid=localStorage.getItem("WECHAT_APP_ID")||"",this.company=this.authServ.company||localStorage.getItem("company"),this.getBilling(),this.getProfile(),this.getUserOpenid()}async getProfile(){let e=Parse.User.current()?.id;if(!e)return;let t=new Parse.Query("Profile");t.equalTo("user",e),t.equalTo("company",this.company),t.notEqualTo("isDeleted",!0);let o=await t.first();o?.id&&(this.profile=o)}async getBilling(){let e;if(Parse.User.current()?.getSessionToken()){try{e=await this.ncloud.apig("aigc/account",null,"get")}catch(e){}return e?.credit&&(this.billing=e),e}}async getUserOpenid(){if(!this.appid)return;let e=-1!=navigator.userAgent.toLowerCase().indexOf("micromessenger"),t=this.getQueryStringByName("code"),o=localStorage.getItem("openid");if(!o){if(!Parse.User.current())return;let i=Parse.User.current().get("wechat");o=i?.[this.appid]?.openid,e&&(t?await this.getwechat(t):this.authWechat())}}authWechat(e="/account/billing"){if(!localStorage.getItem("openid")){let e=window.location.href;return e=encodeURIComponent(e),void(window.location.href="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+this.appid+"&redirect_uri="+e+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect")}}getQueryStringByName(e){let t=location.search.match(new RegExp("[?&]"+e+"=([^&]+)","i"));return null==t||t.length<1?"":t[1]}async getwechat(e,t="/account/billing"){return new Promise((async(o,i)=>{this.http.post("https://server.fmode.cn/api/wechat/get_wx",{company:this.company,code:e}).subscribe((async e=>{if(console.log(e),e&&200==e.code&&e.data.openid){let t=e.data.openid;localStorage.setItem("openid",t),localStorage.removeItem("code");let i={};i[this.appid]={openid:t},Parse.User.current().set("wechat",i),await Parse.User.current().save(),o(!0)}}),(async e=>{console.log("Error updating items",e),this.authWechat(t),i(!1)}))}))}getWXSignPackageInWechat(){let e={company:this.company,href:encodeURIComponent(location.href.split("?")[0])};this.http.post("https://server.fmode.cn/api/wechat/getconfig",e).subscribe((e=>{const t=e.data;this.wxAppId=t.appid,this.wxpayEnabled=!0,wx.config({debug:!1,appId:t.appid,timestamp:t.timestamp,nonceStr:t.nonceStr,signature:t.signature,jsApiList:["chooseWXPay","onMenuShareTimeline","onMenuShareAppMessage","onMenuShareQQ","onMenuShareQZone","updateAppMessageShareData","updateTimelineShareData"]});let o="https://ai.fmode.cn";location.pathname;Parse.User.current()?.id;let i=this.shareInfo||{title:"飞码AI",desc:"解放创意引领未来|国际前沿的AIGC平台",link:o,type:"link",imgUrl:"https://file-cloud.fmode.cn/E4KpGvTEto/20230822/3mkf41033623275.png",success:function(){console.log("分享成功")},error:function(){console.log("分享失败")},cancel:function(){console.log("取消分享")}};wx.ready((()=>{wx.updateAppMessageShareData(i),wx.updateTimelineShareData(i)})),wx.error((()=>{}))}))}async saveAccountLog(e,t,o,i){return new Promise(((r,a)=>{this.http.post("https://server.fmode.cn/api/apig/saveAccountLog",{company:o,uid:Parse.User.current().id,orderid:t,info:e}).subscribe((e=>{console.log(e),r(!0)}),(e=>{console.warn(e),i&&i.error("请求超时,请稍后再试"),a()}))}))}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:AccountService,deps:[{token:i1.NovaCloudService},{token:i2.AuthService},{token:i3.HttpClient}],target:i0.ɵɵFactoryTarget.Injectable})}static{this.ɵprov=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:AccountService,providedIn:"root"})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:AccountService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.NovaCloudService},{type:i2.AuthService},{type:i3.HttpClient}]});
8
+ import{Injectable}from"@angular/core";import{NovaCloudService}from"../../nova-cloud/nova-cloud.service";import{FmodeParse}from"../../core/parse";import{AuthService}from"../login/auth.service";import{HttpClient}from"@angular/common/http";import*as i0 from"@angular/core";import*as i1 from"../../nova-cloud/nova-cloud.service";import*as i2 from"../login/auth.service";import*as i3 from"@angular/common/http";const Parse=FmodeParse.with("nova");export class AccountService{constructor(e,t,i){this.ncloud=e,this.authServ=t,this.http=i,this.company="",this.billing={credit:{usedDetail:{}}},this.billingCache=null,this.billingCachePromise=null,this.billingCacheTime=0,this.CACHE_EXPIRY_TIME=12e4,this.wxAppId="",this.wxpayEnabled=!1,this.appid=localStorage.getItem("WECHAT_APP_ID")||"",this.company=this.authServ.company||localStorage.getItem("company"),this.getBilling(),this.getProfile(),this.getUserOpenid()}async getProfile(){let e=Parse.User.current()?.id;if(!e)return;let t=new Parse.Query("Profile");t.equalTo("user",e),t.equalTo("company",this.company),t.notEqualTo("isDeleted",!0);let i=await t.first();i?.id&&(this.profile=i)}async getBilling(){const e=Date.now();return this.billingCache?(e-this.billingCacheTime>=this.CACHE_EXPIRY_TIME&&(this.billingCachePromise||(this.billingCachePromise=this.fetchBillingData())),this.billingCache):(this.billingCachePromise||(this.billingCachePromise=this.fetchBillingData()),this.billing={credit:{balance:4,usedDetail:{}}},this.billing)}async fetchBillingData(){let e;if(!Parse.User.current()?.getSessionToken())return this.billingCachePromise=null,this.billing;try{e=await this.ncloud.apig("aigc/account",null,"get"),e?.credit&&(this.billing=e,this.billingCache=e,this.billingCacheTime=Date.now())}catch(e){console.warn("Failed to fetch billing data:",e)}return this.billingCachePromise=null,this.billingCache||this.billing}async getUserOpenid(){if(!this.appid)return;let e=-1!=navigator.userAgent.toLowerCase().indexOf("micromessenger"),t=this.getQueryStringByName("code"),i=localStorage.getItem("openid");if(!i){if(!Parse.User.current())return;let a=Parse.User.current().get("wechat");i=a?.[this.appid]?.openid,e&&(t?await this.getwechat(t):this.authWechat())}}authWechat(e="/account/billing"){if(!localStorage.getItem("openid")){let e=window.location.href;return e=encodeURIComponent(e),void(window.location.href="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+this.appid+"&redirect_uri="+e+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect")}}getQueryStringByName(e){let t=location.search.match(new RegExp("[?&]"+e+"=([^&]+)","i"));return null==t||t.length<1?"":t[1]}async getwechat(e,t="/account/billing"){return new Promise((async(i,a)=>{this.http.post("https://server.fmode.cn/api/wechat/get_wx",{company:this.company,code:e}).subscribe((async e=>{if(console.log(e),e&&200==e.code&&e.data.openid){let t=e.data.openid;localStorage.setItem("openid",t),localStorage.removeItem("code");let a={};a[this.appid]={openid:t},Parse.User.current().set("wechat",a),await Parse.User.current().save(),i(!0)}}),(async e=>{console.log("Error updating items",e),this.authWechat(t),a(!1)}))}))}getWXSignPackageInWechat(){let e={company:this.company,href:encodeURIComponent(location.href.split("?")[0])};this.http.post("https://server.fmode.cn/api/wechat/getconfig",e).subscribe((e=>{const t=e.data;this.wxAppId=t.appid,this.wxpayEnabled=!0,wx.config({debug:!1,appId:t.appid,timestamp:t.timestamp,nonceStr:t.nonceStr,signature:t.signature,jsApiList:["chooseWXPay","onMenuShareTimeline","onMenuShareAppMessage","onMenuShareQQ","onMenuShareQZone","updateAppMessageShareData","updateTimelineShareData"]});let i="https://ai.fmode.cn";location.pathname;Parse.User.current()?.id;let a=this.shareInfo||{title:"飞码AI",desc:"解放创意引领未来|国际前沿的AIGC平台",link:i,type:"link",imgUrl:"https://file-cloud.fmode.cn/E4KpGvTEto/20230822/3mkf41033623275.png",success:function(){console.log("分享成功")},error:function(){console.log("分享失败")},cancel:function(){console.log("取消分享")}};wx.ready((()=>{wx.updateAppMessageShareData(a),wx.updateTimelineShareData(a)})),wx.error((()=>{}))}))}async saveAccountLog(e,t,i,a){return new Promise(((o,n)=>{this.http.post("https://server.fmode.cn/api/apig/saveAccountLog",{company:i,uid:Parse.User.current().id,orderid:t,info:e}).subscribe((e=>{console.log(e),o(!0)}),(e=>{console.warn(e),a&&a.error("请求超时,请稍后再试"),n()}))}))}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:AccountService,deps:[{token:i1.NovaCloudService},{token:i2.AuthService},{token:i3.HttpClient}],target:i0.ɵɵFactoryTarget.Injectable})}static{this.ɵprov=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:AccountService,providedIn:"root"})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:AccountService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.NovaCloudService},{type:i2.AuthService},{type:i3.HttpClient}]});
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi91c2VyL2FjY291bnQvYWNjb3VudC5zZXJ2aWNlLm1qcw==`
10
10