mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	wechat login is ready
This commit is contained in:
		@@ -7,7 +7,7 @@
 | 
				
			|||||||
* 功能新增:**支持AI解读 PDF, Word, Excel等文件**
 | 
					* 功能新增:**支持AI解读 PDF, Word, Excel等文件**
 | 
				
			||||||
* 功能优化:优化聊天界面的用户上传文件的列表样式
 | 
					* 功能优化:优化聊天界面的用户上传文件的列表样式
 | 
				
			||||||
* 功能优化:优化聊天页面对话样式,支持列表样式和对话样式切换
 | 
					* 功能优化:优化聊天页面对话样式,支持列表样式和对话样式切换
 | 
				
			||||||
* 功能新增:支持微信等社交媒体登录
 | 
					* 功能新增:支持微信扫码登录,未注册用户微信扫码后会自动注册并登录。移动使用微信浏览器打开可以实现无感登录。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## v4.0.9
 | 
					## v4.0.9
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -446,14 +446,9 @@ func (h *MidJourneyHandler) getData(finish bool, userId uint, page int, pageSize
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if item.Progress < 100 && item.ImgURL == "" && item.OrgURL != "" {
 | 
							if item.Progress < 100 && item.ImgURL == "" && item.OrgURL != "" {
 | 
				
			||||||
			// discord 服务器图片需要使用代理转发图片数据流
 | 
								image, err := utils.DownloadImage(item.OrgURL, h.App.Config.ProxyURL)
 | 
				
			||||||
			if strings.HasPrefix(item.OrgURL, "https://cdn.discordapp.com") {
 | 
								if err == nil {
 | 
				
			||||||
				image, err := utils.DownloadImage(item.OrgURL, h.App.Config.ProxyURL)
 | 
									job.ImgURL = "data:image/png;base64," + base64.StdEncoding.EncodeToString(image)
 | 
				
			||||||
				if err == nil {
 | 
					 | 
				
			||||||
					job.ImgURL = "data:image/png;base64," + base64.StdEncoding.EncodeToString(image)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				job.ImgURL = job.OrgURL
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -285,9 +285,99 @@ func (h *UserHandler) CLoginRequest(c *gin.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CLoginCallback 第三方登录回调
 | 
					// CLoginCallback 第三方登录回调
 | 
				
			||||||
func (h *UserHandler) CLoginCallback(c *gin.Context) {
 | 
					func (h *UserHandler) CLoginCallback(c *gin.Context) {
 | 
				
			||||||
	//platform := h.GetTrim(c, "type")
 | 
						loginType := h.GetTrim(c, "login_type")
 | 
				
			||||||
	//code := h.GetTrim(c, "code")
 | 
						code := h.GetTrim(c, "code")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var res types.BizVo
 | 
				
			||||||
 | 
						apiURL := fmt.Sprintf("%s/api/clogin/info", h.App.Config.ApiConfig.ApiURL)
 | 
				
			||||||
 | 
						r, err := req.C().R().SetBody(gin.H{"login_type": loginType, "code": code}).
 | 
				
			||||||
 | 
							SetHeader("AppId", h.App.Config.ApiConfig.AppId).
 | 
				
			||||||
 | 
							SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.App.Config.ApiConfig.Token)).
 | 
				
			||||||
 | 
							SetSuccessResult(&res).
 | 
				
			||||||
 | 
							Post(apiURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							resp.ERROR(c, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if r.IsErrorState() {
 | 
				
			||||||
 | 
							resp.ERROR(c, "error with login http status: "+r.Status)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res.Code != types.Success {
 | 
				
			||||||
 | 
							resp.ERROR(c, "error with http response: "+res.Message)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// login successfully
 | 
				
			||||||
 | 
						data := res.Data.(map[string]interface{})
 | 
				
			||||||
 | 
						session := gin.H{}
 | 
				
			||||||
 | 
						var user model.User
 | 
				
			||||||
 | 
						tx := h.DB.Debug().Where("openid", data["openid"]).First(&user)
 | 
				
			||||||
 | 
						if tx.Error != nil { // user not exist, create new user
 | 
				
			||||||
 | 
							// 检测最大注册人数
 | 
				
			||||||
 | 
							var totalUser int64
 | 
				
			||||||
 | 
							h.DB.Model(&model.User{}).Count(&totalUser)
 | 
				
			||||||
 | 
							if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
 | 
				
			||||||
 | 
								resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							salt := utils.RandString(8)
 | 
				
			||||||
 | 
							password := fmt.Sprintf("%d", utils.RandomNumber(8))
 | 
				
			||||||
 | 
							user = model.User{
 | 
				
			||||||
 | 
								Username:   fmt.Sprintf("%s@%d", loginType, utils.RandomNumber(10)),
 | 
				
			||||||
 | 
								Password:   utils.GenPassword(password, salt),
 | 
				
			||||||
 | 
								Avatar:     fmt.Sprintf("%s", data["avatar"]),
 | 
				
			||||||
 | 
								Salt:       salt,
 | 
				
			||||||
 | 
								Status:     true,
 | 
				
			||||||
 | 
								ChatRoles:  utils.JsonEncode([]string{"gpt"}),               // 默认只订阅通用助手角色
 | 
				
			||||||
 | 
								ChatModels: utils.JsonEncode(h.App.SysConfig.DefaultModels), // 默认开通的模型
 | 
				
			||||||
 | 
								Power:      h.App.SysConfig.InitPower,
 | 
				
			||||||
 | 
								OpenId:     fmt.Sprintf("%s", data["openid"]),
 | 
				
			||||||
 | 
								Nickname:   fmt.Sprintf("%s", data["nickname"]),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tx = h.DB.Create(&user)
 | 
				
			||||||
 | 
							if tx.Error != nil {
 | 
				
			||||||
 | 
								resp.ERROR(c, "保存数据失败")
 | 
				
			||||||
 | 
								logger.Error(tx.Error)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							session["username"] = user.Username
 | 
				
			||||||
 | 
							session["password"] = password
 | 
				
			||||||
 | 
						} else { // login directly
 | 
				
			||||||
 | 
							// 更新最后登录时间和IP
 | 
				
			||||||
 | 
							user.LastLoginIp = c.ClientIP()
 | 
				
			||||||
 | 
							user.LastLoginAt = time.Now().Unix()
 | 
				
			||||||
 | 
							h.DB.Model(&user).Updates(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							h.DB.Create(&model.UserLoginLog{
 | 
				
			||||||
 | 
								UserId:       user.Id,
 | 
				
			||||||
 | 
								Username:     user.Username,
 | 
				
			||||||
 | 
								LoginIp:      c.ClientIP(),
 | 
				
			||||||
 | 
								LoginAddress: utils.Ip2Region(h.searcher, c.ClientIP()),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建 token
 | 
				
			||||||
 | 
						token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
				
			||||||
 | 
							"user_id": user.Id,
 | 
				
			||||||
 | 
							"expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							resp.ERROR(c, "Failed to generate token, "+err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 保存到 redis
 | 
				
			||||||
 | 
						key := fmt.Sprintf("users/%d", user.Id)
 | 
				
			||||||
 | 
						if _, err := h.redis.Set(c, key, tokenString, 0).Result(); err != nil {
 | 
				
			||||||
 | 
							resp.ERROR(c, "error with save token: "+err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						session["token"] = tokenString
 | 
				
			||||||
 | 
						resp.SUCCESS(c, session)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Session 获取/验证会话
 | 
					// Session 获取/验证会话
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
ALTER TABLE `chatgpt_chat_models` CHANGE `power` `power` SMALLINT NOT NULL COMMENT '消耗算力点数';
 | 
					ALTER TABLE `chatgpt_chat_models` CHANGE `power` `power` SMALLINT NOT NULL COMMENT '消耗算力点数';
 | 
				
			||||||
ALTER TABLE `chatgpt_users` ADD `openid` VARCHAR(100) NULL COMMENT '第三方登录账号ID' AFTER `last_login_ip`;
 | 
					ALTER TABLE `chatgpt_users` ADD `openid` VARCHAR(100) NULL COMMENT '第三方登录账号ID' AFTER `last_login_ip`;
 | 
				
			||||||
ALTER TABLE `chatgpt_users` ADD `platform` VARCHAR(30) NULL COMMENT '登录平台' AFTER `openid`;
 | 
					ALTER TABLE `chatgpt_users` ADD UNIQUE(`openid`);
 | 
				
			||||||
 | 
					ALTER TABLE `chatgpt_users` ADD `platform` VARCHAR(30) NULL COMMENT '登录平台' AFTER `openid`;
 | 
				
			||||||
 | 
					ALTER TABLE `chatgpt_users` CHANGE `avatar` `avatar` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头像';
 | 
				
			||||||
 | 
					ALTER TABLE `chatgpt_chat_history` CHANGE `icon` `icon` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色图标';
 | 
				
			||||||
							
								
								
									
										115
									
								
								web/src/assets/css/login.styl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								web/src/assets/css/login.styl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					.bg {
 | 
				
			||||||
 | 
					  position fixed
 | 
				
			||||||
 | 
					  left 0
 | 
				
			||||||
 | 
					  right 0
 | 
				
			||||||
 | 
					  top 0
 | 
				
			||||||
 | 
					  bottom 0
 | 
				
			||||||
 | 
					  background-color #313237
 | 
				
			||||||
 | 
					  background-image url("~@/assets/img/login-bg.jpg")
 | 
				
			||||||
 | 
					  background-size cover
 | 
				
			||||||
 | 
					  background-position center
 | 
				
			||||||
 | 
					  background-repeat repeat-y
 | 
				
			||||||
 | 
					  //filter: blur(10px); /* 调整模糊程度,可以根据需要修改值 */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main {
 | 
				
			||||||
 | 
					  .contain {
 | 
				
			||||||
 | 
					    position fixed
 | 
				
			||||||
 | 
					    left 50%
 | 
				
			||||||
 | 
					    top 40%
 | 
				
			||||||
 | 
					    width 90%
 | 
				
			||||||
 | 
					    max-width 400px;
 | 
				
			||||||
 | 
					    transform translate(-50%, -50%)
 | 
				
			||||||
 | 
					    padding 20px 10px;
 | 
				
			||||||
 | 
					    color #ffffff
 | 
				
			||||||
 | 
					    border-radius 10px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .logo {
 | 
				
			||||||
 | 
					      text-align center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .el-image {
 | 
				
			||||||
 | 
					        width 120px;
 | 
				
			||||||
 | 
					        cursor pointer
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .header {
 | 
				
			||||||
 | 
					      width 100%
 | 
				
			||||||
 | 
					      margin-bottom 24px
 | 
				
			||||||
 | 
					      font-size 24px
 | 
				
			||||||
 | 
					      color $white_v1
 | 
				
			||||||
 | 
					      letter-space 2px
 | 
				
			||||||
 | 
					      text-align center
 | 
				
			||||||
 | 
					      padding-top 10px
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .content {
 | 
				
			||||||
 | 
					      width 100%
 | 
				
			||||||
 | 
					      height: auto
 | 
				
			||||||
 | 
					      border-radius 3px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .block {
 | 
				
			||||||
 | 
					        margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .el-input__inner {
 | 
				
			||||||
 | 
					          border 1px solid $gray-v6 !important
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .el-icon-user, .el-icon-lock {
 | 
				
			||||||
 | 
					            font-size 20px
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .btn-row {
 | 
				
			||||||
 | 
					        padding-top 10px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .login-btn {
 | 
				
			||||||
 | 
					          width 100%
 | 
				
			||||||
 | 
					          font-size 16px
 | 
				
			||||||
 | 
					          letter-spacing 2px
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .text-line {
 | 
				
			||||||
 | 
					        justify-content center
 | 
				
			||||||
 | 
					        padding-top 10px;
 | 
				
			||||||
 | 
					        font-size 14px;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .opt {
 | 
				
			||||||
 | 
					        padding 15px
 | 
				
			||||||
 | 
					        .el-col {
 | 
				
			||||||
 | 
					          text-align center
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .divider {
 | 
				
			||||||
 | 
					        border-top: 2px solid #c1c1c1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .clogin {
 | 
				
			||||||
 | 
					        padding 15px
 | 
				
			||||||
 | 
					        display flex
 | 
				
			||||||
 | 
					        justify-content center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .iconfont {
 | 
				
			||||||
 | 
					          font-size 20px
 | 
				
			||||||
 | 
					          background: #E9F1F6;
 | 
				
			||||||
 | 
					          padding: 8px;
 | 
				
			||||||
 | 
					          border-radius: 50%;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .iconfont.icon-wechat {
 | 
				
			||||||
 | 
					          color #0bc15f
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .footer {
 | 
				
			||||||
 | 
					    color #ffffff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .container {
 | 
				
			||||||
 | 
					      padding 20px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -155,6 +155,7 @@ const synthesis = (text) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 重新生成
 | 
					// 重新生成
 | 
				
			||||||
const reGenerate = (prompt) => {
 | 
					const reGenerate = (prompt) => {
 | 
				
			||||||
 | 
					  console.log(prompt)
 | 
				
			||||||
  emits('regen', prompt)
 | 
					  emits('regen', prompt)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								web/src/components/TaskList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								web/src/components/TaskList.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="running-job-list">
 | 
				
			||||||
 | 
					    <div class="running-job-box" v-if="list.length > 0">
 | 
				
			||||||
 | 
					      <div class="job-item" v-for="item in list">
 | 
				
			||||||
 | 
					        <div v-if="item.progress > 0" class="job-item-inner">
 | 
				
			||||||
 | 
					          <el-image v-if="item.img_url" :src="item['img_url']" fit="cover" loading="lazy">
 | 
				
			||||||
 | 
					            <template #placeholder>
 | 
				
			||||||
 | 
					              <div class="image-slot">
 | 
				
			||||||
 | 
					                正在加载图片
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <template #error>
 | 
				
			||||||
 | 
					              <div class="image-slot">
 | 
				
			||||||
 | 
					                <el-icon>
 | 
				
			||||||
 | 
					                  <Picture/>
 | 
				
			||||||
 | 
					                </el-icon>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					          </el-image>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div class="progress">
 | 
				
			||||||
 | 
					            <el-progress type="circle" :percentage="item.progress" :width="100"
 | 
				
			||||||
 | 
					                         color="#47fff1"/>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <el-image fit="cover" v-else>
 | 
				
			||||||
 | 
					          <template #error>
 | 
				
			||||||
 | 
					            <div class="image-slot">
 | 
				
			||||||
 | 
					              <i class="iconfont icon-quick-start"></i>
 | 
				
			||||||
 | 
					              <span>任务正在排队中</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </template>
 | 
				
			||||||
 | 
					        </el-image>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <el-empty :image-size="100" v-else/>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import {ref} from "vue";
 | 
				
			||||||
 | 
					import {CircleCloseFilled, Picture} from "@element-plus/icons-vue";
 | 
				
			||||||
 | 
					import {isImage, removeArrayItem, substr} from "@/utils/libs";
 | 
				
			||||||
 | 
					import {FormatFileSize, GetFileIcon, GetFileType} from "@/store/system";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					  list: {
 | 
				
			||||||
 | 
					    type: Array,
 | 
				
			||||||
 | 
					    default:[],
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped lang="stylus">
 | 
				
			||||||
 | 
					@import "~@/assets/css/running-job-list.styl"
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -101,6 +101,12 @@ const routes = [
 | 
				
			|||||||
        meta: {title: '用户登录'},
 | 
					        meta: {title: '用户登录'},
 | 
				
			||||||
        component: () => import('@/views/Login.vue'),
 | 
					        component: () => import('@/views/Login.vue'),
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        name: 'login-callback',
 | 
				
			||||||
 | 
					        path: '/login/callback',
 | 
				
			||||||
 | 
					        meta: {title: '用户登录'},
 | 
				
			||||||
 | 
					        component: () => import('@/views/LoginCallback.vue'),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        name: 'register',
 | 
					        name: 'register',
 | 
				
			||||||
        path: '/register',
 | 
					        path: '/register',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -626,10 +626,12 @@ const connect = function (chat_id, role_id) {
 | 
				
			|||||||
        reader.onload = () => {
 | 
					        reader.onload = () => {
 | 
				
			||||||
          const data = JSON.parse(String(reader.result));
 | 
					          const data = JSON.parse(String(reader.result));
 | 
				
			||||||
          if (data.type === 'start') {
 | 
					          if (data.type === 'start') {
 | 
				
			||||||
 | 
					            const prePrompt = chatData.value[chatData.value.length-1].content
 | 
				
			||||||
            chatData.value.push({
 | 
					            chatData.value.push({
 | 
				
			||||||
              type: "reply",
 | 
					              type: "reply",
 | 
				
			||||||
              id: randString(32),
 | 
					              id: randString(32),
 | 
				
			||||||
              icon: _role['icon'],
 | 
					              icon: _role['icon'],
 | 
				
			||||||
 | 
					              prompt:prePrompt,
 | 
				
			||||||
              content: ""
 | 
					              content: ""
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
          } else if (data.type === 'end') { // 消息接收完毕
 | 
					          } else if (data.type === 'end') { // 消息接收完毕
 | 
				
			||||||
@@ -864,7 +866,7 @@ const reGenerate = function (prompt) {
 | 
				
			|||||||
    type: "prompt",
 | 
					    type: "prompt",
 | 
				
			||||||
    id: randString(32),
 | 
					    id: randString(32),
 | 
				
			||||||
    icon: loginUser.value.avatar,
 | 
					    icon: loginUser.value.avatar,
 | 
				
			||||||
    content: md.render(text)
 | 
					    content: text
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  socket.value.send(JSON.stringify({type: "chat", content: prompt}));
 | 
					  socket.value.send(JSON.stringify({type: "chat", content: prompt}));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,43 +86,7 @@
 | 
				
			|||||||
          <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
 | 
					          <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
 | 
				
			||||||
            <div class="job-list-box">
 | 
					            <div class="job-list-box">
 | 
				
			||||||
              <h2>任务列表</h2>
 | 
					              <h2>任务列表</h2>
 | 
				
			||||||
              <div class="running-job-list">
 | 
					              <task-list :list="runningJobs" />
 | 
				
			||||||
                <div class="running-job-box" v-if="runningJobs.length > 0">
 | 
					 | 
				
			||||||
                  <div class="job-item" v-for="item in runningJobs" :key="item.id">
 | 
					 | 
				
			||||||
                    <div v-if="item.progress > 0" class="job-item-inner">
 | 
					 | 
				
			||||||
                      <el-image :src="item['img_url']" fit="cover" loading="lazy">
 | 
					 | 
				
			||||||
                        <template #placeholder>
 | 
					 | 
				
			||||||
                          <div class="image-slot">
 | 
					 | 
				
			||||||
                            正在加载图片
 | 
					 | 
				
			||||||
                          </div>
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <template #error>
 | 
					 | 
				
			||||||
                          <div class="image-slot">
 | 
					 | 
				
			||||||
                            <el-icon>
 | 
					 | 
				
			||||||
                              <Picture/>
 | 
					 | 
				
			||||||
                            </el-icon>
 | 
					 | 
				
			||||||
                          </div>
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
                      </el-image>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                      <div class="progress">
 | 
					 | 
				
			||||||
                        <el-progress type="circle" :percentage="item.progress" :width="100"
 | 
					 | 
				
			||||||
                                     color="#47fff1"/>
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <el-image fit="cover" v-else>
 | 
					 | 
				
			||||||
                      <template #error>
 | 
					 | 
				
			||||||
                        <div class="image-slot">
 | 
					 | 
				
			||||||
                          <i class="iconfont icon-quick-start"></i>
 | 
					 | 
				
			||||||
                          <span>任务正在排队中</span>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                      </template>
 | 
					 | 
				
			||||||
                    </el-image>
 | 
					 | 
				
			||||||
                  </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <el-empty :image-size="100" v-else/>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <h2>创作记录</h2>
 | 
					              <h2>创作记录</h2>
 | 
				
			||||||
              <div class="finish-job-list">
 | 
					              <div class="finish-job-list">
 | 
				
			||||||
@@ -228,6 +192,7 @@ import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
 | 
				
			|||||||
import Clipboard from "clipboard";
 | 
					import Clipboard from "clipboard";
 | 
				
			||||||
import {checkSession} from "@/action/session";
 | 
					import {checkSession} from "@/action/session";
 | 
				
			||||||
import {useSharedStore} from "@/store/sharedata";
 | 
					import {useSharedStore} from "@/store/sharedata";
 | 
				
			||||||
 | 
					import TaskList from "@/components/TaskList.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const listBoxHeight = ref(0)
 | 
					const listBoxHeight = ref(0)
 | 
				
			||||||
// const paramBoxHeight = ref(0)
 | 
					// const paramBoxHeight = ref(0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,10 +215,11 @@ const init = () => {
 | 
				
			|||||||
const logout = function () {
 | 
					const logout = function () {
 | 
				
			||||||
  httpGet('/api/user/logout').then(() => {
 | 
					  httpGet('/api/user/logout').then(() => {
 | 
				
			||||||
    removeUserToken()
 | 
					    removeUserToken()
 | 
				
			||||||
    store.setShowLoginDialog(true)
 | 
					    router.push("/login")
 | 
				
			||||||
    loginUser.value = {}
 | 
					    // store.setShowLoginDialog(true)
 | 
				
			||||||
    // 刷新组件
 | 
					    // loginUser.value = {}
 | 
				
			||||||
    routerViewKey.value += 1
 | 
					    // // 刷新组件
 | 
				
			||||||
 | 
					    // routerViewKey.value += 1
 | 
				
			||||||
  }).catch(() => {
 | 
					  }).catch(() => {
 | 
				
			||||||
    ElMessage.error('注销失败!');
 | 
					    ElMessage.error('注销失败!');
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -163,7 +163,7 @@
 | 
				
			|||||||
          </el-form>
 | 
					          </el-form>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="task-list-box" @scrollend="handleScrollEnd">
 | 
					      <div class="task-list-box">
 | 
				
			||||||
        <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
 | 
					        <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
 | 
				
			||||||
          <div class="extra-params">
 | 
					          <div class="extra-params">
 | 
				
			||||||
            <el-form>
 | 
					            <el-form>
 | 
				
			||||||
@@ -450,43 +450,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          <div class="job-list-box">
 | 
					          <div class="job-list-box">
 | 
				
			||||||
            <h2>任务列表</h2>
 | 
					            <h2>任务列表</h2>
 | 
				
			||||||
            <div class="running-job-list">
 | 
					            <task-list :list="runningJobs" />
 | 
				
			||||||
              <div class="running-job-box" v-if="runningJobs.length > 0">
 | 
					 | 
				
			||||||
                <div class="job-item" v-for="item in runningJobs">
 | 
					 | 
				
			||||||
                  <div v-if="item.progress > 0" class="job-item-inner">
 | 
					 | 
				
			||||||
                    <el-image :src="item['img_url']" fit="cover" loading="lazy">
 | 
					 | 
				
			||||||
                      <template #placeholder>
 | 
					 | 
				
			||||||
                        <div class="image-slot">
 | 
					 | 
				
			||||||
                          正在加载图片
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                      </template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                      <template #error>
 | 
					 | 
				
			||||||
                        <div class="image-slot">
 | 
					 | 
				
			||||||
                          <el-icon>
 | 
					 | 
				
			||||||
                            <Picture/>
 | 
					 | 
				
			||||||
                          </el-icon>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                      </template>
 | 
					 | 
				
			||||||
                    </el-image>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <div class="progress">
 | 
					 | 
				
			||||||
                      <el-progress type="circle" :percentage="item.progress" :width="100"
 | 
					 | 
				
			||||||
                                   color="#47fff1"/>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                  </div>
 | 
					 | 
				
			||||||
                  <el-image fit="cover" v-else>
 | 
					 | 
				
			||||||
                    <template #error>
 | 
					 | 
				
			||||||
                      <div class="image-slot">
 | 
					 | 
				
			||||||
                        <i class="iconfont icon-quick-start"></i>
 | 
					 | 
				
			||||||
                        <span>任务正在排队中</span>
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </template>
 | 
					 | 
				
			||||||
                  </el-image>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
              <el-empty :image-size="100" v-else/>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <h2>创作记录</h2>
 | 
					            <h2>创作记录</h2>
 | 
				
			||||||
            <div class="finish-job-list">
 | 
					            <div class="finish-job-list">
 | 
				
			||||||
@@ -617,6 +581,7 @@ import {useRouter} from "vue-router";
 | 
				
			|||||||
import {getSessionId} from "@/store/session";
 | 
					import {getSessionId} from "@/store/session";
 | 
				
			||||||
import {copyObj, removeArrayItem} from "@/utils/libs";
 | 
					import {copyObj, removeArrayItem} from "@/utils/libs";
 | 
				
			||||||
import {useSharedStore} from "@/store/sharedata";
 | 
					import {useSharedStore} from "@/store/sharedata";
 | 
				
			||||||
 | 
					import TaskList from "@/components/TaskList.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const listBoxHeight = ref(0)
 | 
					const listBoxHeight = ref(0)
 | 
				
			||||||
const paramBoxHeight = ref(0)
 | 
					const paramBoxHeight = ref(0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -296,39 +296,8 @@
 | 
				
			|||||||
          <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
 | 
					          <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
 | 
				
			||||||
            <div class="job-list-box">
 | 
					            <div class="job-list-box">
 | 
				
			||||||
              <h2>任务列表</h2>
 | 
					              <h2>任务列表</h2>
 | 
				
			||||||
              <div class="running-job-list">
 | 
					              <task-list :list="runningJobs" />
 | 
				
			||||||
                <div class="running-job-box" v-if="runningJobs.length > 0">
 | 
					              
 | 
				
			||||||
                  <div class="job-item" v-for="item in runningJobs">
 | 
					 | 
				
			||||||
                    <div v-if="item.progress > 0" class="job-item-inner">
 | 
					 | 
				
			||||||
                      <el-image :src="item['img_url']" fit="cover" loading="lazy">
 | 
					 | 
				
			||||||
                        <template #placeholder>
 | 
					 | 
				
			||||||
                          <div class="image-slot">
 | 
					 | 
				
			||||||
                            正在加载图片
 | 
					 | 
				
			||||||
                          </div>
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <template #error>
 | 
					 | 
				
			||||||
                          <div class="image-slot"></div>
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
                      </el-image>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                      <div class="progress">
 | 
					 | 
				
			||||||
                        <el-progress type="circle" :percentage="item.progress" :width="100"
 | 
					 | 
				
			||||||
                                     color="#47fff1"/>
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <el-image fit="cover" v-else>
 | 
					 | 
				
			||||||
                      <template #error>
 | 
					 | 
				
			||||||
                        <div class="image-slot">
 | 
					 | 
				
			||||||
                          <i class="iconfont icon-quick-start"></i>
 | 
					 | 
				
			||||||
                          <span>任务正在排队中</span>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                      </template>
 | 
					 | 
				
			||||||
                    </el-image>
 | 
					 | 
				
			||||||
                  </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <el-empty :image-size="100" v-else/>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
              <h2>创作记录</h2>
 | 
					              <h2>创作记录</h2>
 | 
				
			||||||
              <div class="finish-job-list">
 | 
					              <div class="finish-job-list">
 | 
				
			||||||
                <div v-if="finishedJobs.length > 0">
 | 
					                <div v-if="finishedJobs.length > 0">
 | 
				
			||||||
@@ -506,6 +475,7 @@ import {checkSession} from "@/action/session";
 | 
				
			|||||||
import {useRouter} from "vue-router";
 | 
					import {useRouter} from "vue-router";
 | 
				
			||||||
import {getSessionId} from "@/store/session";
 | 
					import {getSessionId} from "@/store/session";
 | 
				
			||||||
import {useSharedStore} from "@/store/sharedata";
 | 
					import {useSharedStore} from "@/store/sharedata";
 | 
				
			||||||
 | 
					import TaskList from "@/components/TaskList.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const listBoxHeight = ref(0)
 | 
					const listBoxHeight = ref(0)
 | 
				
			||||||
// const paramBoxHeight = ref(0)
 | 
					// const paramBoxHeight = ref(0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,19 +35,22 @@
 | 
				
			|||||||
          </el-row>
 | 
					          </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <el-row class="opt" :gutter="24">
 | 
					          <el-row class="opt" :gutter="24">
 | 
				
			||||||
            <el-col :span="span" v-if="cLoginURL !== ''">
 | 
					            <el-col :span="8"><el-link type="primary" @click="router.push('/register')">注册</el-link></el-col>
 | 
				
			||||||
              <el-tooltip class="item" effect="light" content="微信扫码登录" placement="top">
 | 
					            <el-col :span="8">
 | 
				
			||||||
                <a class="wechat-login" :href="cLoginURL"><i class="iconfont icon-wechat"></i></a>
 | 
					 | 
				
			||||||
              </el-tooltip>
 | 
					 | 
				
			||||||
            </el-col>
 | 
					 | 
				
			||||||
            <el-col :span="span"><el-link type="primary" @click="router.push('/register')">注册</el-link></el-col>
 | 
					 | 
				
			||||||
            <el-col :span="span">
 | 
					 | 
				
			||||||
              <el-link type="info" @click="showResetPass = true">重置密码</el-link>
 | 
					              <el-link type="info" @click="showResetPass = true">重置密码</el-link>
 | 
				
			||||||
            </el-col>
 | 
					            </el-col>
 | 
				
			||||||
            <el-col :span="span">
 | 
					            <el-col :span="8">
 | 
				
			||||||
              <el-link type="info" @click="router.push('/')">首页</el-link>
 | 
					              <el-link type="info" @click="router.push('/')">首页</el-link>
 | 
				
			||||||
            </el-col>
 | 
					            </el-col>
 | 
				
			||||||
          </el-row>
 | 
					          </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div v-if="wechatLoginURL !== ''">
 | 
				
			||||||
 | 
					            <el-divider class="divider">其他登录方式</el-divider>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div class="clogin">
 | 
				
			||||||
 | 
					              <a class="wechat-login" :href="wechatLoginURL"><i class="iconfont icon-wechat"></i></a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,8 +83,7 @@ const password = ref(process.env.VUE_APP_PASS);
 | 
				
			|||||||
const showResetPass = ref(false)
 | 
					const showResetPass = ref(false)
 | 
				
			||||||
const logo = ref("/images/logo.png")
 | 
					const logo = ref("/images/logo.png")
 | 
				
			||||||
const licenseConfig = ref({})
 | 
					const licenseConfig = ref({})
 | 
				
			||||||
const cLoginURL = ref('')
 | 
					const wechatLoginURL = ref('')
 | 
				
			||||||
const span = ref(8)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
  // 获取系统配置
 | 
					  // 获取系统配置
 | 
				
			||||||
@@ -94,7 +96,6 @@ onMounted(() => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  httpGet("/api/config/license").then(res => {
 | 
					  httpGet("/api/config/license").then(res => {
 | 
				
			||||||
    licenseConfig.value = res.data
 | 
					    licenseConfig.value = res.data
 | 
				
			||||||
    span.value = 6
 | 
					 | 
				
			||||||
  }).catch(e => {
 | 
					  }).catch(e => {
 | 
				
			||||||
    showMessageError("获取 License 配置:" + e.message)
 | 
					    showMessageError("获取 License 配置:" + e.message)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
@@ -108,10 +109,9 @@ onMounted(() => {
 | 
				
			|||||||
  }).catch(() => {
 | 
					  }).catch(() => {
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // const returnURL = `${location.protocol}//${location.host}/user/api/clogin/callback`
 | 
					  const returnURL = `${location.protocol}//${location.host}/login/callback`
 | 
				
			||||||
  const returnURL = `https://ai.r9it.com/user/api/clogin/callback`
 | 
					 | 
				
			||||||
  httpGet("/api/user/clogin/request?return_url="+returnURL).then(res => {
 | 
					  httpGet("/api/user/clogin/request?return_url="+returnURL).then(res => {
 | 
				
			||||||
    cLoginURL.value = res.data.url
 | 
					    wechatLoginURL.value = res.data.url
 | 
				
			||||||
  }).catch(e => {
 | 
					  }).catch(e => {
 | 
				
			||||||
    console.error(e)
 | 
					    console.error(e)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
@@ -147,103 +147,5 @@ const login = function () {
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
.bg {
 | 
					@import "@/assets/css/login.styl"
 | 
				
			||||||
  position fixed
 | 
					 | 
				
			||||||
  left 0
 | 
					 | 
				
			||||||
  right 0
 | 
					 | 
				
			||||||
  top 0
 | 
					 | 
				
			||||||
  bottom 0
 | 
					 | 
				
			||||||
  background-color #313237
 | 
					 | 
				
			||||||
  background-image url("~@/assets/img/login-bg.jpg")
 | 
					 | 
				
			||||||
  background-size cover
 | 
					 | 
				
			||||||
  background-position center
 | 
					 | 
				
			||||||
  background-repeat repeat-y
 | 
					 | 
				
			||||||
  //filter: blur(10px); /* 调整模糊程度,可以根据需要修改值 */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.main {
 | 
					 | 
				
			||||||
  .contain {
 | 
					 | 
				
			||||||
    position fixed
 | 
					 | 
				
			||||||
    left 50%
 | 
					 | 
				
			||||||
    top 40%
 | 
					 | 
				
			||||||
    width 90%
 | 
					 | 
				
			||||||
    max-width 400px;
 | 
					 | 
				
			||||||
    transform translate(-50%, -50%)
 | 
					 | 
				
			||||||
    padding 20px 10px;
 | 
					 | 
				
			||||||
    color #ffffff
 | 
					 | 
				
			||||||
    border-radius 10px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .logo {
 | 
					 | 
				
			||||||
      text-align center
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      .el-image {
 | 
					 | 
				
			||||||
        width 120px;
 | 
					 | 
				
			||||||
        cursor pointer
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .header {
 | 
					 | 
				
			||||||
      width 100%
 | 
					 | 
				
			||||||
      margin-bottom 24px
 | 
					 | 
				
			||||||
      font-size 24px
 | 
					 | 
				
			||||||
      color $white_v1
 | 
					 | 
				
			||||||
      letter-space 2px
 | 
					 | 
				
			||||||
      text-align center
 | 
					 | 
				
			||||||
      padding-top 10px
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .content {
 | 
					 | 
				
			||||||
      width 100%
 | 
					 | 
				
			||||||
      height: auto
 | 
					 | 
				
			||||||
      border-radius 3px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      .block {
 | 
					 | 
				
			||||||
        margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .el-input__inner {
 | 
					 | 
				
			||||||
          border 1px solid $gray-v6 !important
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          .el-icon-user, .el-icon-lock {
 | 
					 | 
				
			||||||
            font-size 20px
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      .btn-row {
 | 
					 | 
				
			||||||
        padding-top 10px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .login-btn {
 | 
					 | 
				
			||||||
          width 100%
 | 
					 | 
				
			||||||
          font-size 16px
 | 
					 | 
				
			||||||
          letter-spacing 2px
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      .text-line {
 | 
					 | 
				
			||||||
        justify-content center
 | 
					 | 
				
			||||||
        padding-top 10px;
 | 
					 | 
				
			||||||
        font-size 14px;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      .opt {
 | 
					 | 
				
			||||||
        padding 15px
 | 
					 | 
				
			||||||
        .el-col {
 | 
					 | 
				
			||||||
          text-align center
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .wechat-login {
 | 
					 | 
				
			||||||
          color #0bc15f
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .footer {
 | 
					 | 
				
			||||||
    color #ffffff;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .container {
 | 
					 | 
				
			||||||
      padding 20px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										105
									
								
								web/src/views/LoginCallback.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								web/src/views/LoginCallback.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="login-callback"
 | 
				
			||||||
 | 
					       v-loading="loading"
 | 
				
			||||||
 | 
					       element-loading-text="正在同步登录信息..."
 | 
				
			||||||
 | 
					       :style="{ height: winHeight + 'px' }">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <el-dialog
 | 
				
			||||||
 | 
					        v-model="show"
 | 
				
			||||||
 | 
					        :close-on-click-modal="false"
 | 
				
			||||||
 | 
					        :show-close="false"
 | 
				
			||||||
 | 
					        style="width: 360px;"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <el-result
 | 
				
			||||||
 | 
					          icon="success"
 | 
				
			||||||
 | 
					          title="登录成功"
 | 
				
			||||||
 | 
					          style="--el-result-padding:10px"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					          <template #sub-title>
 | 
				
			||||||
 | 
					            <div class="user-info">
 | 
				
			||||||
 | 
					              <div class="line">您的初始账户信息如下:</div>
 | 
				
			||||||
 | 
					              <div class="line"><span>用户名:</span>{{username}}</div>
 | 
				
			||||||
 | 
					              <div class="line"><span>密码:</span>{{password}}</div>
 | 
				
			||||||
 | 
					              <div class="line">您后期也可以通过此账号和密码登录</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </template>
 | 
				
			||||||
 | 
					          <template #extra>
 | 
				
			||||||
 | 
					            <el-button type="primary" @click="finishLogin">我知道了</el-button>
 | 
				
			||||||
 | 
					          </template>
 | 
				
			||||||
 | 
					      </el-result>
 | 
				
			||||||
 | 
					    </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import {ref} from "vue"
 | 
				
			||||||
 | 
					import {useRouter} from "vue-router"
 | 
				
			||||||
 | 
					import {ElMessage, ElMessageBox} from "element-plus";
 | 
				
			||||||
 | 
					import {httpGet} from "@/utils/http";
 | 
				
			||||||
 | 
					import {setUserToken} from "@/store/session";
 | 
				
			||||||
 | 
					import {isMobile} from "@/utils/libs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const winHeight = ref(window.innerHeight)
 | 
				
			||||||
 | 
					const loading = ref(true)
 | 
				
			||||||
 | 
					const router = useRouter()
 | 
				
			||||||
 | 
					const show = ref(false)
 | 
				
			||||||
 | 
					const username = ref('')
 | 
				
			||||||
 | 
					const password = ref('')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const code = router.currentRoute.value.query.code
 | 
				
			||||||
 | 
					if (code === "") {
 | 
				
			||||||
 | 
					  ElMessage.error({message: "登录失败:code 参数不能为空",duration: 2000, onClose: () => router.push("/")})
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					  // 发送请求获取用户信息
 | 
				
			||||||
 | 
					  httpGet("/api/user/clogin/callback",{login_type: "wx",code: code}).then(res => {
 | 
				
			||||||
 | 
					    setUserToken(res.data.token)
 | 
				
			||||||
 | 
					    if (res.data.username) {
 | 
				
			||||||
 | 
					      username.value = res.data.username
 | 
				
			||||||
 | 
					      password.value = res.data.password
 | 
				
			||||||
 | 
					      show.value = true
 | 
				
			||||||
 | 
					      loading.value = false
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      finishLogin()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }).catch(e => {
 | 
				
			||||||
 | 
					    ElMessageBox.alert(e.message, {
 | 
				
			||||||
 | 
					      confirmButtonText: '重新登录',
 | 
				
			||||||
 | 
					      type:"error",
 | 
				
			||||||
 | 
					      title:"登录失败",
 | 
				
			||||||
 | 
					      callback: () => {
 | 
				
			||||||
 | 
					        router.push("/login")
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const finishLogin = () => {
 | 
				
			||||||
 | 
					  if (isMobile()) {
 | 
				
			||||||
 | 
					    router.push('/mobile')
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    router.push('/chat')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.login-callback {
 | 
				
			||||||
 | 
					  .user-info {
 | 
				
			||||||
 | 
					    display flex
 | 
				
			||||||
 | 
					    flex-direction column
 | 
				
			||||||
 | 
					    padding 10px
 | 
				
			||||||
 | 
					    border 1px dashed #e1e1e1
 | 
				
			||||||
 | 
					    border-radius 10px
 | 
				
			||||||
 | 
					    .line {
 | 
				
			||||||
 | 
					      text-align left
 | 
				
			||||||
 | 
					      font-size 14px
 | 
				
			||||||
 | 
					      line-height 1.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      span{
 | 
				
			||||||
 | 
					        font-weight bold
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user